Python 画像連結により動画を作成する方法 OpenCV

 '20/07/26更新:本文を一部編集しました。
 下記動画は写真を連結をして作成しました。音楽の挿入はWindows10標準搭載のフォトを使用していますが、それ以外の加工は全てPythonです。

www.youtube.com

 ▼本プログラム実行のためのフォルダ環境

f:id:HK29:20191014151728p:plain

① jpg2avi.py…本体ファイル(コードは本記事の最後に記載してます) 

②openh264-1.8.0-win64.dll…mp4も作成できるコーデック(DL先は下記)

 https://github.com/cisco/openh264/releases

③img…動画作成のための連結する画像ファイルを入れておくフォルダ

▼imgフォルダ内
画像ファイルのアスペクト比(縦横比)は統一されてなくて構いません。本コードには、指定した解像度へ画像を調整後に動画にします。

f:id:HK29:20191014151843p:plain

▼実行後のフォルダ内
編集された画像と、それらを連結して出来た動画が出来る。

f:id:HK29:20191014154401p:plain

次の2つの画像を見ると、実際に撮影した時の写真のアスペクト比(縦横比)が異なっていても(2つ目画像の黒塗り潰り以外が元の画像サイズ)、指定した解像度に合わせて画像を出力している様子がわかります。その方法は、指定したサイズの黒無地の画像を下地にして、それに合成することで、全ての画像サイズを同一にします。これにより画像を連結して動画を作成できます。

f:id:HK29:20191014155855j:plain

f:id:HK29:20191014155652j:plain

▼本プログラム
解像度と動画時間の設定は if __name__ == '__main__': 以下にある変数で指定できます。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os, glob, shutil
import cv2
from PIL import Image, ImageFont, ImageDraw
import numpy as np

