Netflixが使っているエンコードされる前と後の動画の品質評価に使われている VMAF(Video Multimethod Assessment Fusion)スコアをffmpegで調べる。以前の記事に異なる解像度でも調べられると書いていたが間違いである。同じ解像度でないと調べられない。対応サブサンプリングは4:2:0、4:2:2、4:4:4になり、エンコード前後でピクセルフォーマットが異なるときは2入力目の参照元のファイルのピクセルフォーマットに変換される。外部ライブラリフィルタなので別途インストールが必要で--enable-libvmafを付ける。--enable-version3つけなくてもよくなった。vmaf v2.0.0以降をリンクしたffmpegではn_threadsでスレッド数を指定しないと処理速度が上がらない。

Dynamic optimizer — a perceptual video encoding optimization framework | by Netflix Technology Blog | Netflix TechBlogより。

3. It relies on existing image quality metrics (VIF, DLM), properly modified to cover multiple scales of resolution, as well as the amount of motion between consecutive video frames in a video sequence as features that are input in a machine-learned set of weights. The final score is the result of combining these elementary features in a support vector machine (SVM) regressor.
3. VMAFは、既存の画質評価指標(VIF、DLM)を、複数の解像度に対応するように適切に修正したものと、連続するフレーム間の動きの量を、機械学習した重みをセットに入力する特徴を利用している。最終的なスコアは、これらの初歩的な特徴をサポートベクターマシン(SVM)の回帰分析と組み合わせた結果になる。

2022年1月24日のコミットでlibvmaf v2.0.0に対応しオプション指定を刷新した。`compute_vmaf()` is deprecated and will be removedのエラーは出なくなり、複数モデルのスコアを同時に調べられるようになっている。

基本コマンド

libvmaf v2.0.0以前のオプション指定になる。このオプションで指定するにはffmpeg 5.0以下を使う。ffmpeg 5.0より先の指定方法

VMAFスコアを調べるのファイルを最初に入力し、圧縮前の元の動画を2番目に入力する。
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4 -an -f null -

コンテナフォーマットが異なる、例えばMP4とMKVなどを比較する。
ffmpeg -i encoded.mp4 -i original.mkv -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

最終的な品質評価になる VMAFスコアは 100 が最高画質。配信向けには 80 程度を目標とするビットレートになるようにする(下の運用方法を参照)。ログファイルに書いてある adm2, vif_scalex スコアは VMAFスコアの元となり 0(最低画質)から 1(最高画質)。motion2 は 0(動かない映像)から 20(ものすごく動く映像)。

解像度が同じでオプションを明記する場合。
ffmpeg -i encoded.mp4 -i original.mp4 -filter_complex "libvmaf=model_path=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:enable_transform=0:phone_model=0:psnr=0:ssim=0:ms_ssim=0:pool=mean:n_threads=4" -an -f null -
ffmpeg -i encoded.mp4 -i original.mp4 -filter_complex "libvmaf=vmaf_v0.6.1.json:log.xml:xml:0:0:0:0:0:mean:4" -an -f null -

yuvの動画を比較する場合。動画はMP4などの圧縮した形式と、展開したyuvの形式とでは処理速度に大きな変化は見られなかった。yuv展開したファイルはデコード由来のスコア計算問題の解決につながるかもしれない。
ffmpeg -video_size 1280x720 -pixel_format yuv420p -framerate 30 -i encoded.yuv -video_size 1280x720 -pixel_format yuv420p -framerate 30 -i original.yuv -filter_complex libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4 -an -f null -

yuvに展開するよりもy4mに展開したほうがフレームレートや解像度のオプションを指定する必要がなく手軽。
ffmpeg -i encoded.y4m -i original.y4m -filter_complex libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4 -an -f null -

エンコードが終わるとコンソールの最後に以下のような結果が表示される。ログファイルで出力する場合はフレーム毎に指定オプション内容が出力される。Exec FPS は処理速度。

Exec FPS: 4.940972
VMAF score = 96.147066

解像度が異なる場合

解像度が異なる VMAFスコアを調べるには、調べたい動画をフィルタでオリジナルの解像度にリサイズしてから調べる。

リサイズする前が 1920×1080、リサイズした後が 1280×720 で、scaleフィルタを使う。VMAFスコアを調べるのに縮小リサイズしたときのアルゴリズムと一致させなくてもよい。
ffmpeg -i original-1080p.mp4 -filter_complex scale=1280:720:flags=spline -c:a copy resized-720p.mp4
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "scale=1920:1080:flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

