web-dev-qa-db-ja.com

FFmpegを使用したビデオ用の意味のあるサムネイル

FFmpegは、ビデオを表すためのサムネイルとして使用できるビデオから画像をキャプチャできます。その最も一般的な方法は、 FFmpeg Wiki にあります。

しかし、私はある間隔でランダムなフレームを選びたくはありません。シーンの変化を捉えるためにFFmpegでフィルタを使用するオプションがいくつかあります。

フィルタthumbnailは、ビデオ内で最も代表的なフレームを見つけようとします。

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

次のコマンドは、前のシーンと比較して40%を超える変化があるフレームだけを選択し(したがってシーンの変化もおそらく)、5つのPNGのシーケンスを生成します。

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

上記のコマンドの情報クレジットは Fabio Sonnati です。 2番目の画像は、n枚の画像を取得して最高の画像を選択できるので、よさそうです。私はそれを試してみて、それは同じ画像を5回生成しました。

さらに調査を重ねた結果、次のことがわかりました。

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrはあなたが異なる画像を取得することを保証します。これは常にビデオの最初のフレームを選択します。ほとんどの場合、最初のフレームはクレジット/ロゴであり意味がありません。そのため、ビデオの最初の3秒を破棄するために-ss 3を追加しました。

私の最後のコマンドはこのようになります:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

これは私ができる最善でした。私は5つのビデオしか選んでいないので、それらはすべてビデオの最初からのもので、ビデオの後半で発生する重要なシーンを見逃してしまう可能性があることに気付きました。

私は他のよりよい選択のためにあなたの頭脳を選びたいと思います。

76
d33pika

理想的には、タイムスパンがビデオの1、2、3、4、および5th 20%である5つのタイムスパンのそれぞれの中で最初の> 40%変化するフレームを探してみてください。

また、クレジットを避けるために6つの期間に分割し、1番目の期間を無視することもできます。

実際には、これはシーンチェンジチェックとビデオの最初のビットを捨てるというあなたの主張を適用しながらfpsを低い数値に設定することを意味します。

...何かのようなもの:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
26
A.M.

意味のあるを定義するのは難しいですが、ビデオファイル全体にN個のサムネールを効率的に作成したい場合は、ユーザーがアップロードしたコンテンツで制作時にサムネールを生成します。

疑似コード

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

どこで:

  • D - ビデオ期間はffmpeg -i <movie>単独またはffprobeから読み取られます。これにはNice JSON出力ライターがあります。
  • N - あなたが望むサムネイルの総数
  • X - 1からNまでのサムネイル番号
  • T - つまずきの時期

単に上記は、映画の各区画の中央のキーフレームを書き留めます。例えば。映画の長さが300秒で、3枚のサムネイルが必要な場合は、50秒、150秒、250秒後に1キーフレームかかります。 5枚のサムネイルの場合は、30秒、90秒、150秒、210秒、270秒になります。ムービーの長さDに応じてNを調整できます。 5分の映画には3つのサムネイルがありますが、1時間に20のサムネイルがあります。

パフォーマンス

上記のffmpegコマンドを呼び出すたびに、1GB H.264の場合、1秒の何分の一(!)かかります。それは即座に<time>の位置にジャンプし(-ssの前に-iを気にする)、実質的に完全なJPEGである最初のキーフレームをとるためです。正確な時間的位置に合わせてムービーをレンダリングするのに浪費する時間はありません。

後処理

上記のscaleや他のサイズ変更方法を混在させることができます。単色のフレームを削除したり、thumbnailのような他のフィルタと混ぜることもできます。

7
gertas

私はかつて似たようなことをしましたが、私はビデオのすべてのフレームを(1 fpsで)エクスポートし、それらを画像間の差を計算するPerlユーティリティと比較しました。各フレームを前のサムネイルと比較し、それがすべてのサムネイルと異なる場合は、それをサムネイルコレクションに追加しました。ここでの利点は、ビデオがシーンAからBに移動し、それらがAに戻った場合、ffmpegは2フレームのAをエクスポートすることです。

2
Eran Ben-Natan

これを試して

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

このコマンドを使用して、ビデオ全体を代表する必要な数のサムネイルを生成できます。

2
LostPuppy

これは私がポスターとして使用するためにライブm3u8ストリームのための定期的なサムネイルを生成するためにすることです。サムネイルを生成するためだけに継続的なffmpegタスクを実行すると、すべてのCPUが消費されるので、代わりに60秒ごとにcronjobを実行してストリームのすべてのサムネイルを生成します。

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

60秒よりも頻繁に実行する必要がある場合(cronjobの制限)、スクリプト内でwhileループを実行すると永久に実行されます。頻度を30秒に変更するには、sleep 30を追加するだけです。ただし、前回の実行は次の動画の開始前に完了しない可能性があるため、多数の動画でこれを実行することはお勧めしません。

理想的にはcronjobを使用して、5分ごとに実行します。

1
chovy