動画の静止を ffmpeg で検出するとき、閾値はビット深度で変わる
※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。
結論
tblend と signalstats で静止を検出するときの YAVG 閾値は輝度のレンジに比例するため、12bit で 4.8 だった閾値は 10bit では 1.2 に読み替えないと判定が丸ごと変わる。
結論
YAVG の閾値は、素材のビット深度に比例します。
手元の実測値はこうでした。
| 書き出し | pix_fmt |
静止とみなす YAVG |
|---|---|---|
| ProRes 4444 | yuv422p12le(12bit) |
4.8 |
| ProRes 422 HQ | 10bit | 1.2 |
この2つはちょうど4倍差です。12bit の輝度は 0〜4095、10bit は 0〜1023 で、レンジ自体が約4倍あります。同じ画の差分でも、ビット深度が違えば YAVG の数字がそのまま4倍変わります。
なので、12bit 前提の 4.8 を 10bit の素材に当てると、動いているフレームまで静止と判定します。逆に 10bit 前提の 1.2 を 12bit に当てれば、静止しているのに動いていることになります。
何をやっているか
静止フレームの検出は、素直にやるとこうなります。
- 前後のフレームの差分を作る(
tblendのdifference) - その差分画像の明るさの平均を読む(
signalstatsのYAVG) - 平均が閾値を下回っている区間を「静止」とみなす
差が無いフレームほど差分画像は黒くなり、YAVG は 0 に近づきます。「どれだけ黒いか」を静止の代わりに使っているわけです。
だから YAVG は輝度の値そのものであって、比率ではありません。レンジが変われば数字が変わります。
使う前に確認する
閾値を当てる前に、素材が何 bit なのかを見ます。
ffprobe -v error -select_streams v:0 \
-show_entries stream=pix_fmt,codec_name \
-of default=nw=1 input.mov
codec_name=prores
pix_fmt=yuv422p12le
12le なら12bit、10le なら10bitです。
**書き出し設定を変えたつもりが無くても変わります。**コーデックのプリセットを ProRes 4444 から 422 HQ に切り替えるだけでビット深度が落ちるので、閾値を据え置いたまま計測すると、その日から判定が静かに壊れます。
エラーは出ません。数字は出続けます。出続けるのに意味が変わっているのがこの手の計測のこわいところです。
閾値は実測で決める
引き継いだ値をそのまま使わず、自分の素材で境目を取ります。
- 明らかに静止している区間を選んで YAVG を測る
- 明らかに動いている区間を選んで YAVG を測る
- その間に閾値を置く
2つの値が近すぎるなら、その素材は YAVG では分離できません。閾値をいじって粘るより、分離できないと認めるほうが早く終わります。
長い動画は分割する
長尺をそのまま流すと、レンダの途中で落ちても気づけません。
手元では1080フレームずつの4分割にして、それぞれを前面で走らせてから結合しています。バックグラウンドに逃がしたくなりますが、落ちたことに気づかないまま次の工程へ進むほうが高くつきます。
数字が出ていることを、測れていることと混同しない
この計測でいちばん危ないのは、閾値が間違っていても結果は普通に出るという点です。
「静止 0 件」という出力は、静止が無いのか、閾値が緩すぎて拾えていないのかを区別しません。同じ画面が出ます。
pix_fmt を毎回確認するのは、その区別を人間の側で持っておくためです。
よくある質問
Q1静止フレームを機械的に検出するにはどうしますか?
前後フレームの差分を tblend で作り、その明るさの平均を signalstats の YAVG で読みます。差が無いフレームほど YAVG が小さくなるので、閾値を下回った区間を静止とみなします。閾値は素材のビット深度に依存するので、固定値を使い回さないでください。
Q2閾値はどう決めればいいですか?
実測して決めます。12bit(yuv422p12le)で 4.8、10bit で 1.2 が手元の値でした。この2つは輝度レンジの比とほぼ同じ4倍差です。新しい素材では、明らかに静止している区間と明らかに動いている区間の YAVG を実際に測って境目を取ってください。
Q3自分の素材のビット深度はどう確認しますか?
ffprobe で pix_fmt を見ます。yuv422p12le なら12bit、yuv422p10le なら10bitです。書き出し設定を変えたつもりが無くても、コーデックのプリセットを切り替えると変わることがあるので、閾値を使う前に毎回確認するのが安全です。
Q4長い動画で処理が終わりません
フレーム数で分割してレンダしてから結合します。手元では1080フレームずつの4分割にしています。バックグラウンドに逃がすと途中で落ちたことに気づけないので、前面で走らせて終了を確認するほうが結局速く終わります。