scale2refフィルタを使うと解像度指定しなくても揃えられる。
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v][1:v]scale2ref=flags=bicubic[0v][1v];[0v][1v]libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

PTSも揃える。
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]scale2ref=flags=bicubic[0v][1v];[0v][1v]libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

比較する動画の解像度に合わせる scale2ref

ts_sync_mode2022年8月10日に追加された。track #9689。このオプションが使えるようになっていれば異なるコンテナでPTSを合わせる処理が不要になり、IO処理が速くなる。

解像度については以下の(Computing VMAF at the Right Resolution、Picking An Upsampling Algorithm、Interpreting VMAF Score When Resolution Is Not 1080p)を参照。

VMAF: The Journey Continues – Netflix TechBlog – Medium

要約すると、vmaf_v0.6.1の学習モデルは1080pを使いそれ以外の解像度を参照するとVMAFスコアが一層高くなり。またエンコード前の映像が1080p未満でさらに縮小リサイズしたときも同様にVMAFスコアが高くなる。

元の映像が1080pではないときはscaleフィルタで1080pに変更することでモデルに合わせた評価になる。

ffmpeg -i resized-576p.mp4 -i original-720p.mp4 -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS,scale=-2:1080:flags=bicubic[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

動画時間が異なる場合

2ファイル入力するフィルタの挙動設定 framesyncに対応しているのでエンコード後の動画がオリジナルよりも後の時間が短い場合はオプションにshortest=1:repeatlast=0を追加すると短い方で終了する。

ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4:shortest=1:repeatlast=0" -an -f null -

動画時間よりも先に出力を停止するなら出力オプションの-tを使う。コマンドは100秒で出力を止める例。任意のフレーム数なら-vframes, -frames:vを指定する。
ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -t 100 -f null -

中断して途中からやり直す場合

PCを終了させるなど処理を中断したときに途中からやり直す方法。フレームぴったりに開始するにはtrimフィルタを使って中断したフレームまでデコードし直してから再開する。開始フレーム位置は正常終了させて出力したvmafのログファイルを見て、Frameの最後の行と同じ値を指定する。すると再開後の最初のフレームが中断前と一致するのでこの行だけ削除して以降のフレームを残す。Frameの値がリセットされるので表計算ソフトなどで振り直す。

ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex "[0:v]trim=start_frame=50246,setpts=PTS-STARTPTS,settb=1/AVTB[0v];[1:v]trim=start_frame=50246,setpts=PTS-STARTPTS,settb=1/AVTB[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4" -an -f null -

trim フィルタの使い方

エンコードと並列してvmafフィルタも使う場合

teeでファイル出力と標準出力に分割して、改めてエンコードしているファイルを標準入力する。Thread message queue blocking; consider raising the thread_queue_size option (current value: 8)のエラーが出るのでnut入力の前に-thread_queue_sizeを指定している。

ffmpeg -i input -map 0 -c:v libx264 -an -flags +global_header -f tee "output.mp4|[f=nut]pipe:" | ffmpeg -thread_queue_size 1024 -i pipe: -i input -filter_complex "[0:v]settb=1/AVTB,setpts=PTS-STARTPTS[0v];[1:v]settb=1/AVTB,setpts=PTS-STARTPTS[1v];[0v][1v]scale2ref=flags=bicubic,libvmaf=vmaf_v0.6.1.json:log_path=log.xml:log_fmt=xml:n_threads=4:n_subsample=1:phone_model=0:psnr=0:ssim=0:ms_ssim=0:shortest=1:repeatlast=0" -an -f null -

リサイズアルゴリズムの注意点

-sでのリサイズと、-vfscaleフィルタを使い-flagsを指定しない場合はbicubicが使われるが、-filter_complexscaleフィルタを使い-flagsを指定しない場合はbilinearが使われて、-vf, -filter_complexで使われるアルゴリズムが異なるのに注意する。この仕様は2021年8月5日のコミットで直り無指定ではbicubicが使われるようになった。拡大リサイズするときにリサイズ品質が高いほどよいとは限らないとされているので一般的なbicubicが勧められている。

(Picking An Upsampling Algorithm)を参照
VMAF: The Journey Continues – Netflix TechBlog – Medium

リサイズする scale
Zライブラリを使ったリサイズフィルタ zscale

モデル名の変更

2020年11月22日からモデルの名前が変わっている。
vmaf/model at master · Netflix/vmaf
Feature/rename integer models by li-zhi · Pull Request #722 · Netflix/vmaf

v2.0.0よりfloatモデルへ

モデル名の変更に続いてfloatモデルが公開されたがffmpegはまだ対応していないので使えない。さらに処理スレッドのn_threadsが自動設定ではなくなったので適宜スレッド数を指定する。

ffmpeg support for v2.0 API and Homebrew · Issue #753 · Netflix/vmaf

gyan FFmpegBtbN FFmpegともにffmpeg 4.4時点ではfloatモデルでも`compute_vmaf()` is deprecated and will be removedのエラーはでるがvmaf計算自体はできるようになっていた。floatモデルは精度は高いが処理は遅く、通常のモデルは精度は低くくなるが処理は速い。

vmafスコアが異常に低い場合

ログを見てフレーム途中のスコアが0になったりするのは映像同士のフレームが一致していないか、デコード由来によるもの。元の映像がインターレースで圧縮後のファイルがプログレッシブのときは元の映像をプログレッシブに変換しなければならない。デコード由来のものはy4mに展開したファイルを試す。ファイルサイズが膨大になるので冒頭からスコア0になる付近までの出力で一度試す。コマンド例は-t 10で冒頭から10秒間出力している。

ffmpeg -i input -t 10 output.y4m

ffmpeg でインターレース解除
Unexplained frame values with AV1 · Issue #951 · Netflix/vmaf

公式ドキュメント:FFmpeg Filters Documentation : libvmaf

オプション(ffmpeg 5.0以前)

調べる内容を増やすほど処理速度が遅くなる。

  • model_path[string]
    SVM(Support Vector Machines)で使われるモデルのパス指定。配布バイナリのffmpegを使う場合にはvmaf_v0.6.1.jsonをffmpegと同じ場所に置いておくのが楽。2021年以降のバージョンは.pklの代わりに.jsonを読み込む。4Kモデルのvmaf_4k_v0.6.1は4Kテレビで画面の高さの1.5倍の距離から見たときの主観的な品質を予測し、4K映像をリサイズせずにエンコードしたのを評価するときに使う。参照。NEG(No Enhancement Gain)モデルは純粋に圧縮利得を評価するのに使い、通常のモデルはフィルタや特定のコーデックでエンコードしたのを含めて評価する。floatモデルは精度は高いが処理は遅く、通常のモデルは精度は低くいが処理は速い。NEGモデルについての参照。パス指定について参照
    既定値:”vmaf_v0.6.1.json”(インストールされているパス)
    Windowsの指定例:model_path=”D\\:/mypath/vmaf_v0.6.1.json”
  • log_path[string]
    出力されるログファイルのパス指定。拡張子はlog_fmtと同じのを指定する
    既定値:無指定(指定するとフレーム毎のログファイルが出力される)
  • log_fmt[string]
    ログファイルのフォーマット指定。csvjsonxmlが指定できる
    既定値:xml
  • enable_transform[boolean]
    VMAFスコアの計算で transform を有効にする
    同一ファイルの VMAFスコアが 100 になる。有効にしないと 100 にならない
    既定値:0
  • phone_model[boolean]
    電話モデルを呼び出してノートPCやTVに適した通常より高い VMAFスコアを計算する。携帯電話などのモバイル端末は画面サイズが大きくなく視聴距離が近いためにビットレートを高くしてもそれほど高画質に見えないからである。モバイル向けのSD解像度に使うとよいかもしれない。適宜視聴環境に合わせて有効にする。詳細は ここを参照
    既定値:0(ノートPC、TV向け)
  • psnr[boolean]
    psnr も一緒に計算する。60 が最高画質
    既定値:0
  • ssim[boolean]
    ssim も一緒に計算する。1 が最高画質
    既定値:0
  • ms_ssim[boolean]
    ms_ssim も一緒に計算する。1 が最高画質
    既定値:0
  • pool[string]
    VMAFスコアの計算方法。mean、min、harmonic_mean(調和平均)が指定できる
    既定値:”mean”
  • n_threads[int]
    VMAFスコアを計算するのに使うスレッド数の指定
    既定値:0(自動設定、v2.0.0以降は手動指定
    範囲:0 から UINT32_MAX まで
  • n_subsample[int]
    VMAFスコアを計算するのに使うフレームサブサンプリング間隔の指定。1より大きな値はそれだけ解析を間引くので処理速度が速くなるが正確さとのトレードオフになる。ログファイルのFrameは元のフレーム順になっているので間引く前後のフレーム位置で迷うことはない
    既定値:1
    範囲:1 から UINT32_MAX まで
  • enable_conf_interval[boolean]
    信頼区間を有効にする
    既定値:0

enable_transformの挙動について

ffmpeg -f lavfi -i color=s=32x32:d=1 -filter_complex setrange=tv,split,libvmaf=vmaf_v0.6.1.json:enable_transform=0 -an -f null -
Exec FPS: 694.752857
VMAF score: 96.616337
ffmpeg -f lavfi -i color=s=32x32:d=1 -filter_complex setrange=tv,split,libvmaf=vmaf_v0.6.1.json:enable_transform=1 -an -f null -
Exec FPS: 574.159669
VMAF score = 100.000000

オプション(ffmpeg 5.0より先)

モデルをffmpegに組み込めるのでモデルを別途用意しなくてもよくなった。vmaf/libvmaf/meson_options.txt at master · Netflix/vmaf · GitHub

  • model:モデルパラメータの指定。:の前に\\のエスケープが必要
    • path[string]
      ffmpegと同じ場所なら無指定でよい
    • version[string]
      バージョンの指定。nameと内容をそろえる
      既定値:vmaf_v0.6.1
    • name[string]
      モデル名の指定。versionと内容をそろえる
    • enable_transform[boolean]
      既定値:0
    • phone_model[boolean]
      enable_transformと同じ
      既定値:0
    • enable_conf_inter[boolean]
      既定値:0

versionとnameの組み合わせ。

version name
vmaf_v0.6.1 vmaf
vmaf_v0.6.1neg vmaf_neg
vmaf_float_v0.6.1 vmaf_float
vmaf_float_v0.6.1neg vmaf_float_neg
vmaf_4k_v0.6.1 vmaf_4k
vmaf_float_4k_v0.6.1 vmaf_float_4k
  • feature:vmaf以外の追加メトリクスの指定。feature=name=foo形式で複数指定はname=foo|name=barで指定。features.md
    • psnr
    • psnr_hvs
    • cambi:バンディングアーチファクトを調べる指標。0がバンディングなし、5あたりから目立ち始め、24は見るに堪えない。cambi.md
    • ciede:CIEDE2000のことで、色味の評価。かなり処理速度が落ちる
    • float_ssim
    • float_ms_ssim:計算はYチャンネルだけ
  • log_path[string]
    log_fmtに合わせてパスとファイル名を指定する
  • log_fmt[string]
    csvjsonxmlsubが指定できる
    既定値:xml
  • pool[string]
    meanminharmonic_meanが指定できる
    既定値:mean
  • n_threads[int]
    スコアを計算するのに使うスレッド数の指定
    既定値:0
    範囲:0からUINT32_MAXまで
  • n_subsample[int]
    スコアを計算するフレーム間隔の指定
    既定値:1
    範囲:1からUINT32_MAXまで

cambiのオプション

挙動がよくわかっていない。

  • window_size:
    Window size to compute CAMBI (default: 63 corresponds to ~1 degree at 4K resolution and 1.5H)
    既定値:63
    範囲:15から127
  • topk:
    Ratio of pixels for the spatial pooling computation
    既定値:0.6
    範囲:0から1.0
  • tvi_threshold:
    Visibilty threshold for luminance ΔL < tvi_threshold*L_mean for BT.1886 既定値:0.019 範囲:0.0001から1.0
  • max_log_contrast:
    Maximum contrast in log luma level (2^max_log_contrast) at 10-bits. Default 2 is equivalent to 4 luma levels at 10-bit and 1 luma level at 8-bit. The default is recommended for banding artifacts coming from video compression.
    既定値:2
    範囲:0から5
  • full_ref:
    optional flag (default: false) to run CAMBI as a full-reference metric, outputting the per-frame difference between the encoded and source images as well as the existing no-reference score.
    既定値:0
  • enc_width:
    Encoding/processing resolution to compute the banding score, useful in cases where scaling was applied to the input prior to the computation of metrics
  • enc_height
  • src_width:
    Encoding/processing resolution to compute the banding score on the reference image, only used if full_ref=true.
  • src_height

テストソースでの実行例。
ffmpeg -f lavfi -i testsrc2=d=1:s=1920x1080 -filter_complex "setrange=tv,split[1],scale=1280:720[0v];[0v][1]scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg|version=vmaf_float_v0.6.1\\:name=vmaf_float|version=vmaf_float_v0.6.1neg\\:name=vmaf_float_neg\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

通常のvmaf_v0.6.1モデルだけ使う。
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

vmaf_v0.6.1negモデルも併用する。
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

vmaf_float_v0.6.1、vmaf_float_v0.6.1negモデルも併用する。
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg|version=vmaf_float_v0.6.1\\:name=vmaf_float|version=vmaf_float_v0.6.1neg\\:name=vmaf_float_neg\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

mp4とmkvを比較するときにts_sync_mode=1を使う。
ffmpeg -i 調べたい動画.mp4 -i 元の動画.mkv -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0:ts_sync_mode=1" -an -f null -

エンコードと同時に並列してvmafフィルタも使う。
ffmpeg -i 元の動画.mp4 -map 0 -c:v libx264 -an -flags +global_header -f tee "output.mp4|[f=nut]pipe:" | ffmpeg -thread_queue_size 1024 -i pipe: -i 元の動画.mp4 -filter_complex "libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

FFmpeg@a9193f7のコミットからエンコードと同時にvmafを計算することもできるようになったが、動画ファイルは出力しない。
ffmpeg -i input -map 0:v:0 -c:v libx264 -crf 18 -f null - -dec 0:0 -filter_complex [0:v][dec:0]libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0:ts_sync_mode=1[vmaf] -map [vmaf] -f nut - > nul 2>&1

AV1ファイルにdav1dデコーダを使い入力オプションに-export_side_dataをつけるとfilm grainを無効化してエンコード結果それ自体を比較する。film grainを使っていなくてもオプションの有無に影響はない。
ffmpeg -export_side_data film_grain -i av1.mp4 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf|version=vmaf_v0.6.1neg\\:name=vmaf_neg\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

VMAFの解説記事

処理を高速化する

オプションを使う

  • n_threadsの数値を高くして処理するスレッド数を増やし高速化する。必要以上に数値を高くしても高速化しない。
  • n_subsampleの数値を高くして一定間隔のフレームだけを処理する。

cropフィルタを使う

cropフィルタを使って中央から一定割合を切り取り処理する。切り取る割合が小さくなるほど不正確になるが、それだけ処理するピクセル数が減るので高速化する。以下のコマンドは同じ解像度のときなので異なる解像度のときは先にscaleフィルタなどで解像度をそろえる。

ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "[0:v]crop=1280:720[0v];[1:v]crop=1280:720[1v];[0v][1v]libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "[0:v]crop=iw*0.7:ih*0.7[0v];[1:v]crop=iw*0.7:ih*0.7[1v];[0v][1v]libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

HW支援で処理を高速化する

ffmpegにlibvmaf_cudaが実装されたが、Windows環境では今のところバイナリをつくるのが困難。

運用方法

既定値では算術平均(pool=mean)が使われるが、低い値をより考慮するなら調和平均(pool=harmonic_mean)がお薦め。
調和平均 – Wikipedia

Download Handout: Fine-Tuning Adaptive Group With Objective Quality Metrics – Streaming Learning Centerから
http://streaminglearningcenter.com/wp-content/uploads/2018/11/Objective_Quality_Metrics_2018.pdf より、スコアが93あれば2DとVR用の映像では元映像と区別が付かない、または目立っても気になるほどではないとされる。VRについてはP43のまとめで言及している。

引用元PDF:https://www.realnetworks.com/sites/default/files/vmaf_reproducibility_ieee.pdf

The results indicate that if a video service operator were to encode video to achieve a VMAF score of about 93 then they would be confident of optimally serving the vast majority of their audience with content that is either indistinguishable from original or with noticeable but not annoying distortion

しかし、実際にエンコードしてスコアを見てみると特にアニメでは95でも気になるシーンがあるので平均96以上は個人的には欲しい。さらに下から1%位置のスコアが89以上も指標に上げられている。

Creating the Perfect Bitrate Ladder for Video Encoding – OTTVerse

a) The harmonic mean score was 95 or higher and
b) The 99 percentile score was 89 or higher

