Python スマホで録画したゲーム動画をYouTube用へ動画編集「MoviePy」

 本記事では、下図左のようにスマホで録画した場合、画面サイズが縦長になるのを下図右のようにアスペクト比を横長へ変更して、日本語文字列を挿入したりと加工しています。

f:id:HK29:20191022104646j:plain

 下記リンク先が完成版で、音声も有りです。

www.youtube.com

 ■本記事で特筆するのは下記3つです。

1. 必要な箇所を開始,終了時間を指定することで抽出して、fpsも変更できてファイルを軽くできる。mp4ファイルで出力。
2. アスペクト比を保ったまま、解像度を指定サイズへ変更し、テキストも入れる。しかし、ここでは音はなし。mp4ファイルで出力。
3. 音声(音楽)を1の動画から抽出して、mp3ファイルで出力。それを2の動画へ結合してmp4ファイルで出力して完成する。

 

■本プログラム実行の流れ

◇①ライブラリのインストール

pip install opencv-python でOpenCVをインストールして、import cv2で使用

pip install moviepy でMoviePyをインストール 

もし、pillow(PIL)がなれけば、conda もしは pipでインストールする。Apnacondaを入れていれば入ってるはず。

◇②使用ファイル

下図のようにファイルを二つ準備する。ひとつは、本記事に後述するコードを適当な名前「***.py」で保存する。そして、スマホ等で録画した動画を準備する。

下図の場合、python edit_mp4.py とすれば実行する。

f:id:HK29:20191022103404j:plain

◇③実行後

下図のようにファイルを4つ出力する。

完成版mp4は「zzz_out3_w_audio.mp4」です。その他は途中過程の中間ファイルを出力しています。例えば、mp3は音声のみ抽出したファイルです。

f:id:HK29:20191022104151j:plain

▼本プログラム

特記事項は、コードの下の方にある下記で入力因子の値を指定できます。

if __name__ == '__main__':
### 設定項目
input_video = "WGJB6714.mp4"
cut_time = (0, 137) # (start, end) second
out_audio = 'zzz_audio.mp3'
out_video1 = 'zzz_out1_simple.mp4'
out_video2 = 'zzz_out2_wo_audio.mp4'
out_video3 = 'zzz_out3_w_audio.mp4'
out_size = (1280, 720) # (width, height) = (1920, 1080)
fps = 30

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import cv2
from PIL import Image, ImageFont, ImageDraw
import numpy as np
import moviepy.editor as mp

def simplification(input_file, out_file, cut_time, fps):
    start, end = cut_time
    video = mp.VideoFileClip(input_file).subclip(start, end)#動画の抽出範囲
    video.write_videofile(out_file, fps=fps) # fpsの指定

def image_add_message(img, message):
    font_path = 'C:\Windows\Fonts\meiryo.ttc'           # Windowsのフォントファイルへのパス
    font_size = 54                                      # フォントサイズ
    font = ImageFont.truetype(font_path, font_size)     # PILでフォントを定義
    img = Image.fromarray(img)                          # cv2(NumPy)型の画像をPIL型に変換
    draw = ImageDraw.Draw(img)                          # 描画用のDraw関数を用意
    
    # テキストを描画(位置、文章、フォント、文字色(BGR+α)を指定)
    draw.text((50, 50), message, font=font, fill=(255, 255, 255, 0))
    img = np.array(img)                                 # PIL型の画像をcv2(NumPy)型に変換
    return img                                          # 文字入りの画像をリターン