def image_resize(image, height):
    # 高さに関して指定サイズと現物の比率を計算し、それをx, yに掛けることでアスペクト比を保つ
    scale = height / image.shape[0]
    return cv2.resize(image, dsize=None, fx=scale, fy=scale)

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 jpg2mp4(folder, outfile, width, height, time):
    img_list = glob.glob(folder + '*.jpg') # ファイルをリストへ格納
    print(img_list)
    fps = len(img_list) / (time*60) # fpsを計算する。ファイル数/再生時間

    for i, e in enumerate(img_list):
        ### ファイル名を連番へリネームする(別にしなくてもやりようはあるけど)
        e2 = str(i).zfill(4) + '.jpg'  # zfillの型は文字列
        #os.rename(e, e2)
        shutil.copy2(e, e2) # ファイルをカレントディレクトリへコピー
        img = cv2.imread(e2)

        ### ファイルサイズを指定サイズへ変更する
        dst = image_resize(img, height)
        print(f"{img.shape} -> {dst.shape}")
        dst_height, dst_width = dst.shape[:2]
        print("i -> " + str(i) + "/" + str(len(img_list)-1))
        print(str(dst_width) + " " + str(dst_height))
        
        ### 無地の画像ファイルをnumpyで作成する
        new_img = np.zeros((height, width, 3), np.uint8)
        #cv2.imwrite("blank.bmp", new_img);

        ### 無地の画像ファイルの上に、元ファイルを上書きする。
        ### →全てのファイルが同じ縦横比(アスペクト比)のファイルになる。
        if dst_width == width:
            print("set A")
            #new_img[上のy座標:下のy座標, 左のx座標:右のx座標]
            new_img[0:dst_height, 0:dst_width] = dst
        else:
            print("set B")
            offset = int((width - dst_width)/2)
            print("offset -> " + str(offset))
            new_img[0:height, offset:dst_width+offset] = dst
        ### メッセージの挿入
        new_img = image_add_message(new_img, global_list[i])
        cv2.imwrite(e2 , new_img)
        
    cap = cv2.VideoCapture('%04d.jpg')
    #fourcc = cv2.VideoWriter_fourcc(*'X264')
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') writer = cv2.VideoWriter(outfile, fourcc, fps, (width, height)) while True: ret, frame = cap.read() if not ret: break writer.write(frame) writer.release() cap.release() print("fps -> " + str(fps)) global_list = ['ハロー コサチューブ', 'Hello KosaTube', "\'19/5月 黒幹之(みゆき)メダカを\n 3匹買った", '水草ウィローモスを入れたり\n ↓(これ)', 'ホテイアオイが枯れたりした\n', '一方、赤ちゃん用水槽は\n ダイソーの300円タッパー', '水草と溶岩石くらいは入れましょう', '卵を生んだら\n 優しく腹下を指で摘まんで取る', '生きてる卵は硬くて、簡単に潰れはしない', '図のように広告の上に置く', '指で転がして粘着糸をとる', '一粒ずつばらばらになった。\n カビ生え時の共連れ死防止のため', '孵化方法 その1\n 卵を水草の上に置いたり、', 'ホテイアオイに付けたりして、', 'それを300円タッパーに入れる', '10日前後で孵化する', '孵化方法 その2\n 500mlペットボトルを半分切って、水入れて卵を入れる', '水道水でOK。\n この場合、毎日半分水換え', 'メチレンブルーを入れても良い', 'この場合、水換えは3日に1度でOK', ' 水量はこれくらいに、一滴落とす', '生まれそうになると目が出来る', 'ちょっと引いて撮影', 'もっと引くとこんなん', '生まれました!!\nどこ?', 'めだか「ここじゃー」\n 中央と左上', '生まれたら、引っ越し♪\n コンビニのスプーンですくう', '表面張力で~', '落ちない\(^o^)/', '300円タッパーへ浸けて離す。泳ぎ回る稚魚達', 'エサは、稚魚用を与える', '普通サイズでは、大きすぎて食べられないため', 'タッパーはフタしてホコリ入らないようにする\n 完全密閉はしない', '絶対にダメ\n →全水換えして、全滅した経験あり。。', r"'19/10月現在 弟妹達はすくすく育つ", 'Hello KosaTube\n 次回へつづく'] if __name__ == '__main__': ### 設定項目 input_folder = r"./img/" out_file = 'output.mp4' #'output.avi' out_width = 1920 out_height = 1080 time = 1.5 #[min] ### 関数呼び出し jpg2mp4(input_folder, out_file, out_width, out_height, time)

上記プログラムは、下記のような流れで処理をします。
1. imgフォルダ内にあったファイルをカレントフォルダへコピー
2. ファイル名を連番に変更
3. 指定した解像度へ全てのファイルを一旦、縦のドットに合わして変換
 この時点で横サイズが異なる画像が存在する。
4. 指定した解像度の無地(黒色)の画像をnumpyで作成
5. 4の画像を下地にして3の画像を合成する
6. 日本語文字列を画像に挿入してゆくが、ライブラリはPILを利用しています
7. 画像を連結して動画にする
 (指定した再生時間と、imgフォルダ内にあるファイル数からfpsを計算しています)

 

本コード作成に至るまでに遭遇したエラーのQ&A

◆エラー例1:Could not open codec 'libopenh264': Unspecified error
コーデックがないためのエラー。コーデックをDLする。ver違いを示すエラーが出る場合もある。本記事の冒頭にも記載してますが、DL先は下記リンクです。

https://github.com/cisco/openh264/releases

◆エラー例2:OpenCV: FFMPEG: tag 0x34363258/'X264' is not supported with codec id 27 and format 'mp4 / MP4 (MPEG-4 Part 14)'
mp4ファイル作成する時のコーデックエラー。解像度を大きくすると解決する場合があった。

◆エラー例3:ValueError: could not broadcast input array from shape (960,1707,3) into shape (0,0,3)
画像サイズがはみ出てるエラー。画像を合成する際に、小さいサイズの画像に大きい画像を挿入しようとしてるため。

以上

<広告>