ビットレートを少しづつ下げつつ、解像度も下げたときに高解像度と低解像度のスコアが逆転する低解像度のビットレートを最適値とする方法。ただしこの方法をとると一番大きい解像度のビットレートが決められない。また実写とアニメ映像とでは必要ビットレートも変わってくる。

netflix_vmaf_approach

Tying Metrics to Predicted Subjective Ratings
MOS PSNR SSIM SSIMplus VMAF
Scoring 1-5 0-100 0-1 0-100 0-100
No artifact threshold NA 45dB 0.99 100 93
Artifacts present NA 35dB 0.5 NA NA
Excellent 5 45+ 0.99+ 80-100 80-100
Good 4 38 .95-.99 60-80 60-80
Fair 3 30 .88-.98 40-60 40-60
Poor 2 24 .50-.88 20-40 20-40
Bad 1 <15 <.5 <20 <20
Just noticeable difference NA NA NA NA 6
Device ratings No No No Multpile Standard, Phone, 4K
Ownership Open source Open source Open source Proprietary Open source

さらに2つの映像ストリームでVMAF差が2未満だとそれぞれの映像の差がわかりにくく、2以上から差が目立ち始め、6以上で過半数の視聴者が変化に気づくと言われている。1080pのスコアが95に達する前にその下の解像度のスコアが2より離れているときは1080pのストリームを複数ビットレートで構成すると品質差が小さくなる。

