特定のフレーム間隔での出力ならtrim、atrimフィルタでもよいが、selectフィルタはピクチャータイプやインターレースタイプの指定でも出力できる。難点はどちらもデコードしながら処理を進めるがselectフィルタのほうが遅い。

基本コマンド

selectフィルタを使いながら音声も同期しつつ出力するのは難しいので音声は無効化-anしている。同じように同期させるならselectフィルタと同様にaselectフィルタで同じ時間にフィルタを当てる。

0(最初)から100フレーム目までの合計101フレームを出力する。
ffmpeg -i input -vf select='between(n,0,100)',setpts=PTS-STARTPTS -vsync 0 -an output

同じ出力結果をtrimフィルタで行う。こちらの方が処理が速い。
ffmpeg -i input -vf trim=start_frame=0:end_frame=101,setpts=PTS-STARTPTS -vsync 0 -an output

開始45秒から30フレーム分出力する。
ffmpeg -i input -vf select='gt(t,45)*lt(selected_n,30)',setpts=PTS-STARTPTS -vsync 0 -an output
ffmpeg -i input -vf trim=45,setpts=PTS-STARTPTS,trim=end_frame=30 -vsync 0 -an output

Iフレームだけ出力する。
ffmpeg -i input -vf select='eq(pict_type,I)' -an -vsync 0 output.mp4
ffmpeg -i input -vf select='eq(pict_type,I)' -an -vsync 0 output-%03d.png

キーフレームだけ出力する。
ffmpeg -i input -vf select='eq(key,1)' -an -vsync 0 output.mp4
ffmpeg -i input -vf select='eq(key,1)' -an -vsync 0 output-%03d.png
ffmpeg -skip_frame nokey -i input -an -vsync 0 output.mp4
ffmpeg -skip_frame nokey -i input -an -vsync 0 output-%03d.png

30000/1001fpsの動画を5フレームに1フレーム間引く。
ffmpeg -i input -vf select=mod(n\,5),setpts=N/(24000/1001*TB),fps=24000/1001 -c:a copy output

ffmpeg でのフレームレート設定の違い

フレーム毎のlavfi.scene_scoreをscenescores.txtに出力する。
ffmpeg -i input -vf select='gte(scene,0)',metadata=print:file=scenescores.txt -an -f null -

シーンチェンジの時間を調べるscdet

lavfi.scene_scoredrawtextフィルタで映像に描写する。
ffplay -i input -vf scdet,select='gte(scene,0)',drawtext=x=10:y=10:fontfile=C\\://WINDOWS/Fonts/arial.ttf:fontsize=24:fontcolor=white:box=1:boxcolor=black@0.4:text='"scene_score "%{metadata\:lavfi.scene_score}'

文字を描写する drawtext

映像の変化が8割以上を出力する。
ffmpeg -i input -vf select='gte(scene,0.8)' -an -vsync 0 output.mp4
ffmpeg -i input -vf select='gte(scene,0.8)' -an -vsync 0 output-%03d.png

画像を1フレームに複数枚並べるにはtileフィルタを使う。
1フレームに複数のフレームを表示する tile

条件の設定には以下の評価式を使う。
ffmpeg で使える評価式

公式ドキュメント:FFmpeg Filters Documentation : select_002c-aselect

オプション

音声のカットはあまり正確ではないらしい

  • expr, e[string]
    選択するフレームを選ぶ。評価式が使える
    既定値:1
  • outputs, n[int]
    出力数の指定。評価式の結果で出力内容を振り分ける。詳細は後述
    既定値:1
    範囲:0 から INT_MAX まで

