OneonetのPyBE

Python,Blender,Excelのいろいろ

動画をタイル状の動画に変換するPythonスクリプト

1つの動画を指定した枚数のタイル状にして表示します。ニュース等で見るモニタを積み重ねて全てに同じ映像を映すアレです。使い道は見当たりませんが、見どころの多い動画を4分割程にして表示するフレームをずらすことで、いつ見ても見どころが映ってる、そんな感じです。ワチャワチャした感じの背景という感じでしょうか。自由にお使いになってください。もし動画作成されたりしたらコメント欄でご紹介頂けますと幸いです。

各タイルでの動画の開始位置はランダムにしてありますが、同じ位置にすることもできます。各タイルで動画を最後まで表示したらランダムな位置に巻き戻しますので短い動画から長時間タイル動画にすることもできます。音は消えてしまうので後から合成してください。ffmpeg等お使いになると楽ちんかと思います。

以下解説です。

6,7行目:
入力ファイルと出力ファイルを指定します。特に制約は無いと思います。

8行目:
write_secに出力動画ファイルの長さを秒で指定します。各タイル毎に最後まで再生したらランダムな位置まで巻き戻していますので、何秒でも指定できます。

9行目:
tileに縦横枚数を指定します。3を指定すれば、3x3の映像になります。値に上限はありませんが処理時間に大きく影響を与えます。3と10では10倍程処理時間がかかります。2ケタにするとハングしているように見えると思います。

10行目:
expにオリジナルの映像サイズのに対する拡大率を指定します。1を指定すれは、入力ファイルと出力ファイルの映像サイズは同じになります。

20行目:
出力動画の形式を変更したい場合はここを修正してください。

26行目:
frame_posのリストに、n×nコのタイル毎に最初の1コマ目のフレームをランダムに格納しています。初期値は0~framecountの範囲で任意です。全てのタイルを先頭から再生したければ全てゼロ、一定間隔ずらしにしければオフセットを指定して初期値を設定してください。

31行目:
デバッグ用です。削っても問題ありません。30行目のforにtqdmを利用したほうがクールかと思います。

36行目:
cap.readはエラーにならない前提です。

38行目:
最後まで再生したらランダムな位置まで巻き戻します。先頭まで巻き戻したい場合はランダムではなくゼロを入れるようにしてください。

40行目:
整数倍でよければフレームスキップが可能です。その場合最大フレーム数溢れのチェックを入れてください。

#-*- coding:utf-8 -*-
import cv2
import random

#入力動画、出力動画のパラメタ設定
src_file = 'domino1.mp4'
dst_file = 'domino25.mp4'
write_sec = 30  # 出力動画の秒数
tile = 5       # n × n の nを指定
exp =2          # オリジナルサイズの何倍にするか?

#内部で利用する値
cap = cv2.VideoCapture(src_file)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)            #動画幅
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)          #動画高
fps = cap.get(cv2.CAP_PROP_FPS)                      #動画FPS
framecount = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  #合計フレーム数

#出力映像の初期設定
fourcc = cv2.VideoWriter_fourcc('m','p','4', 'v')
video  = cv2.VideoWriter(dst_file, fourcc, fps, (int(width*exp), int(height*exp)))

#開始位置の設定
frame_pos = []
for _ in range(tile**2):
    frame_pos.append(random.randint(0,framecount))

tmp_frame_x = [0]*(tile)
tmp_frame_y = [0]*(tile)
for i in range(int(fps * write_sec)):
    print('{} / {}'.format(i, int(fps*write_sec)))
    for y in range(tile):      #縦に結合
        for x in range(tile):  #横一列を読み込む
            tile_pos = y*tile + x
            cap.set(cv2.CAP_PROP_POS_FRAMES, frame_pos[tile_pos]) 
            _, tmp_frame_x[x] = cap.read()
            if frame_pos[tile_pos] >= framecount-1:
               frame_pos[tile_pos] = random.randint(0,framecount)
            else:
               frame_pos[tile_pos] += 1
        tmp_frame_y[y] = cv2.hconcat(tmp_frame_x)  #横に結合

    total_frame = cv2.vconcat(tmp_frame_y)  #最後に縦に結合
    #expに指定された倍率にリサイズ
    resize_frame = cv2.resize(total_frame, dsize=None, fx=exp/tile, fy=exp/tile)
    video.write(resize_frame)

cap.release()
video.release()

是非動画をミラーサイトでご覧になってください。
oneonet-blog.blogspot.com