Identifying the Top Rung of a Bitrate Ladder – OTTVerse

Testing revealed that viewers were unable to distinguish videos with VMAF scores within 2 points. So, if two videos had scores of 92 and 94, they were visually indistinguishable; beyond that level,

Assuming these researchers and Netflix are both correct, a VMAF delta under 2 is irrelevant, deltas above 2 will start to be noticeable, and deltas above 6 will be noticeable by 50% of the viewers.

複数解像度で異なるビットレートでエンコードしたときに以下のようなグラフになり、各解像度の端をつなげた凸包(convex hull)との交点を最適のビットレートとする方法がある。この方法だと上の方法よりも少ないビットレートを指定することになるのと最大解像度でのビットレートも決めることができる。

異なる解像度とビットレートの凸包

Per-Title Encode Optimization. delivering the same or better… | by Netflix Technology Blog | Netflix TechBlog

film-grainをつけたAV1ファイルのvmafスコアを計算するには、エンコード結果それ自体を比較するためにdav1dデコーダに-export_side_data film_grainをつけてfilm-grainを無効化する。libaom-av1libdav1dの両方が含まれるときはlibdav1dが優先される。

VMAF and AV1’s film grain synthesis · Issue #1192 · Netflix/vmaf · GitHub
Discard synthesized film grain for VMAF calculation · Issue #139 · alexheretic/ab-av1 · GitHub

