見出し画像

【Python】moviepy(1.0.3)の動画のクロップサイズ指定の不具合(たぶん)

【状況】動画の(x1, y1)-(x2, y2)の領域を切り抜いて別動画にしてくれるはずが,サイズがわずかに小さく切り取られて動画にエンコードしても再生できない.
【対処】x2, y2に1ずつ加えて切り抜き関数を呼び出す(という嫌なやり方で対応できたけど・・・ライブラリ変えますw)

切り抜いた動画が再生できない!

縦横16の倍数にしてmp4エンコードしたはずなのに,できたファイルが再生できない.ファイルの「プロパティ」→「詳細」でフレームサイズを確認すると,なぜか縦横2ずつ小さい.別の個所の切り抜きでも同じなので,デバッガで止めてcropの戻り値を確認.

入力座標は,(0, 239)-(559, 558)で,幅と高さは,width=560, height=320(両方とも16の倍数)となりますが,切り抜き関数から得られたデータのフレームサイズを見ると,

size: (559, 319)

1ずつ小さい!せっかく16の倍数になるように計算したのに,16の倍数から必ず外れてしまいますね.

検証する

縦横ともに1ずつ足りないという規則性があったので,原因は推測できます.

座標値(x1, y1)(x2, y2)から幅width, 高さheightを計算するときに,以下のように表します.

width = x2 - x1 + 1
height = y2 - y1 + 1

具体的な値を入れると分かりやすいけど,(0, 0)-(9, 9)で幅と高さが10になるはずですが,座標値で引き算するだけでは1足りないんですよね.つまり,ライブラリの実装で+1が抜けているんでしょ,これ.

ちなみに,動画ファイルのプロパティを見ると,フレーム幅558, フレーム高318になっていてさらに意味不明な結果でした・・・.

【変更前】

clip = video.fx(crop, x1=x1, y1=y1, x2=x2, y2=y2) 

【変更後】

clip = video.fx(crop, x1=x1, y1=y1, x2=x2+1, y2=y2+1) 

再度実行して,デバッガでフレームサイズを確認.

size: (560, 320)

フレームサイズは意図通り.ファイルのプロパティを見たらフレーム幅560, フレーム高320,と正しく,動画の再生も問題なし.

画像の中心,幅と高さを指定する方法も怪しい

画像の中心,幅と高さを与える方法もあります.

切り抜き領域が(0, 239)-(559, 558)のとき,以下のように計算すると,
width = 560, height = 320, x_center = 279, y_center = 398
となります.

width = x2 - x1  + 1
height = y2 - y1 + 1
x_center = (x1 + x2) // 2
y_center = (y1 + y2) // 2
clip = video.fx(crop, x_center = x_center, y_center = y_center, width = width, height = height) 

横方向について,0~279の280ピクセルあるので一見良さそうに見えますが,幅方向のフレームサイズが0となってしまい,動画が生成されません(エラー表示などがないので,気づきにくかった).

これは,ライブラリ関数内部において行われているであろう,フレーム内に領域が収まっているかどうかの左端の判定が,

x_center - width / 2 >= 0 

のようになっていることが推測されます.

試しに1ピクセル分横にずらし,(1, 239)-(560, 558),同様に計算すると,
width = 560, height = 320, x_center = 280, y_center = 398
となります.このときは画像が生成されましたので,推測が当たっているようです.であれば,中心位置の調整によって対応が可能になります.

ここでは,中心の計算の際に1を加算します.

x_center = (x1 + x2 + 1) // 2
y_center = (y1 + y2 + 1) // 2

結果,x_center = 280となります.この値を与えて同じ処理をしたところ,幅方向のフレームサイズが560となり,意図通りの動画ができあがりました.1ピクセルずつ減らして確認したので,左端については十分な対応ができています.

右端について,少しずつ値を変えて実験したところ,フレームの幅frame_widthを用いると,以下のような判定をしていることが予想されます.

x_center + width / 2 <= frame_width 

ただし,左端とは異なり,右端からはみ出た部分は自動的にカットしているようです.左端のようにファイルが出力されない,ということはありませんでした.すっきりしたけど,すっきりしないw

結局どちらを使うか

以前は使っていて問題点に気づかなかったのですが,たまたま特殊なケースで大当たりしたようです.とはいえ,回避するためにつじつま合わせするのは・・・.

と,ここまで書いていて気づいてしまった.トリミングしたいだけならffmpeg使えばいいんじゃ?(ffmpegはパスの設定なども必要で面倒なので,インストールだけで楽なmoviepy使ってたんだけどね)

この記事が気に入ったらサポートをしてみませんか?