def edit_mp4(input_file, out_file, out_size, fps):
    # アンパック代入
    out_width, out_height = out_size
    # 動画とそのフレームワーク情報を取得
    video = cv2.VideoCapture(input_file)
    width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
    size = (width, height)
    num_of_frame = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
    fps = int(video.get(cv2.CAP_PROP_FPS))

    # 出力フォーマットの指定
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    writer = cv2.VideoWriter(out_file, fourcc, fps, out_size)
    # アスペクト比を保ったままた、縦のサイズを指定のサイズに合わせるための比率を算出
    scale = out_height / height
    #cv2.imwrite("blank.bmp", new_img);
    # フレーム1枚ずつ処理する
    flag = num_of_frame // len(global_list)
    j=0
    k=0
    for i in range(num_of_frame):
        if i % 30 == 0:
            print(str(i) + "/" + str(num_of_frame))
        ret, frame = video.read()
        # 無地の画像ファイルをnumpyで作成する
        new_frame = np.zeros((out_height, out_width, 3), np.uint8)
        if out_width == width:
            #print("set A")
            new_frame[0:out_height, 0:out_width] = frame
        else:
            #print("set B")
            frame = cv2.resize(frame, dsize=None, fx=scale, fy=scale)
            frame_height, frame_width = frame.shape[:2]
            offset = int((out_width - frame_width)*2/3)
            #print(offset)
            #new_frame[上のy座標:下のy座標, 左のx座標:右のx座標]
            new_frame[0:out_height, offset:(frame_width+offset)] = frame
        ### 文字列の挿入
        '''
        print("i -> " + str(i) + "/" + str(num_of_frame))
        print("k/len(global_list) -> " + str(k) + "/" + str(len(global_list)))
        print(global_list[k])
        '''
        new_frame = image_add_message(new_frame, global_list[k])
        writer.write(new_frame)
        j += 1
        if flag < j:
            j = 0
            k += 1
    # 後処理
    writer.release()
    video.release()
    print(str(size) + " -> " + str(out_width) + "," + str(out_height))
    print("num_of_frame -> " + str(num_of_frame))
    print("fps -> " + str(fps))

def extract_audio(input_video1, input_video2, out_file, out_audio):
    clip_in = mp.VideoFileClip(input_video1).subclip()
    clip_in.audio.write_audiofile(out_audio)
    clip_out = mp.VideoFileClip(input_video2).subclip()
    clip_out.write_videofile(out_file, audio=out_audio)

global_list = ['ドラクエモンスターズ\nスーパーライト\n略して、DQMSL',
               '配信開始は2014年\n今更、\nどんな感じなのかやってみた',
               'スマホはiPhoneXR\n操作はヌルヌル動く',
               'まだレベルが低いため\nほぼ単なる\n打撲攻撃だけ',
               'ある程度進むめば\n面白しろそうと思った',
               'ただ、ふと思う',
               'こやつらは\n戦って\n仲間になる',
               'なぜケンカする\n必要があるのか?\n',
               '最初から、\n話しすれば済みそう(笑)',
               'ところで、この元は\niPhoneの「画面収録」による\n3分の録画でした。',
               'スマホゲームを録画すると\n アスペクト比が縦長になります。',
               '編集は、\n普通のソフトを使わず\nPythonで次の4つを加工した。',
               '1. fpsを60→30削減\n2. 尺を3分→2分程に短縮',
               '3. 解像度を横長に変更\n4. 文章をこうして入れた',
               '終わり',
               ]

if __name__ == '__main__':
    ### 設定項目
    input_video = "WGJB6714.mp4"
    cut_time = (0, 137) # (start, end) second
    out_audio = 'zzz_audio.mp3'
    out_video1 = 'zzz_out1_simple.mp4'
    out_video2 = 'zzz_out2_wo_audio.mp4'
    out_video3 = 'zzz_out3_w_audio.mp4'
    out_size = (1280, 720) # (width, height) = (1920, 1080)
    fps = 30
  
    ### 関数呼び出し
    # 編集1. 指定区間を抽出する。fpsも変更できる。
    simplification(input_video, out_video1, cut_time, fps)
    input_video = out_video1
    # 編集2. アスペクト比を保ったまま、指定のサイズへ変更する。テキストも入れる。しかし、ここでは音なし。
    edit_mp4(input_video, out_video2, out_size, fps)
    input_video1 = out_video1 # with audio
    input_video2 = out_video2 # fixed video
    # 編集3. 音楽を編集1の動画から抽出して、編集2の動画へ結合して完成
    extract_audio(input_video1, input_video2, out_video3, out_audio)

##### 以下、参考にさせて頂いたHP

▼日本語挿入に関して、下記リンクの関数がそのまま使える形であったのでコピペさせて頂きました。https://watlab-blog.com/2019/08/25/image-text/

以上

<広告>