複数の動画に字幕を付けて1つの動画にまとめる
複数の動画を1つの動画にまとめます。また各動画の説明用字幕を付けます。画像処理ライブラリOpenCVの cv2.VideoCapture で各動画の先頭から3秒まで読みだし、画像処理ライブラリPillowのdraw.textで字幕を付けます。字幕が徐々に現れて徐々に消えるように見せるため、元の動画から読みだした画像とアルファ値を持たせた字幕用画像を、字幕用画像のアルファ値を変えながらPillowのImage.alpha_compositeで合成しています。
環境
準備
Mixkitから3つの動画をダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにフォルダ'movies'を作りその中にファイル名 ’01.mp4', ’02.mp4', ’03.mp4' で保存しました。
White sand beach and palm trees
Black scorpion walking closeup
Fly over a huge canyon covered in vegetation
コード
はじめにmoviesフォルダに含まれるファイルをリストにし、動画をひとつづつ読みだします。cv2.VideoCaptureで動画を取り込み、cap.get(cv2.CAP_PROP_POS_MSEC)で現在の時間を調べ3000msまで取り込みます。字幕用画像のアルファ値を
~1000ms:0から徐々に増える
1000~2000ms:一定の値(220)
2000ms~:0まで徐々に減る
として字幕が表れて消える効果を表現しています。
動画読み出しと動画書き出しをOpenCVで行いますが、字幕の書き込みは日本語が描けるPillowで行うためOpenCV⇔Pillowで画像を受け渡す際には画像フォーマットの変換を行います。
動画はmp4ファイルで書き出すとともに、このブログに貼るためにgifアニメーションも作成しています。
import os from PIL import Image, ImageDraw, ImageFont import cv2 import numpy as np # moviesフォルダ内のファイルをリストに files = os.listdir('movies/') # gifファイル作成用イメージリスト images =[] # 動画ファイル作成用イメージリスト frames =[] # 字幕のリスト subtitles =[ '白い砂浜の南国のビーチ', '森を進む黒いサソリ', '森に囲まれたロッキー渓谷' ] # 字幕のアルファ値(不透明度)の最大値 amax = 220 # 保存用動画ファイルのフォーマット設定 fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') out = cv2.VideoWriter('subtitles.mp4', fourcc, 25.0, (384, 216)) # 動画ファイルをひとつずつ読みだして処理 for i in range(3): movie = files[i] subtitle = subtitles[i] # 動画ファイルのキャプチャー cap = cv2.VideoCapture('movies/'+movie) while(cap.isOpened()): # キャプチャー画像の取り込み ret, frame = cap.read() # 現在時間を取得 time = cap.get(cv2.CAP_PROP_POS_MSEC) # キャプチャー画像があって3000msec以内の場合 if ret==True and time<3000: # 384x216にリサイズ resize = cv2.resize(frame, (384, 216)) # 画像をcv2形式からPillow形式に変換しアルファチャンネルを追加 im = Image.fromarray(cv2.cvtColor(resize, cv2.COLOR_BGR2RGB)) im.putalpha(255) # 字幕のアルファ値設定 if time < 1000: alpha = int(amax*time/1000) elif time > 2000: alpha = int(amax*(3000-time)/1000) else: alpha = amax # 字幕用画像に字幕を縁取り文字で描画 im2 = Image.new('RGBA', (384, 216), (0, 0, 0, 0)) draw = ImageDraw.Draw(im2) font = ImageFont.truetype('C:\Windows\Fonts\BIZ-UDGothicB.ttc', 24) draw.text((40, 180), subtitle, fill=(255, 255, 255, alpha), font=font, stroke_width=1, stroke_fill=(150, 150, 150, alpha)) # 動画のフレームと字幕用画像の合成 comp = Image.alpha_composite(im, im2) # gifファイル作成用イメージリストにフレームを追加 images.append(comp) # Pillow形式からcv2形式に変換 img = cv2.cvtColor(np.array(comp), cv2.COLOR_RGB2BGR) # VideoWriterにフレームを追加 out.write(img) else: # キャプチャー画像がない場合はループ終了 break # 再生画像をクローズ cap.release() # 出力動画ファイルをクローズ out.release() # gif動画保存 images[0].save('subtitles.gif', save_all=True, append_images=images[1:], optimize=True, duration=40, loop=0)
実行結果
3つの動画が字幕を付けて1つの動画にまとめられました。
以下のサイトを参考にさせていただきました
Pythonの文法メモ > 【OpenCV】動画ファイルの読み出しとプロパティ取得、キャプチャー画像の保存
Pythonの文法メモ > 【Pillow】アルファチャンネル付き図形と画像の合成