概要
動画を扱うことがありますが、あまりファイルサイズが大きくならない様に時間を短くしたり、圧縮したりしています。
PYTHONで動画を画像変換したり、反対に連続画像を動画に変換できる様なので、フレーム数を少なくしたり、画像毎にサイズ変更するなどによる画質劣化やサイズ圧縮効果など確認しました。
動画の作成
変換前の元動画ですが、先々回の投稿で作成した動画利用します。
この動画は python プログラムウィンドウを「XBoxGameBar」(‘ctrl’+‘G’ で起動)でスクリーンキャプチャーし、Windows10に最初からインストールされている「ビデオエディッター」で時間や再生速度等を編集したものです。
動画の下に書いてある通り、元動画のフレームレートは 30FPS、画素数は 1600X1200 です。動画の時間は約10秒でファイルサイズは3.97(MB)です。動画→画像→動画への変換時にフレームレートと画素数を変化させて、画質とファイル容量を確認してみます。
動画→画像→動画 変換
次の動画は変換結果です。フレームレート・画素数を変えた8動画を「ビデオエディッター」で連結し、挿入しています。理由は解りませんが、YouTubeにアップロードしリンクは出来ましたが、変換した動画を直接このページに挿入することは出来ませんでした。
各動画の左上にフレームレートと画素数を表示していますが、元動画にはなく、変換過程で追記したものです。
次の表は動画と同じ順番でフレームレート・画素数・ファイルサイズ・元画像に対するファイルサイズ比率を示しています。画質の良し悪しは、利用目的や感性にも依るのでジャッジしていません。
変換動画のファイルサイズ
No | FPS | 画素数 | サイズ | サイズ比率 |
元 | 30 | 1600X1200 | 3.97 | 1 |
1 | 5 | 800X600 | 1.21 | 0.30 |
2 | 5 | 600X400 | 0.97 | 0.24 |
3 | 5 | 400X300 | 0.58 | 0.20 |
4 | 5 | 200X150 | 0.24 | 0.06 |
5 | 3 | 800X600 | 0.79 | 0.20 |
6 | 3 | 600X400 | 0.61 | 0.15 |
7 | 3 | 400X300 | 0.38 | 0.10 |
8 | 3 | 200X150 | 0.17 | 0.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()
まとめ
変換した動画をこのページに直接挿入出来なかったことは残念です。また、時間のある時に原因を調べようと思います。