web-dev-qa-db-ja.com

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

こんにちは、ffmpegを使用して動画からフレームを抽出する必要があります。これよりも速い方法はありますか。

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

89
Stpn

JPEGエンコードステップのパフォーマンスが過度に集中している場合、フレームを常に非圧縮でBMPイメージとして保存できます。

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

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

113
Multimedia Mike

この質問に出くわしたので、簡単に比較してみましょう。 38m07sの長さのビデオから毎分1フレームを抽出するこれら2つの異なる方法を比較します。

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がdefault であり、入力ビデオ-ssオプションの前に必ず-iを追加してください。

-filter:v -fps=fps=...の代わりに-rを使用することをお勧めします。 後者は不正確かもしれません。fixed 、まだいくつかの問題が発生したので、安全にプレイする方が良い。

49
blutorange

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\,100)',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がなければ動作しないようですが、その理由はわかりません-誰か?
6
Voy

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

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

0
Kübra

私の場合、少なくとも毎秒フレームが必要です。上記の「シーク先」アプローチを使用しましたが、タスクを並列化できるかどうか疑問に思いました。ここで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がフラッディングされるため、メインスレッドが端末に正常に戻らないことです。

0
Tycon