評価式

  • n
    連続したフレーム。0 から始まる
  • selected_n
    選択された連続フレーム。0 から始まる。条件で範囲を狭めて最初以外からの連続フレームを選択するときに使う
  • prev_selected_n
    選択された連続フレームの最後のフレーム。選択されてなければ NAN
  • TB
    タイムスタンプのタイムベース
  • pts
    映像の PTS
  • t
    映像の PTS 秒。pts*TB=tになる
  • prev_pts
    映像の PTS。ptsとの違いは1フレーム次の pts までのフレームを対象とする
  • prev_selected_pts
    最後にフィルタが当たった映像フレームの PTS。固定値。不明ならば NAN。使い方が分からない
  • prev_selected_t
    選択された最後の映像フレームの PTS 。固定値。不明ならば NAN。使い方が分からない
  • start_pts
    最初の映像フレームの PTS。一般的には 0 になることが多い。固定値。不明ならば NAN
  • start_t
    最初の映像フレームの PTS 秒。一般的には 0 になることが多い。固定値。不明ならば NAN
  • pict_type (video only)
    ピクチャータイプの指定
    • I:I フレーム
    • P:P フレーム
    • B:B フレーム
    • S:スイッチフレーム。詳細不明。VC-1で使われているらしい
    • SI:スイッチングIフレーム
    • SP:スイッチングPフレーム
    • BI:スペシャルイントラフレーム
  • interlace_type (video only)
    フィールドタイプの指定
    • PROGRESSIVE
    • TOPFIRST
    • BOTTOMFIRST
  • consumed_sample_n(audio only)
  • samples_n(audio only)
  • sample_rate(audio only)
  • key
    キーフレームなら 1、それ以外なら 0。I フレームより出力が少なくなる。つまりキーフレームはすべてIフレームだが、Iフレームのすべてがキーフレームではない。ffprobekeyはこれのこと
  • pos
    position の値。デコードしたところまでの合計ファイルサイズ。分からなければ -1 になる。showinfoフィルタで調べられる
  • scen(video only)
    変化率。小さい値ほど変化が小さく、大きい値ほど変化が大きいフレームだけを検出する。一般的には大きな値になるほどIフレームだけになる。0 は完全一致
  • concatdec_select
    concatdemuxer でinpoint, outpoint, durationを使うと一部分だけ選択できるが、このオプションで指定した区間では先に選択した部分が含まれないフレームをスキップできる。これによって再エンコードするときに連結部分がうまくつながる

outputs, n の使用例

シーンチェンジの割合でフィルタを使い分ける。シーンチェンジが9割未満にbm3dフィルタを当てる。
ffplay -i input -vf "select='1*gte(scene,0.9)+2*lt(scene,0.9)':n=2[0][1];[1]bm3d=1[1a];[0][1a]interleave"

e=1 のとき1出力目の[i]が出力され、e=2 のとき2出力目の[p]が出力され、e=3 のとき3出力目の[b]が出力される。
ffmpeg -i input.mp4 -filter_complex "setpts=N/TB,select='1*eq(pict_type,PICT_TYPE_I)+2*eq(pict_type,PICT_TYPE_P)+3*eq(pict_type,PICT_TYPE_B)':n=3[i][p][b]" -vsync 0 -map "[i]" -r 1 -frame_pts 1 iframe_%04d.png -map "[p]" -r 1 -frame_pts 1 pframe_%04d.png -map "[b]" -r 1 -frame_pts 1 bframe_%04d.png

video – ffmpeg save images with frame type – Super User

interleaveフィルタを使って複数出力を1つに戻すことができる。フレームタイプ別にフィルタを当て、Iフレームにはsabフィルタ、Pフレームにはbm3dフィルタ、Bフレームにはnlmeansフィルタを当てる。
ffplay -i input -vf select='1*eq(pict_type,B)+2*eq(pict_type,P)+3*eq(pict_type,B)':n=3[i][p][b];[i][p][b]interleave=3
ffplay -i input -vf select='1*eq(pict_type,B)+2*eq(pict_type,P)+3*eq(pict_type,B)':n=3[i][p][b];[i]sab[i0];[p]bm3d=1[p0];[b]nlmeans[b0];[i0][p0][b0]interleave=3

連続したフレームをN分割してそれぞれ個別出力する例。
ffmpeg -i input -filter_complex select='1+mod(n,4)':n=4[0v][1v][2v][3v] -map "[0v]" -map 0:a 0.mp4 -map "[1v]" -map 0:a 1.mp4 -map "[2v]" -map 0:a 2.mp4 -map "[3v]" -map 0:a 3.mp4

