特定のフレーム間隔での出力ならtrim、atrim
フィルタでもよいが、select
フィルタはピクチャータイプやインターレースタイプの指定でも出力できる。難点はどちらもデコードしながら処理を進めるがtrim、atrim
フィルタはデコード開始終了を指定できるのに対して、select、aselect
フィルタは区間を区切ってもストリーム全体をデコードするので尺が長いほど処理が長くなる。一定周期でフレームを間引くならshuffleframes
フィルタもある。
trim フィルタの使い方
指定フレーム毎に順番を入れ換える shuffleframes
基本コマンド
select
フィルタを使いながら音声も同期しつつ出力するのは難しいので音声は無効化-anしている。同じように同期させるならselect
フィルタと同様にaselect
フィルタで同じ時間にフィルタを当てる。
0(最初)から100フレーム目までの合計101フレームを出力する。
ffmpeg -i input -vf select='between(n\,0\,100)',setpts=PTS-STARTPTS -an output
同じ出力結果をtrim
フィルタで行う。こちらの方が処理が速い。
ffmpeg -i input -vf trim=start_frame=0:end_frame=101,setpts=PTS-STARTPTS -an output
開始45秒から30フレーム分出力する。
ffmpeg -i input -vf select='gte(t\,45)*lt(selected_n\,30)',setpts=PTS-STARTPTS -an output
ffmpeg -i input -vf trim=45,setpts=PTS-STARTPTS,trim=end_frame=30 -an output
-ss -toを併用するには-copytsを併用し入力オプションで指定する。
ffmpeg -ss 6 -to 10 -copyts -i input -vf select='trunc(t+1001/30000+TB)-trunc(t)' imgg-%04d.png
Using FFMPEG select video filter with -ss on input – Stack Overflow
任意のフレームを指定して出力する。フレーム番号をeq()に+でつなげる。
ffmpeg -i input -vf select='eq(n\,10)+eq(n\,30)' -fps_mode vfr output-%03d.jpg
逆に任意のフレームを除き前フレームをコピーして出力する。動画出力時には-rに元の映像のフレームレートを指定する。
ffmpeg -i input -vf select='not(eq(n\,10)+eq(n\,30))' -r 30000/1001 output.mp4
偶数フレームを間引き奇数フレームを残す。
-vf "select=mod(n\,2)"
奇数フレームを間引き偶数フレームを残す。
-vf "select=mod(n-1\,2)"
0/5と3/5周期のフレームを残す。
-vf "select=bitor(not(mod(n\,5))\,not(mod(n+3\,5)))"
Need help with dropping frames : ffmpeg
Iフレームだけ出力する。
ffmpeg -i input -vf select='eq(pict_type\,I)' -an -fps_mode vfr output.mp4
ffmpeg -i input -vf select='eq(pict_type\,I)' -an -fps_mode vfr output-%03d.png
キーフレームだけ出力する。
ffmpeg -i input -vf select='eq(key\,1)' -an -fps_mode vfr output.mp4 ffmpeg -i input -vf select='eq(key\,1)' -an -fps_mode vfr output-%03d.png ffmpeg -skip_frame nokey -i input -an -fps_mode vfr output.mp4 ffmpeg -skip_frame nokey -i input -an -fps_mode vfr output-%03d.png
一定間隔のキーフレームで出力する。コマンド例は7フレーム毎。
ffmpeg -y -skip_frame nokey -i input.mp4 -vf "select='if(eq(n\,0)\,1\,gte(t-prev_selected_t\,7))'" -fps_mode vfr -frame_pts true out-%06d.png
ffmpeg – Obtain the filtered keyframe screenshot from the video – Stack Overflow
30000/1001fpsの動画を5フレームに1フレーム間引く。フレーム数が4/5になるのでsetpts
で改めて正しいフレームレートを指定する。
ffmpeg -i input -vf select=mod(n\,5),setpts=N/((24000/1001)*TB),fps=24000/1001 -c:a copy output
一定フレーム間隔のタイムラプスの動画を作る。例は1/960に間引いている。音声は同期できないので-anを併用する。
ffmpeg -i input -vf "select='not(mod(n\,960))',setpts=PTS/960" -an output
Is it possible to timelapse a video for a set duration? Say a 16 hour video into 1 minute : ffmpeg
フレーム毎のlavfi.scene_scoreをscenescores.txtに出力する。
ffmpeg -i input -vf select='gte(scene\,0)',metadata=print:file=scenescores.txt -an -f null -
ログファイルにデコードしたフレーム番号とタイムスタンプを記載する。
ffmpeg -report -i input -vf select='if(eq(n\,0)\,1\,floor(t)-floor(prev_selected_t))' -an -f null -
linux – Per second frame count using ffmpeg – Stack Overflow
ffprobeを使えばスコアだけ出力できる。setpts
フィルタのN+1で開始フレーム番号を0ではなく1にしている。
ffprobe -f lavfi -i "movie=input.mp4,setpts=N+1,select=gte(scene\,0)" -select_streams v -show_entries packet_tags=lavfi.scene_score -of default=nk=1:nw=1 > scenescores.txt
How to save frame numbers with ffmpeg? – Stack Overflow
video – Using FFMPEG: How to do a Scene Change Detection? with timecode? – Stack Overflow
lavfi.scene_scoreをdrawtext
フィルタで映像に描写する。
ffplay -i input -vf 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
フィルタでscene_scoreを描写し、drawgraph
フィルタで折れ線グラフで描写し、s=1280×256は入力映像の横幅を揃える。drawgrid
フィルタで補助線を加え、vstack
フィルタで上下に並べる。
ffplay -i input -vf select='gte(scene\,0)',drawtext=y=H/20:fontfile=C\\://WINDOWS/Fonts/arial.ttf:fontsize=20:fontcolor=white:box=1:boxcolor=black@0.4:text='"scene_score "%{metadata\:lavfi.scene_score}',split[0],drawgraph=lavfi.scene_score:min=0:max=1:s=1280x256,drawgrid=0:0:0:32,[0]vstack
一定間隔で映像に線を引く drawgrid
メタデータを映像化する drawgraph, adrawgraph
映像の変化が8割以上を出力する。
ffmpeg -i input -vf select='gte(scene\,0.8)' -an -fps_mode vfr output.mp4
ffmpeg -i input -vf select='gte(scene\,0.8)' -an -fps_mode vfr output-%03d.png
画像を1フレームに複数枚並べるにはtile
フィルタを使う。
1フレームに複数のフレームを表示する tile
3フレーム毎にシーンチェンジ検出を行いログとJPG出力する。
ffmpeg -i input -filter_complex "select='not(mod(n\,3))',select='gt(scene\,0.3)',metadata=print:file=_ffScenes.txt" -fps_mode vfr scn_%03d.jpg
ffmpeg scene detection: check only every nth frame? – Stack Overflow
最初からならlte、区間はbetween、最後までならgteを指定し、一部のシーンだけカットするならnot、一部のシーンだけ残すならnotを消す。
-vf 'select=not(between(t\,114.953\,211.882)+between(t\,21*60+44.893\,23*60+5.890)+gte(t\,24*60+0.861)),setpts=N/FRAME_RATE/TB' -af 'aselect=not(between(t\,114.953\,211.882)+between(t\,21*60+44.893\,23*60+5.890)+gte(t\,24*60+0.861)),asetpts=N/SR/TB'
(need help) remove specific time on a video : ffmpeg
条件の設定には以下の評価式を使う。
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フレームのすべてがキーフレームではない。ffprobeのkey
はこれのこと - pos
position の値。デコードしたところまでの合計ファイルサイズ。分からなければ -1 になる。showinfo
フィルタで調べられる - scen(video only)
変化率。小さい値ほど変化が小さく、大きい値ほど変化が大きいフレームだけを検出する。一般的には大きな値になるほどシーンチェンジのフレームだけになる。0 は完全一致 - concatdec_select
concat
demuxer でinpoint, outpoint, durationを使うと一部分だけ選択できるが、このオプショ
ンで指定した区間では先に選択した部分が含まれないフレームをスキップできる。これによって再エンコードするときに連結部分がうまくつながる - iw (video only)
入力横幅 - ih (video only)
入力縦幅
outputs, n の使用例
interleave
で複数区間を混ぜてffmpegで出力するときは出力オプションで入力フレームレートを-rで指定する。
偶数フレームにbm3d
フィルタを当てる。最初の0番目のフレームは偶数フレームである。
ffplay -i input -vf "select='1*mod(n-1\,2)+2*mod(n\,2)':n=2[evn][odd];[evn]bm3d=1[evn];[evn][odd]interleave"
ffmpeg -i input -vf "select='1*mod(n-1\,2)+2*mod(n\,2)':n=2[evn][odd];[evn]bm3d=1[evn];[evn][odd]interleave" -r 24000/1001 output
video – Drop every even or odd frames using FFmpeg? – Super User
偶数奇数フレームの確認方法。n-1は0248と偶数フレームだけ映像に流れてコンソールに101010が流れる。
ffplay -f lavfi -i "testsrc2=r=60000/1001,select=print(mod(n-1\,2)),fps=30/1.001,setpts=PTS-STARTPTS"
シーンチェンジの割合でフィルタを使い分ける。シーンチェンジが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]" -fps_mode vfr -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
フィルタを使うと分割毎にフィルタを使い分けられる。また一定周期でフレームを間引くならshuffleframes
フィルタの方が直感的でわかりやすい。
- trim フィルタの使い方
- 映像と音声の pts を扱う setpts, asetpts
- 【ffmpeg】動画・音声を連結する concat の使い方
- 指定フレーム毎に順番を入れ換える shuffleframes
使い分けるサンプルコマンド。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