本記事では、下記リンク先のような動画を作成します。
iPhoneで録画した動画をPythonで編集して作成する。
具体的には、次の1~8のような処理をする。
特に、黒■が本記事の新規項目である。一方、白□は以前の記事に記載したものではあるものの、本記事で出来ること一覧として載せる。
実際に真似する場合には、本記事の最後に記載したプログラム中のそれ前後を参考下さい。
□1. 時刻指定による動画の区間抽出
→メソッドがある。
video = mp.VideoFileClip(input_file).subclip(start, end)
■2. 動画の一部分モザイク処理
→動画からフレーム(画像)毎に処理する。
# モザイクを掛ける範囲を切り取り cut_area = img[y:y+h, x:x+w] # 縮小 small = cv2.resize(cut_area, dsize=None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST) # 拡大 zoom = cv2.resize(small, dsize=(w, h), interpolation=cv2.INTER_NEAREST) # 合成する img[y:y+h, x:x+w] = zoom
□3. 動画のアスペクト比を保ったまま、解像度を変更
→縦を軸に縮尺して、その変化率を横に適応することで縦横比を保つ
frame = cv2.resize(frame, dsize=None, fx=scale, fy=scale)
■4. 無地の画像の作成
→numpyで作成する。背景色の指定はRGBをBGRに変換して与える
new_frame = np.zeros((out_height, out_width, 3), np.uint8) # 黒塗り画像 if True: # True:薄黄色画像に変換。False:黒塗り rgb_color = (255, 250, 205) #lemonchiffon bgr_color = tuple(reversed(rgb_color)) new_frame[:] = bgr_color
□5. 無地の画像を下地に画像の合成
→合成する画像の配置位置の指定は、左上の座標と幅と高さで指定
#new_frame[上のy座標:下のy座標, 左のx座標:右のx座標] new_frame[0:out_height, offset:(frame_width+offset)] = frame
■6. 日本語の挿入
→フォント色の指定方法も記載。指定はRGBの順ではなく、BGRの順で指定
# RGB色の代表例 # 白:(255, 255, 255), 青:(0, 0, 255), 赤:(255, 0, 0), 黒:(0,0,0) # マゼンダ:(255, 0, 255), ライム:(0, 255, 0), シアン:(0, 255, 255) # 但し、PILではRGBでなくてBGRなので、青にしたい場合(255, 0, 0) font_color = (255, 0, 0) draw.text((250, 200), message, font_color, font)
□7. 動画から音声のみを抽出mp3
→メソッドがある
clip_in = mp.VideoFileClip(input_video1).subclip()
clip_in.audio.write_audiofile(out_audio)
■8. ビープ音を鳴らす。これにより、プログラム終了を音で知らせる
→from winsound import Beep する。
mysound = [523, 587, 659, 698, 784, 880, 932] # ドレミファソラシ for e in mysound: Beep(e, 1000) # 第二引数の1000は1秒
▼本プログラム
特記事項:モザイクや画像合成の処理において、思った通りにならない、もしくはエラーになる場合は、凡そ、場所指定に問題がある場合が多いと思われます。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import cv2 from PIL import Image, ImageFont, ImageDraw import numpy as np import moviepy.editor as mp from winsound import Beep # 動画を抽出する関数 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' #msgothic.ttc' font_size = 58 font = ImageFont.truetype(font_path, font_size) img = Image.fromarray(img) draw = ImageDraw.Draw(img) # RGB色の代表例 # 白:(255, 255, 255), 青:(0, 0, 255), 赤:(255, 0, 0), 黒:(0,0,0) # マゼンダ:(255, 0, 255), ライム:(0, 255, 0), シアン:(0, 255, 255) # 但し、PILではRGBでなくてBGRなので、青にしたい場合(255, 0, 0) font_color = (255, 0, 0) draw.text((250, 200), message, font_color, font) return np.array(img) # 動画のアスペクト比を調整する関数(但し、ここでは音声は含まれない) def edit_mp4(input_file, out_file, out_size, fps): print("start edit_mp4") # アンパック代入 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 % 50 == 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 True: # True:薄黄色画像に変換。False:黒塗り rgb_color = (255, 250, 205) #lemonchiffon bgr_color = tuple(reversed(rgb_color)) new_frame[:] = bgr_color 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): print("start extract_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) # 動画にモザイクを掛ける関数(中で画像にモザイクを掛ける関数を呼び出している) def mosaic_movie(input_file, out_file, fps, x, y, w, h, scale=0.1): print("start mosaic_movie") # 動画とそのフレームワーク情報を取得 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, (width, height)) # フレーム1枚ずつ処理する for i in range(num_of_frame): ret, frame = video.read() #dst = mosaic_area(frame, x, y, w, h, scale=0.1) # モザイク関数を呼び出し buf = mosaic_area(frame, 70, 850, 360, 50, scale=0.1) dst = mosaic_area(buf, 600, 850, 180, 50, scale=0.1) writer.write(dst) if i % 50 == 0: print(str(i) + "/" + str(num_of_frame)) #cv2.imwrite('mosaic' + str(i) + '.jpg', dst) # 後処理 writer.release() video.release() print("num_of_frame -> " + str(num_of_frame)) print("fps -> " + str(fps)) # 画像にモザイクを掛ける関数 def mosaic_area(img, x, y, w, h, scale=0.1): # オリジナルの縦と横長さ height, width = img.shape[:2] # モザイクを掛ける範囲を切り取り cut_area = img[y:y+h, x:x+w] # 縮小 small = cv2.resize(cut_area, dsize=None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST) # 拡大 zoom = cv2.resize(small, dsize=(w, h), interpolation=cv2.INTER_NEAREST) # 合成する img[y:y+h, x:x+w] = zoom return img # ビープ音を鳴らす def alerm(): mysound = [523, 587, 659, 698, 784, 880, 932] # ドレミファソラシ for e in mysound: Beep(e, 1000) # 第二引数の1000は1秒 # 動画に加える日本語のリスト global_list = ['星のドラゴンクエスト\n 略して、星ドラ', '配信開始は2015年。\nその頃から\nプレイしている', 'スマホはiPhoneXRで\n操作はヌルサク動く', 'ネット通信で\n4人で協力プレイ可能', '知らない3名の名前は\nモザイク掛けてますf^^;', 'キャラは職業によって、\n攻撃力や体力、守備力\nなどが異なる', '武器、防具は、\nガチャで入手する', '無論、\nワタクシことぽっくんは\n無課金である', 'いつの間にか4年。。\n無料ガチャ、\n相当引いた。。', 'そのため、\n無課金でもそこそこ強い。', '最近は、武器の\nハイパーインフレ状態。\n次から次へと\n強いのが出てくる(笑)', 'このゲームの面白さは、\n武器、防具集めである', 'それは、昔、\n皆ビックリマンシールを\n集めてたように。。', 'そして、今や\n私もあなたも\n星ドラおっさん♪おばはん', '昔のキャラも出てくる\nドラクエ2の勇者やら=3', 'ところで、\nこの動画編集には、\nPythonを使用した', 'スマホの画面サイズから、\nYouTube用の\nアスペクト比へ変更', 'モザイクを入れたり、\n背景色変えたり、\n文章をこうして入れた', '結局、\n星ドラとPythonは\n面白い!ってこと!?', 'See you', ] # メイン関数 if __name__ == '__main__': ### 設定項目 input_video = "CHEN7662.mp4" cut_time = (3, 263) # (start, end) second out_video1 = 'zzz_out1_simple.mp4' out_audio = 'zzz_audio.mp3' out_video2_wo = 'zzz_out2_mosaic_wo_audio.mp4' out_video2_w = 'zzz_out2_mosaic_w_audio.mp4' out_video3_wo = 'zzz_out3_wo_audio.mp4' out_video3_w = 'zzz_out3_w_audio.mp4' out_size = (1920, 1080) # (width, height) = (1920, 1080) or (1280, 720) fps = 30 ##### 関数呼び出し ### 編集1. 指定区間を抽出する simplification(input_video, out_video1, cut_time, fps) ### 編集2. モザイク処理 input_video = out_video1 mosaic_movie(input_video, out_video2_wo, fps, x=800, y=400, w=400, h=300, scale=0.1) ### 音楽を編集1の動画から抽出して、編集2の動画へ結合する input_video2 = out_video2_wo extract_audio(input_video, input_video2, out_video2_w, out_audio) ### 編集3. アスペクト比を保ったまま指定のサイズへ変更し、日本語文も入れる。 ### 但し、ここで音がなくなる。次の編集3で音声を結合する。 input_video = out_video2_w edit_mp4(input_video, out_video3_wo, out_size, fps) ### 音楽を編集1の動画から抽出して、編集2の動画へ結合する input_video2 = out_video3_wo extract_audio(input_video, input_video2, out_video3_w, out_audio) ### 本プログラムが終わったことを音で知らす。 alerm() ############################# # 上記関数を利用して、動画でなくて画像にモザイクを掛ける場合の例 ''' img = cv2.imread('mosaic0.jpg') img2 = mosaic_area(img, 70, 850, 400, 50, scale=0.1) img3 = mosaic_area(img2, 600, 850, 200, 50, scale=0.1) cv2.imwrite('mosaic_000.jpg', img3) ''' print("finished")
以上
<広告>