ffmpeg -export_side_data film_grain -i 調べたいAV1の動画 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,libvmaf=model=version=vmaf_v0.6.1\\:name=vmaf\\:enable_transform=0\\:enable_conf_interval=0:feature=name=psnr:log_path=vmaf.xml:log_fmt=xml:pool=mean:n_threads=4:n_subsample=1:shortest=1:repeatlast=0" -an -f null -

vifだけを計算する場合

ffmpeg 4.4から使えるフィルタにvifがある。こちらもlibvmafフィルタと同様に処理が遅いがlibvmafのリンクが必要ないので手軽に使える。2つの映像は同じ解像度、ピクセルフォーマットでなければならない。メタデータに出力できるのでffprobe連携しやすい。

framesyncに対応しているので片方の出力が終わったら終了するようにできる。
2ファイル入力するフィルタの挙動設定 framesync

ffprobe の使い方
Visual Information Fidelity – Wikipedia

オプション設定はなし。出力値は0から1までの小数。framesyncに非対応なので動画時間は揃える必要がある該当コミット
ffmpeg -i 調べたい動画 -i 元の動画 -filter_complex "scale2ref=flags=bicubic,vif=shortest=1:repeatlast=0" -an -f null -

公式ドキュメント:FFmpeg Filters Documentation : vif

