ffmpegを使用してフレームを抽出する最速の方法は?


118

こんにちはffmpegを使用してビデオからフレームを抽出する必要があります。これよりも高速な方法はありますか。

ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg



1
^更新されたURL:trac.ffmpeg.org/wiki/Seeking
Lambart

5
CPUサイクルに余裕がある場合は、複数の動画から並行して抽出できます:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange

回答:


155

JPEGエンコードステップのパフォーマンスが高すぎる場合は、常にフレームを非圧縮でBMP画像として保存できます。

ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp

これには、JPEGへのトランスコーディングによる量子化により、品質の低下が発生しないという利点もあります。(PNGもロスレスですが、エンコードにJPEGよりはるかに時間がかかる傾向があります。)


9
これにより、私のマシンで大量のフレームがドロップします。ffmpegにすべてをレンダリングするように指示できますか?
Evi1M4chine 16

45
@ Evi1M4chineは-rパラメータを削除するだけですべてのフレームが抽出されます
studioj

14
JPEGはCPUでそれほど難しくありませんが、圧縮されていないビットマップはストレージで非常に難しいため、JPEGに比べてBMPの方がスループットが高くなるとは思いません。
マーカス・ミュラー

4
すべてのフレームを抽出するには:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf 2017

10
あなたはffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png、そうですか?(欠落していた-i
ジョシュア

68

この質問に出くわしたので、ここで簡単に比較します。これらの2つの異なる方法を比較して、38分07秒の動画から1分あたり1フレームを抽出します。

time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp

1分36.029秒

ffmpegがビデオファイル全体を解析して目的のフレームを取得するため、これには時間がかかります。

time for i in {0..39} ; do ffmpeg -accurate_seek -ss `echo $i*60.0 | bc` -i input.mp4   -frames:v 1 period_down_$i.bmp ; done

0分4.689秒

これは約20倍高速です。高速シークを使用して目的の時間インデックスに移動し、フレームを抽出してから、時間インデックスごとにffmpegを数回呼び出します。注-accurate_seek デフォルトであり 、そしてあなたが追加します-ss入力された映像の前に-iオプション。

後者は不正確になる可能性が-filter:v -fps=fps=...ある-rため代わりに使用することをお勧めします。固定としてチケットがマークされて、私はまだいくつかの問題が発生するので、より良い、それは安全なプレーでした。


6
2016年2月:ffmpeg 2.1以降、正確なシークオプションがデフォルトになりました-trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bcはUbuntuのネイティブパッケージではなく、bashを使用できますlet "i = $i * 60"。ところで-優れたアイデア
ギラドマヤニ

3
-ss前に追加する良いヒント-i。そうでない場合、ビデオ全体がデコードされ、不要なフレームは破棄されます
MoustafaAAtta

2
これはグーグルのトップなので、2018年もこれは配当を生み出すアプローチであることに注意したいと思います。最良の結果ffmpegは、ホストのコアごとに1 つ実行されているようです-(bmpの場合)(ディスクのような他のボトルネックに達するまで)速度がほぼ線形に向上します。
Knetic

質問は「最速の道」についてであるので、これは受け入れられた答えであるべきです。明らかに、他のメソッドに比べてまだ速いと思うffprobeを使用した「forループ」の終了変数を知る必要があります。
iamprem

9

抽出するフレームが正確にわかっている場合(1、200、400、600、800、1000など)、次を使用してみてください:

select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)' \
       -vsync vfr -q:v 2

これをImagemagickのモンタージュへのパイプと共に使用して、任意のビデオから10フレームのプレビューを取得します。明らかに、あなたが使用して把握する必要があるフレーム番号ffprobe

ffmpeg -i myVideo.mov -vf \
    select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)',scale=320:-1 \
    -vsync vfr -q:v 2 -f image2pipe -vcodec ppm - \
  | montage -tile x1 -geometry "1x1+0+0<" -quality 100 -frame 1 - output.png

少し説明:

  1. ffmpegでは、式+はORおよび*ANDを表します
  2. \,単に,キャラクターを脱出している
  3. -vsync vfr -q:v 2それなしではうまくいかないようですが、理由はわかりません-誰か?

4

私はそれを試してみました。32秒で3600フレーム。あなたの方法は本当に遅いです。これを試してみてください。

ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg

ffmpeg -i "input URL" -vf fps=1/5 out%d.png 入力URLがhttpsリンクでなければならない場所を試し ています。
x2212

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela

1

私の場合、少なくとも毎秒フレームが必要です。上記の「シーク」アプローチを使用しましたが、タスクを並列化できるかどうか疑問に思いました。ここではFIFOアプローチでNプロセスを使用しました:https : //unix.stackexchange.com/questions/103920/parallelize-a-bash-for-loop/216475#216475

open_sem(){
  mkfifo /tmp/pipe-$$
  exec 3<>/tmp/pipe-$$
  rm /tmp/pipe-$$
  local i=$1
  for((;i>0;i--)); do
    printf %s 000 >&3
  done
}
run_with_lock(){
    local x
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
    "$@" 
    printf '%.3d' $? >&3
    )&
}
N=16
open_sem $N
time for i in {0..39} ; do run_with_lock ffmpeg -ss `echo $i` -i /tmp/input/GOPR1456.MP4  -frames:v 1 /tmp/output/period_down_$i.jpg  & done

基本的に私は&でプロセスをフォークしましたが、同時スレッド数をNに制限しました。

これにより、私の場合、「シークトゥ」アプローチが26秒から16秒に改善されました。唯一の問題は、stdoutがフラッディングされるため、メインスレッドが端末に正常に終了しないことです。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.