![FFmpegを使用してN秒ごとにフレームを正確に抽出する](https://rvso.com/image/1672321/FFmpeg%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6N%E7%A7%92%E3%81%94%E3%81%A8%E3%81%AB%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%82%92%E6%AD%A3%E7%A2%BA%E3%81%AB%E6%8A%BD%E5%87%BA%E3%81%99%E3%82%8B.png)
使用法
私は を使用してビデオから画像を抽出しますffmpeg
。
私は 10 秒ごとに 1 つの縮小された画像をダンプし、 でモンタージュに組み合わせますimagemagick
。これらのモンタージュは、Web ベースのビデオ プレーヤーでスクラバーをホバーしたときにビデオのプレビューを表示するために使用されます。(モンタージュ内のどの画像を表示するかを計算します)。
コマンド
いろいろ試してみた結果、品質よりも速度を重視する以下のコマンドに落ち着きました。
ffmpeg \
-loglevel error \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$video_file" \
-r 0.1 \
-filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12" \
-color_range 2 \
f%09d.jpg
これはうまく機能しているようです。ショットは時々 ± 0.5 - 1 秒ほどずれますが、許容範囲内です。
問題
問題は、ffmpeg
ビデオの開始時に余分な画像が 1 つ生成されることです。たとえば、ファイルは次のようになります。
file time
f000000001.jpg 00:00:00
f000000002.jpg 00:00:00
f000000003.jpg 00:00:10
f000000004.jpg 00:00:20
f000000005.jpg 00:00:30
...
場合によっては、1 番目と 2 番目が数ミリ秒ずれることがあります。
私の知る限りでは(今のところ)、最初の画像を削除して残りの作業を続行するだけで済みますが、なぜこれが起こるのか、バグなのか、それとも何か他の原因なのかはわかりません。
言い換えれば、最初の2フレームの「効果」が信頼性のあるffmpeg
他のバージョンでも削除できるようにします。
指定した時間にビデオから10秒間のスナップショットを表示するために画像を使用するので10秒ずれている最初に生成された画像を削除しない場合。何らかの理由で削除する必要がある場合はない最初に重複したもの、他のバージョンなどを作成し、最初の画像を削除すると、同じ問題が発生します。
モンタージュ
(興味深いことに、モンタージュは次のように作成されます):
montage -tile 5x -geometry +0+0 -background none [file1 - file50 ] montage01.jpg
montage -tile 5x -geometry +0+0 -background none [file51 - file100] montage02.jpg
...
回答に基づいて現在使用しているコマンド(シェル):
# Set on call or global:
file_in=sample.mp4
pix_fmt=yuvj420p
sec_snap_interval=10
nr_start=1
pfx_out=snap
ffmpeg \
-loglevel warning \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$file_in" \
-pix_fmt "$pix_fmt" \
-filter:v "
scale_cuda=
w = -1 :
h = 100,
thumbnail_cuda = 2,
hwdownload,
format = nv12,
select = 'bitor(
gte(t - prev_selected_t, $sec_snap_interval),
isnan(prev_selected_t)
)'
" \
-vsync passthrough \
-color_range 2 \
-start_number "$nr_start" \
"$pfx_out%09d.jpg"
答え1
を使用すると、-r 0.1
出力フレームレートが 0.1Hz に設定されますが、入力ビデオから正確に 10 秒ごとにフレームが取得されることは保証されません (理由はわかりません)。
これを解決する一つの方法は、フィルターを選択。
例 (GPU アクセラレーションなし):
ffmpeg -i input.mp4 -vf "select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" -vsync 0 f%09d.jpg
bitor(gte(t-prev_selected_t\,10)
1
は、「渡された」タイムスタンプの差が 10 秒以上の場合です。
式が と評価されると1
、フレームが「選択」され、出力に渡されます。bitor
はisnan(prev_selected_t)
最初のフレームを通過しますが、ここでprev_selected_t
はNaN
(値はありません)。-vsync 0
「パススルー」を適用します - 各フレームはタイムスタンプとともにデマルチプレクサーからマルチプレクサーに渡されます。
scale_cuda
以下はとの例ですthumbnail_cuda
。
ffmpeg \
-loglevel error \
-hwaccel cuvid \
-hwaccel_output_format cuda \
-c:v h264_cuvid \
-i "$video_file" \
-filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12,select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" \
-vsync 0 \
-color_range 2 \
f%09d.jpg
- フィルターの使用により、フィルターを最後に
thumbnail_cuda
配置する必要があります。select
テスト:
10fps のフレーム カウンターを使用して合成ビデオを作成します。
ffmpeg -y -f lavfi -r 10 -i testsrc=size=128x72:rate=1:duration=1000 -vf setpts=N/10/TB -vcodec libx264 -pix_fmt yuv420p input.mp4
上記のコマンドを実行した後の出力フレーム:
ご覧のとおり、選択されたフレームはちょうど 10 秒ごとです。