PYTHON で動画を画像変換し、画像を動画変換する

概要

 動画を扱うことがありますが、あまりファイルサイズが大きくならない様に時間を短くしたり、圧縮したりしています。
 PYTHONで動画を画像変換したり、反対に連続画像を動画に変換できる様なので、フレーム数を少なくしたり、画像毎にサイズ変更するなどによる画質劣化やサイズ圧縮効果など確認しました。

動画の作成

 変換前の元動画ですが、先々回の投稿で作成した動画利用します。       
 この動画は python プログラムウィンドウを「XBoxGameBar」(‘ctrl’+‘G’ で起動)でスクリーンキャプチャーし、Windows10に最初からインストールされている「ビデオエディッター」で時間や再生速度等を編集したものです。
 動画の下に書いてある通り、元動画のフレームレートは 30FPS、画素数は 1600X1200 です。動画の時間は約10秒でファイルサイズは3.97(MB)です。動画→画像→動画への変換時にフレームレートと画素数を変化させて、画質とファイル容量を確認してみます。

【変換前の元動画】30FPS,画素数 : 1600X1200,サイズ : 3.97MB

動画→画像→動画 変換

 次の動画は変換結果です。フレームレート・画素数を変えた8動画を「ビデオエディッター」で連結し、挿入しています。理由は解りませんが、YouTubeにアップロードしリンクは出来ましたが、変換した動画を直接このページに挿入することは出来ませんでした。

 各動画の左上にフレームレートと画素数を表示していますが、元動画にはなく、変換過程で追記したものです。

【変換後の8画像を合成】左上にフレームレート・画素記載

 次の表は動画と同じ順番でフレームレート・画素数・ファイルサイズ・元画像に対するファイルサイズ比率を示しています。画質の良し悪しは、利用目的や感性にも依るのでジャッジしていません。

変換動画のファイルサイズ
NoFPS画素数サイズサイズ比率
301600X12003.971
5800X6001.210.30
5600X4000.970.24
5400X3000.580.20
5200X1500.240.06
3800X6000.790.20
3600X4000.610.15
3400X3000.380.10
3200X1500.170.04

 次の図は、動画から画像ファイルに変換した状態の一部です。今回の場合、311ファイル(※)が生成されています。
 ※約10(秒) X 30(FPS) = 約300ファイル

【 動画→画像変換で生成された画像ファイルの一部 】

  

アニメーション挿入テスト

 次の動画の通り、変換過程で橙色の飛行機アイコン画像を挿入しています。アイコン画像を追加する際に位置を少しづつずらして挿入することで、アニメーションのようになることを確認しました。

プログラム

 「動画→画像→動画 変換」プログラムです。

import cv2
import os
import shutil
import glob
import time
import sys

# 動画の全フレームを画像保存
def save_all_frames(video_path, dir_path, basename, ext='jpg'):
    cap = cv2.VideoCapture(video_path)

    if not cap.isOpened():
        return

    os.makedirs(dir_path, exist_ok=True)
    base_path = os.path.join(dir_path, basename)

    digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))

    n = 0

    while True:
        ret, frame = cap.read()
        if ret:
            cv2.imwrite('{}_{}.{}'.format(base_path, str(n).zfill(digit), ext), frame)
            n += 1
        else:
            return


# 画像から動画に変換する
def image_to_video(images , frm_r , width , height , pth , img_rate ):

    fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
    video = cv2.VideoWriter(pth, fourcc, frm_r , (width, height))

    n = 0
    for i in range(len(images)):
        n += 1
        if n % img_rate == 0 :
            img = cv2.imread(images[i])
            img = cv2.resize(img , (width , height))

            cv2.putText(img, str(frm_r) + " fps" , (90, 30), cv2.FONT_HERSHEY_PLAIN, 2,(255, 0, 255), 2, cv2.LINE_AA)
            cv2.putText(img, str(width) + " X " + str(height) , (90, 65), cv2.FONT_HERSHEY_PLAIN, 2,(255, 0, 255), 2, cv2.LINE_AA)
            
            video.write(img) 
        
    video.release()




if __name__ == '__main__':
    
    f_name = "org_std"
    pth_mov_src = "C:/Users/省略/" + f_name + ".mp4"
    pth_fld_img = "C:/Users/省略/" + f_name + "_"

    frm_rate_std = 30                                               # 変更不可
    pic_rate = 10                                                   # 間引き割合・フレームレート変更

    if frm_rate_std % pic_rate != 0:
        print("フレームレート設定異常(強制終了)")
        sys.exit()
    
    frm_rate = int(frm_rate_std / pic_rate)                         # 割り切れる様にする
    
    frm_w = 800                                                     # 元:1600
    frm_h = int(frm_w*3/4)                                          # 元:1200
    
    
    for i in range(1):
        
        # 動画を画像に変換する

        if i==0:
            ext_inf = "jpg"
        else:
            ext_inf = "png"

        print ("画像変換開始(" + ext_inf + ")")
        pth_tmp = pth_fld_img + ext_inf
        
        if os.path.isdir(pth_tmp) :
            shutil.rmtree(pth_tmp)                                  # ファイルごとディレクトリ削除 
        if not os.path.isdir(pth_tmp) :
            os.mkdir(pth_tmp)                                       # ディレクトリ作成
    
        save_all_frames(pth_mov_src, pth_tmp , "test" , ext_inf )   # 全フレームを画像ファイル保存
        print ("画像変換終了(" + ext_inf + ")")
        print("")



        # 画像を動画に変換する
        print ("動画変換開始(" + ext_inf + ")")

        pth_tgt_tmp = pth_fld_img + ext_inf + "_" + str(frm_rate) + "_" + str(frm_w) + "_" + str(frm_h) + ".mp4"
        
        img_src = pth_tmp + "/*." + ext_inf
        images = sorted(glob.glob(img_src))
        image_to_video(images , frm_rate , frm_w , frm_h , pth_tgt_tmp ,pic_rate )
        print ("動画変換終了(" + ext_inf + ")")
        print("")
        print("")
        

 アニメーションを挿入する場合は、「 image_to_video 」関数を下記に変更します。

def image_to_video(images , frm_r , width , height , pth , img_rate ):

    img2 = cv2.imread("C:/Users/省略/plane_r.png")
    rows,cols,channels = img2.shape

    fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
    video = cv2.VideoWriter(pth, fourcc, frm_r , (width, height))

    n = 0
    for i in range(len(images)):
        spd_x = 70 + 2 * i
        spd_y = i
        
        n += 1
        if n % img_rate == 0 :
            img1 = cv2.imread(images[i])
            img1 = cv2.resize(img1 , (width , height))

            roi = img1[ spd_y : rows + spd_y , spd_x : cols + spd_x ]

            img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
            ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
            mask_inv = cv2.bitwise_not(mask)

            img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)
            img2_fg = cv2.bitwise_and(img2,img2,mask = mask)

            dst = cv2.add(img1_bg,img2_fg)
            img1[ spd_y : rows + spd_y , spd_x : cols + spd_x ] = dst

            video.write(img1) 
        
    video.release()

まとめ

 変換した動画をこのページに直接挿入出来なかったことは残念です。また、時間のある時に原因を調べようと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です