特定のフレーム間隔で出力する。以下は4フレーム間隔の例で2出力する。
ffmpeg -i input -filter_complex select='1*eq(mod(n+1,4),0)+2*eq(mod(n+3,4),0)':n=2[0v][1v] -map "[0v]" -map 0:a 0.mp4 -map "[1v]" -map 0:a 1.mp4

eq(mod(N,4),0)の出力フレーム番号。1が開始フレーム。

N 出力フレーム
n 1, 5, 9
n+1 4, 8, 12
n+2 3, 7, 11
n+3 2, 6, 10

concatdec_select の使用例

FFMPEG: seeking using a txt file – Stack Overflow
【ffmpeg】動画・音声を連結する concat の使い方 其の3

ffmpeg -f concat -segment_time_metadata 1 -i list_of_movies.txt -vf select=concatdec_select,setpts=N/FRAME_RATE/TB -af aselect=concatdec_select,asetpts=N/SR/TB output.mp4

trimフィルタとの違い

フィルタ内をフレーム単位や時間単位で編集するにはどちらのフィルタも同じように使えるが先に書いたようにselectフィルタのほうが遅い。selectフィルタは複数出力が出来るので、フレームを過不足無く分割出力するときにinterleaveフィルタを使うと分割毎にフィルタを使い分けられる。

使い分けるサンプルコマンド。10秒のテストソースの動画を出力する。
ffmpeg -f lavfi -i testsrc2=d=10 testsrc2.mp4

3秒から7秒までの動画にフィルタで編集する。trimフィルタは7秒のフレームを含まないが、selectフィルタは7秒のフレームを含む。どちらのフィルタで編集してもタイムスタンプは保持している。
ffmpeg -i testsrc2.mp4 -vf trim=3:7 trim.mp4
ffmpeg -i testsrc2.mp4 -vf select='between(t,3,7)' select.mp4

タイムスタンプを調べる。
ffprobe -v error -i trim.mp4 -select_streams v:0 -show_entries packet=pts_time -of default=noprint_wrappers=1:nokey=1 > trim.txt
ffprobe -v error -i select.mp4 -select_streams v:0 -show_entries packet=pts_time -of default=noprint_wrappers=1:nokey=1 > select.txt

タイムスタンプをsetptsフィルタで0秒開始にする。
ffmpeg -i testsrc2.mp4 -vf trim=3:7,setpts=N/FRAME_RATE/TB trim-setpts.mp4
ffmpeg -i testsrc2.mp4 -vf select='between(t,3,7)',setpts=N/FRAME_RATE/TB select-setpts.mp4

改めてタイムスタンプを調べる。
ffprobe -v error -i trim-setpts.mp4 -select_streams v:0 -show_entries packet=pts_time -of default=noprint_wrappers=1:nokey=1 > trim-setpts.txt
ffprobe -v error -i select-setpts.mp4 -select_streams v:0 -show_entries packet=pts_time -of default=noprint_wrappers=1:nokey=1 > select-setpts.txt

タイムスタンプをリセットしないとconcatフィルタなどでつなげたときに再生が意図したものにならない。
ffmpeg -i testsrc2.mp4 -vf split[1],trim=3:7[0];[1]trim=8:9,[0]concat trim-concat.mp4
ffmpeg -i testsrc2.mp4 -vf split[1],select='between(t,3,7)'[0];[1]select='between(t,8,9)',[0]concat select-concat.mp4

タイムスタンプをリセットしてつなげる。
ffmpeg -i testsrc2.mp4 -vf split[1],trim=3:7,setpts=N/FRAME_RATE/TB[0];[1]trim=8:9,setpts=N/FRAME_RATE/TB,[0]concat trim-setpts-concat.mp4
ffmpeg -i testsrc2.mp4 -vf split[1],select='between(t,3,7)',setpts=N/FRAME_RATE/TB[0];[1]select='between(t,8,9)',setpts=N/FRAME_RATE/TB,[0]concat select-setpts-concat.mp4

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)