メタデータ出力内容。

lavfi.vif.scale.0
lavfi.vif.scale.1
lavfi.vif.scale.2
lavfi.vif.scale.3

ffprobe連携例。出力時間は-read_intervalsで指定。
ffprobe -f lavfi -i movie=enc.mp4[0];movie=orig.mp4[1];[0][1]scale2ref=flags=bicubic,vif -show_entries packet_tags=lavfi.vif.scale.0 -read_intervals "%1" > vif.scale.0.txt

vmaf/integer_vif.c at master · Netflix/vmaf · GitHub
FFmpeg/vf_vif.c at master · FFmpeg/FFmpeg · GitHub

配布場所

Windows用:
FFmpeg Windows Builds – gyan.dev
Releases · BtbN/FFmpeg-Builds

Linux用:
FFmpeg Static Builds

モデルデータはGithubで配布している。
vmaf/model at master · Netflix/vmaf

関連フィルタ

VapourSynthで使う場合。
VMAF – Video Multi-Method Assessment Fusion – Doom9’s Forum
HomeOfVapourSynthEvolution/VapourSynth-VMAF: Video Multi-Method Assessment Fusion

編集履歴

コメントで指摘のあった部分を追記修正。2018年3月13日
media-autobuild_suiteで有効化できないのに言及。2018年7月12日、2018年7月13日
v1.3.9に対応。2018年8月12日
運用方法を追加。2019年1月26日
リサイズの説明を追記。2019年12月2日
Zeranoeで対応バイナリ配布開始。2020年4月30日
scale2refフィルタの利用例を追加。2020年7月19日
モデル名の変更に言及。scale2refフィルタの入力順を訂正。2020年12月27日
モデルファイルの形式を.pklから.jsonに変更。2021年1月5日
n_threadsに言及。2021年2月13日
動画時間が異なる場合の項目を追加。2021年3月5日
中断して途中からやり直す場合の項目を追加。2021年3月15日
vifフィルタの項目を追加。2021年3月21日
NEG、floatモデルに言及。2021年6月23日
エンコードと並列してVMAFフィルタも使う項目を追加。2021年7月30日
ffmpeg 5.0以降のlibvmaf v2.0.0対応に言及。コマンド例はまだ刷新できていない。2022年3月6日
ffmpeg 5.0以降のlibvmaf v2.0.0のオプション、コマンド例を追加。2022年3月12日
ffmpeg 5.0以降のlibvmaf v2.0.0のオプションの追記。2022年3月16日、2022年3月22日
運用方法を追記。2022年8月3日
処理を高速化する項目の内容を充実させた。2023年4月19日
film_grain、libvmaf_cudaに言及。2023年10月12日

