Hori Blog

フリーランスでバックエンドエンジニアとして活動している Ryota Hori のブログです。
最近はテック系記事より雑記ブログ気味。

eyecatch image of FFmpeg と ImageMagick によるサムネイルを用いた動画のシーン検出

FFmpeg と ImageMagick によるサムネイルを用いた動画のシーン検出

FFmpeg のみで動画からシーン検出するスクリプトはちらほらと見かけるのですが、軽量でシンプルなシーン検出が欲しかったのでサムネイルと ImageMagick で整備してみました。

やりたいこと

動画の要らない部分をカットしたかったのですが、落ちていた FFmpeg のシーン検出スクリプトが古い FFmpeg に依存していたり環境が限定されたり、ちょっと扱いづらそうだったので自作しようと思ったのがきっかけです。

FFmpeg のみで試行錯誤するのも良かったのですが、別の用途で一定時間ごとのサムネイルを生成する必要があったため、サムネイルを使いまわして映像のみでのシーン検出をやってみました。

動画を元データに検出しようとするとそれなりに負荷も時間もかかりますが、サムネイル比較であれば高速で軽量なのでお得です。

準備

FFmpeg と ImageMagick を用意します。

Docker でインストールする場合は Alpine の apk で取れる FFmpeg のバージョンは古いので注意が必要です。

動作確認したのは

  • FFmpeg : 3.2.2
  • ImageMagick : 6.9.6-8, 7.0.5-0

です。 ImageMagick は apk でとれる 6.9.6-8 でも動きました。

サムネイル生成

FFmpeg によるサムネイル生成は色々とやり方があるのですが、方法によっては取得されるサムネイルの時間が不正確な場合があります。

試した中で最もズレが少なかったのが以下。

ffmpeg -i 'src.mp4' -filter:v fps=fps=1/5=down output/%04d.jpg

fps1/5 は 5 秒に 1 枚のペースで出力するという意味です。10 秒ごとなら 1/10 です。

output/ ディレクトリを出力先にしているので、事前にディレクトリは作っておきます。

シーン検出

1 つ前のサムネイルとの差分がどの程度あるかによってシーン変化を検出します。

ImageMagick には compare というコマンドがあり、差分画像を出力することができます。

Command-line Tools: Compare @ ImageMagick

使い方はこんな感じ。

compare -metric AE output/0000.jpg output/0001.jpg diff.jpg

差分画像が diff.jpg に出力されます。

src1.jpg

src2.jpg

diff-AE.jpg

赤いのが全部差分です。ほぼ差分となってしまっていますが、動画の場合はノイズが乗るので単純比較だと上手くいきませんでした。

オプションを指定することで解決できます。

compare コマンドでは -metrics オプションで差分のロジックを指定できます。

一覧はこちら。

Command-line Options @ ImageMagick

それぞれ向き不向きはあると思いますが、単純な差分抽出であれば上記サンプルで使用している、変化のあったピクセルをチェックする AE がお手軽かと思います。

なお、動画の場合はどうしてもノイズが生じるため、厳密な画像比較をするとほとんど全面が差分として出力されてしまいます。対処としては AE を使用する場合は -fuzz オプションを併用すると微量の色変化では反応しなくなるため使いやすいです。

10% で指定したのがこちら。

compare -metric AE -fuzz 10% output/0000.jpg output/0001.jpg diff.jpg

diff-AE-FUZZ10.jpg

30% で指定したものがこちら。

compare -metric AE -fuzz 30% output/0000.jpg output/0001.jpg diff.jpg

diff-AE-FUZZ30.jpg

用途によって適切な設定は異なると思いますが、ものの移動や場所の切り替えについては検出できていそうな画像が出力されていることが分かるかと思います。

試しに同じ動画内でシーンが切り替わった画像を 10% で比較してみると

src3.jpg

diff-AE-FUZZ10-changed.jpg

このように大きく変化していることがわかります。

変化率の抽出

差分画像が出力されるので画像から解析しても良いのですが、 compare コマンドでは差分が生じる場合には標準エラー出力に差分を示す数値が出力されます。

AE の場合は変化したピクセル数が出力されるので、全体の解像度と比較してどのくらいの割合が変化したかを算出することができます。

先ほどの -fuzz 30% 指定の場合では

$ compare -metric AE -fuzz 30% output/0000.jpg output/0001.jpg /dev/null
18743

サムネイルサイズが 1280x720 だったので

18743 / (1280*720) = 0.020337457

で 2% くらいの変化率ですね。

このサムネイルの変化率が設定した閾値を超えた場合にシーン変更として扱うことでシーン検出を実現できるかと思います。

以上

ImageMagick も FFmpeg もオプションが豊富すぎてとっつきにくい印象がどうしてもありますが、コマンドで一括処理できるのが非常に捗るので熟達していきたいものです。