7 thoughts on “新しい映像の品質評価 libvmaf

  • Tac

    色々と参考にさせていただいております。ありがとうございます。
    最近libvmafを試していたので、いくつかコメントします。
    間違いなどあったらすみません。

    ●以下の自動ビルドスクリプトでlibvmafを有効にしたWindows用のffmpegバイナリがビルドできました。
      https://github.com/jb-alvarado/media-autobuild_suite
     VMAFが使いたかっただけなので、”Choose ffmpeg and mpv optional libs: “で
     1(Yes)を選び、ffmpeg_options.txtで–enable-avisynthと–enable-libvmafだけ残し、
     他はdisableにしたり消したりして最低限にしたのですが、
     mpv_options.txtの方も不要なものはここでちゃんと無効にしておかないと、
     mpvのビルドをNoにした場合でもmpv用ライブラリをビルドしにいってしまい
     無駄に時間がかかってしまうので注意。
     ビルドしたのは3/5で、各コミットは以下のとおりです。
      media-autobuild_suite r2654 5f3980c4def9d3a818b94b5cc4eb514ad078f0cb
      ffmpeg 69995a94d8409a704361dce9bc16ede7f88bdf1a
      vmaf r683 7426b6fb090b5754bad0c6c428d7dbc2303d980c

    ●libvmafは現状ではpthread依存のようで、ffmpegに–disable-w32threadsが必要なようですが、
     Zeranoe氏はpthreadだと遅くなるということで避けたいらしく、
     当面はlibvmafはZeranoe版バイナリには入らない模様です。
      https://ffmpeg.zeranoe.com/forum/viewtopic.php?p=13039#p13039

    ●i7-4702MQで1080p/2157framesのSSIMやVMAFを計算させると
     -lavfi ssimなら150fps出るのに、-lavfi libvmafは2.8fpsしか出ませんでした。
     また、ssimでは実行中のメモリ使用量も200MB以下なのですが、
     libvmafでは最大6GBくらいになってかなり重くなりました・・・。
     バグなのか仕様なのかビルドの問題なのか不明ですが
     もうちょっと軽く動くようになると嬉しいですね。

    ●VMAFは基本的にオリジナルソース(=reference)を固定した上で、それを元にして
     リサイズやエンコードした複数の配信用ファイル(=distorted,main)のVMAFスコアを測り、
      「同じビットレートならどれが一番VMAFスコアが高いか」
     を調べて、最適な配信用ファイルを決めるといった形で使うものだと思います。

    ●したがって、リサイズしたもののVMAFスコアを測る場合は、
     リサイズしたものをオリジナル解像度に戻した上で
     VMAFスコアを測るのが一般的ではないかと思います。
     コマンド的には
      ffmpeg -i resized720p_x265_2000kbps.mp4 -i original1080p.mp4 -filter_complex scale=1920:1080:flags=print_info+lanczos,libvmaf=model_path=model/vmaf_v0.6.1.pkl -an -f null –
     といった形の方が順序的にも意味的にもわかりやすいのではないかと思います。

    ●YUVの動画比較のところでoriginal.yuvとreference.yuvという
     表現(どちらもオリジナルを指す)が混在してしまっているので、
      original.yuv → encoded.yuv
      reference.yuv → original.yuv
     といった表現に変えるとわかりやすくなると思います。
     (referenceって一般的にはあまりなじみが無い表現かなーと)

    ●enable_transformは、「transformを計算する」というより
      「VMAFスコアの計算でtransformを有効にする」
     という説明の方がよいかと思います。
     (どういう処理なのかは自分もよくわかってないですが
      同一ファイルのVMAFスコアが100.0になったりするようです。)

    ●poolの harmonic mean の指定は harmonic_mean のようです。

  • Tac

    メモリリークの情報と更新ありがとうございます。
    メモリリークの件は調べていませんでした。

    ●修正後の記事のリサイズの部分ですが、
      ・「リサイズする場合」というタイトルは「解像度が異なる場合」が良いかも。
      ・VMAF計測時のリサイズアルゴリズムは特に縮小時と同じにする必要はなく
       好きなもの(視聴時に期待できるもの)を選択すればよい。
       (高性能なアルゴリズムにすればスコアも上がる)
      ・エンコードする前の動画(オリジナル)をリサイズするのではなく、
       エンコード後の動画をオリジナルサイズにあわせてリサイズする。
      ・VMAF計測時のコマンドでscaleの引数が1280:720になっているが1920:1080のはず。
       (このままだと解像度違いでエラーになる)
     ということで、以下のような感じが良いかもしれません。
     (splineとlanczosにしているのはなんとなくです)

    ——————————————————————–
    解像度が異なる場合

    解像度が異なる場合に VMAF スコアを調べるには、調べたい動画をフィルタでオリジナルの解像度にリサイズしてから調べればよい。

    オリジナルが 1920 x 1080、エンコード後が 1280 x 720 の場合。

    ffmpeg -i original-1080p.mp4 -filter_complex scale=1280:720:flags=spline -acodec copy resized-720p.mp4

    ffmpeg -i resized-720p.mp4 -i original-1080p.mp4 -filter_complex “scale=1920:1080:flags=lanczos,libvmaf” -an -f null –
    ——————————————————————–

    ●SSIMとは違って [encoded][original] と [original][encoded] の結果が異なるので、
     「順序は必ず “-i 調べたい動画 -i オリジナル” にする必要がある」という注意書きもあると良いかなと思いました。

    • admin

      コメント確認しました。スレッドも確認しましたが HEVC の100倍遅いと言われている AV1 ですがそれどころじゃないくらい遅くて今のところ全く実用的ではないですね。

       AV1(libaom-av1)で1920×1080の10フレームだけをエンコードしてみた結果。http://mevius.5ch.net/test/read.cgi/avi/1515759816/378

       他のエンコーダとの速度比較などhttp://mevius.5ch.net/test/read.cgi/avi/1515759816/392

      • Tac

        AV1はまだ最適化もされてませんから遅すぎてテストもままならないですね・・・。
        なおZeranoe版ffmpegではlibaomのビルドミスがあり、
        ffmpeg-20180404-53688b6までは本来よりも更に遅い結果(最大で5.7倍くらい)となります。
        そのため検証するならそれ以降のビルド(このコメントを書いている時点ではまだ未リリース)を使う必要があります。
         https://ffmpeg.zeranoe.com/forum/viewtopic.php?f=5&t=5601#p13513

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)