T-CREATOR

FFmpeg 画質評価の作法:VMAF/SSIM/PSNR を揃えて測るベンチ手順

FFmpeg 画質評価の作法:VMAF/SSIM/PSNR を揃えて測るベンチ手順

動画エンコード後の品質評価、どのように行っていますか? 目視確認だけでは主観的になりがちですし、複数の設定を比較する際には客観的な指標が欠かせません。 FFmpeg にはVMAF(Video Multimethod Assessment Fusion)SSIM(Structural Similarity Index)、**PSNR(Peak Signal-to-Noise Ratio)**という 3 つの主要な画質評価指標が組み込まれており、これらを適切に活用することで、科学的かつ再現性のある品質評価が可能になるのです。

本記事では、FFmpeg を使った画質評価の基礎から実践的なベンチマーク手順まで、初心者の方にもわかりやすく解説いたします。 これらの指標を正しく理解し、適切に使い分けることで、エンコード設定の最適化や品質管理が格段に向上しますよ。

背景

動画品質評価が必要な理由

動画配信サービスやストリーミングプラットフォームが普及する中、限られた帯域幅で高品質な映像を届けることが求められています。 エンコード設定を変更すると、ファイルサイズは削減できますが、同時に画質も劣化する可能性があります。

このトレードオフを最適化するためには、客観的な品質指標による評価が不可欠です。

以下の図は、動画エンコードと品質評価の関係性を示しています。

mermaidflowchart TD
    original["オリジナル<br/>動画ファイル"]
    encode["エンコード処理<br/>(FFmpeg)"]
    compressed["圧縮された<br/>動画ファイル"]
    metrics["品質評価指標<br/>(VMAF/SSIM/PSNR)"]
    analysis["品質分析<br/>結果"]

    original -->|入力| encode
    encode -->|出力| compressed
    original -->|参照| metrics
    compressed -->|比較| metrics
    metrics -->|計算| analysis

このように、オリジナル動画と圧縮後の動画を比較することで、エンコードによる品質劣化を定量的に測定できます。

3 つの評価指標の位置づけ

VMAF、SSIM、PSNR はそれぞれ異なるアプローチで画質を評価します。 PSNR は最も古典的で計算が高速ですが、人間の視覚特性を考慮していません。 SSIM は構造的な類似性を評価し、PSNR よりも人間の知覚に近い結果を提供します。

VMAF は機械学習ベースの最新指標で、Netflix が開発した最も人間の主観評価に近い指標として注目されています。

以下の表は、3 つの指標の特徴を比較したものです。

#指標開発元アプローチ計算速度人間の知覚との相関スコア範囲
1PSNR-ピクセル差分★★★0〜∞ dB(高いほど良い)
2SSIMテキサス大学構造的類似性★★★★-1〜1(1 が完全一致)
3VMAFNetflix機械学習★★★0〜100(高いほど良い)

これらを組み合わせて使用することで、多角的な品質評価が可能になります。

課題

各指標の特性と限界

画質評価において、単一の指標だけに依存することは危険です。 各指標には得意・不得意があり、動画の内容やエンコード方式によって評価結果が大きく異なることがあるからです。

PSNR の課題:人間の視覚との乖離

PSNR はピクセル単位の誤差を測定するため、計算は高速ですが人間の視覚特性を考慮していません。 例えば、高周波成分(細かいディテール)の劣化と低周波成分(大まかな構造)の劣化を同等に扱ってしまいます。

その結果、PSNR が高くても主観的には画質が悪く感じられるケースが発生します。

SSIM の課題:動画特有の問題への対応

SSIM は静止画の構造的類似性を評価する指標として設計されました。 動画特有の時間的な変化(モーションブラー、動きベクトルなど)を十分に考慮できていません。

また、明るさやコントラストの変化には比較的寛容ですが、色相の変化には敏感ではないという特性があります。

VMAF の課題:計算コストと環境依存

VMAF は機械学習モデルを使用するため、計算コストが高く、大量の動画を評価する際には時間がかかります。 また、モデルファイルの準備や FFmpeg のビルド設定によっては利用できない環境もあるでしょう。

さらに、学習データに偏りがある場合、特定の動画タイプでは正確な評価ができない可能性も指摘されています。

以下の図は、各指標の評価における課題を示しています。

mermaidflowchart LR
    video["評価対象<br/>動画"]

    subgraph psnr_box["PSNR評価"]
        psnr_calc["ピクセル差分<br/>計算"]
        psnr_issue["課題:視覚特性<br/>無視"]
    end

    subgraph ssim_box["SSIM評価"]
        ssim_calc["構造的類似性<br/>計算"]
        ssim_issue["課題:時間的<br/>変化に弱い"]
    end

    subgraph vmaf_box["VMAF評価"]
        vmaf_calc["機械学習<br/>モデル"]
        vmaf_issue["課題:計算<br/>コスト高"]
    end

    video --> psnr_calc
    video --> ssim_calc
    video --> vmaf_calc

    psnr_calc --> psnr_issue
    ssim_calc --> ssim_issue
    vmaf_calc --> vmaf_issue

図で理解できる要点:

  • 各指標は異なる計算方式を採用している
  • それぞれ固有の課題や限界がある
  • 単一指標だけでは不十分な理由が視覚化されている

評価環境の統一が困難

正確なベンチマークを行うには、評価条件を統一する必要があります。 しかし、動画ファイルのフレームレート、解像度、ビット深度、色空間などのパラメータが異なると、正確な比較ができません。

また、FFmpeg のバージョンやビルドオプション、使用するフィルターチェーンによっても結果が変わってしまうのです。

解決策

複合的な評価アプローチ

単一指標の限界を克服するには、VMAF、SSIM、PSNR の 3 つを同時に測定し、総合的に判断することが重要です。 それぞれの指標が異なる側面から画質を評価するため、3 つの結果を比較することで、より正確な品質評価が可能になります。

推奨される評価基準は以下のとおりです。

#指標優秀良好許容範囲要改善
1VMAF95 以上85〜9470〜8470 未満
2SSIM0.98 以上0.95〜0.970.90〜0.940.90 未満
3PSNR45dB 以上40〜44dB35〜39dB35dB 未満

FFmpeg による統一的な測定環境

FFmpeg は動画処理の標準ツールとして、これら 3 つの指標すべてをサポートしています。 同一のツールで評価することで、環境の統一が容易になり、再現性の高いベンチマークが実現できるのです。

測定の基本的な流れは、以下のようになります。

mermaidflowchart TD
    start["測定開始"]
    prepare["動画ファイル<br/>準備"]
    check["フォーマット<br/>確認"]
    normalize["正規化処理<br/>(必要に応じて)"]

    subgraph measurement["並列測定"]
        vmaf_measure["VMAF測定"]
        ssim_measure["SSIM測定"]
        psnr_measure["PSNR測定"]
    end

    collect["結果収集"]
    analyze["総合分析"]
    report["レポート<br/>生成"]

    start --> prepare
    prepare --> check
    check --> normalize
    normalize --> measurement
    measurement --> collect
    collect --> analyze
    analyze --> report

図で理解できる要点:

  • 測定前の準備フェーズで動画を正規化する
  • 3 つの指標は並列に測定可能である
  • 結果を収集して総合的に分析する流れが明確になっている

自動化スクリプトの活用

手動で 3 つの指標を個別に測定するのは効率的ではありません。 シェルスクリプトや Python スクリプトを使って測定プロセスを自動化することで、作業時間を大幅に削減できます。

また、結果を JSON 形式や CSV 形式で保存することで、後から分析や可視化が容易になるでしょう。

具体例

環境準備

FFmpeg のインストール確認

まず、FFmpeg が VMAF サポート付きでインストールされているか確認しましょう。

bash# FFmpegのバージョンとビルド設定を確認
ffmpeg -version

出力結果の中に --enable-libvmaf が含まれていれば、VMAF 測定が可能です。 もし含まれていない場合は、VMAF サポート付きで FFmpeg を再ビルドする必要があります。

VMAF モデルファイルの準備

VMAF を使用するには、モデルファイルが必要です。 一般的には、以下のパスにインストールされています。

bash# Homebrewでインストールした場合(macOS)
ls /opt/homebrew/share/libvmaf/model/

# Linuxの場合
ls /usr/share/model/vmaf/

vmaf_v0.6.1.json または vmaf_4k_v0.6.1.json などのファイルが存在することを確認してください。

測定用の動画ファイル準備

オリジナル動画とエンコード済み動画

ベンチマークには、比較元となるオリジナル動画(参照動画)と、エンコード後の動画(評価対象)が必要です。

bash# サンプル動画をダウンロード(例)
wget https://example.com/sample_original.mp4 -O original.mp4

# エンコード設定でテスト動画を作成
ffmpeg -i original.mp4 -c:v libx264 -crf 23 -preset medium encoded_crf23.mp4

ここでは、H.264 コーデックで CRF(Constant Rate Factor)23 という標準的な品質設定でエンコードしています。 CRF 値は 0〜51 の範囲で、値が小さいほど高品質(大容量)、大きいほど低品質(小容量)になります。

動画フォーマットの統一

正確な比較のため、両動画のフレームレート、解像度、色空間を統一する必要があります。

bash# 動画情報を確認
ffprobe -v error -select_streams v:0 \
  -show_entries stream=width,height,r_frame_rate,pix_fmt \
  -of default=noprint_wrappers=1 original.mp4

このコマンドで、動画の幅・高さ・フレームレート・ピクセルフォーマットが確認できます。 もし参照動画と評価対象動画で異なる場合は、前処理で統一しましょう。

bash# フォーマット統一の例(1920x1080、30fps、yuv420pに変換)
ffmpeg -i original.mp4 -vf scale=1920:1080,fps=30 \
  -pix_fmt yuv420p original_normalized.mp4

PSNR 測定の実行

基本的な PSNR 測定コマンド

PSNR は最も高速に計算できる指標です。 FFmpeg の psnr フィルターを使用します。

bash# PSNR測定コマンド
ffmpeg -i encoded_crf23.mp4 -i original.mp4 \
  -filter_complex "psnr=stats_file=psnr_output.log" \
  -f null -

このコマンドの構造を説明します。 最初の -i オプションで評価対象動画を指定し、2 番目の -i で参照動画(オリジナル)を指定します。

-filter_complex オプションで psnr フィルターを適用し、結果を psnr_output.log に保存しています。 -f null - は出力動画を生成せず、処理だけを実行するための指定です。

PSNR 結果の読み方

測定が完了すると、ログファイルに各フレームの PSNR 値が記録されます。

bash# ログファイルの確認
cat psnr_output.log

出力例:

textn:1 mse_avg:12.34 mse_y:11.23 mse_u:13.45 mse_v:12.89 psnr_avg:37.21 psnr_y:37.63 psnr_u:36.84 psnr_v:37.03
n:2 mse_avg:11.87 mse_y:10.98 mse_u:12.76 mse_v:12.01 psnr_avg:37.38 psnr_y:37.72 psnr_u:37.07 psnr_v:37.33

各項目の意味は以下のとおりです。

  • n: フレーム番号
  • mse_avg: 平均二乗誤差(全チャンネル)
  • psnr_avg: 平均 PSNR 値(全チャンネル、最も重要な値
  • psnr_y: 輝度(Y)チャンネルの PSNR
  • psnr_u, psnr_v: 色差(U, V)チャンネルの PSNR

PSNR 平均値の抽出

全フレームの平均 PSNR 値を算出するには、ログファイルの最終行を確認するか、以下のコマンドで計算します。

bash# 平均PSNR値を抽出(Linuxの場合)
grep "psnr_avg" psnr_output.log | \
  awk '{sum+=$6; count++} END {print "Average PSNR:", sum/count, "dB"}'

このコマンドは、ログファイルから psnr_avg を含む行を抽出し、6 番目のフィールド(PSNR 値)の平均を計算します。 40dB 以上であれば良好な品質と判断できるでしょう。

SSIM 測定の実行

基本的な SSIM 測定コマンド

SSIM は構造的類似性を評価します。 FFmpeg の ssim フィルターを使用します。

bash# SSIM測定コマンド
ffmpeg -i encoded_crf23.mp4 -i original.mp4 \
  -filter_complex "ssim=stats_file=ssim_output.log" \
  -f null -

コマンド構造は PSNR と同様で、フィルター名を ssim に変更するだけです。 SSIM もフレームごとの値がログファイルに記録されます。

SSIM 結果の読み方

SSIM ログファイルの出力例:

textn:1 Y:0.9876 U:0.9823 V:0.9845 All:0.9851 (14.27)
n:2 Y:0.9889 U:0.9834 V:0.9856 All:0.9863 (14.63)

各項目の意味は以下のとおりです。

  • n: フレーム番号
  • Y, U, V: 各チャンネルの SSIM 値(0〜1、1 が完全一致)
  • All: 全チャンネルの平均 SSIM 値(最も重要な値
  • 括弧内の数値: SSIM 値を dB 換算した値

SSIM 平均値の抽出

全フレームの平均 SSIM 値を算出します。

bash# 平均SSIM値を抽出
grep "All:" ssim_output.log | \
  awk '{sum+=$8; count++} END {print "Average SSIM:", sum/count}'

SSIM 値は 0 から 1 の範囲で、0.95 以上であれば高品質と判断できます。 0.90 未満の場合は、視覚的に劣化が認識できるレベルでしょう。

VMAF 測定の実行

基本的な VMAF 測定コマンド

VMAF は最も人間の知覚に近い評価を提供します。 libvmaf フィルターを使用し、モデルファイルを指定します。

bash# VMAF測定コマンド(macOSの場合)
ffmpeg -i encoded_crf23.mp4 -i original.mp4 \
  -filter_complex "[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf=model_path=/opt/homebrew/share/libvmaf/model/vmaf_v0.6.1.json:log_path=vmaf_output.json:log_fmt=json" \
  -f null -

このコマンドは少し複雑なので、部分ごとに説明します。

まず、setpts=PTS-STARTPTS で両動画のタイムスタンプを 0 から開始するように正規化しています。 これにより、フレームの同期が正確になります。

次に、libvmaf フィルターで以下のパラメータを指定しています。

  • model_path: VMAF モデルファイルのパス
  • log_path: 結果を保存する JSON ファイルのパス
  • log_fmt=json: 出力フォーマットを JSON に指定

Linux での実行例

Linux の場合は、モデルファイルのパスが異なります。

bash# VMAF測定コマンド(Linuxの場合)
ffmpeg -i encoded_crf23.mp4 -i original.mp4 \
  -filter_complex "[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf=model_path=/usr/share/model/vmaf/vmaf_v0.6.1.json:log_path=vmaf_output.json:log_fmt=json" \
  -f null -

環境に応じて、モデルファイルのパスを適切に変更してください。

VMAF 結果の読み方

測定が完了すると、JSON 形式の結果ファイルが生成されます。

bash# VMAF結果の確認
cat vmaf_output.json

JSON ファイルには、フレームごとの詳細な VMAF スコアが記録されています。 重要なのは、全体の平均 VMAF 値です。

bash# 平均VMAF値を抽出(jqコマンドを使用)
jq '.pooled_metrics.vmaf.mean' vmaf_output.json

もし jq がインストールされていない場合は、以下のコマンドでインストールできます。

bash# macOS
brew install jq

# Ubuntu/Debian
sudo apt install jq

VMAF 値は 0 から 100 の範囲で、以下のように解釈します。

#VMAF 範囲品質評価視聴体験
195〜100非常に優秀オリジナルとほぼ区別不可
285〜94優秀わずかな劣化のみ
370〜84良好許容範囲内の品質
450〜69普通劣化が認識できる
550 未満要改善明確な品質低下

複数の設定を一括ベンチマーク

実際のエンコード最適化では、複数の CRF 値やプリセットを比較する必要があります。 以下は、自動化スクリプトの例です。

ベンチマークスクリプト(Bash)

bash#!/bin/bash
# FFmpeg画質評価ベンチマークスクリプト

# 設定
ORIGINAL="original.mp4"
RESULTS_DIR="benchmark_results"
VMAF_MODEL="/opt/homebrew/share/libvmaf/model/vmaf_v0.6.1.json"

# 結果ディレクトリ作成
mkdir -p "$RESULTS_DIR"

このスクリプトの冒頭部分では、オリジナル動画のパス、結果保存ディレクトリ、VMAF モデルのパスを定義しています。 環境に応じて、これらの変数を変更してください。

テストパターンの定義

bash# テストするCRF値の配列
CRF_VALUES=(18 23 28 33)

# テストするプリセット
PRESETS=("fast" "medium" "slow")

ここでは、4 つの異なる CRF 値(18, 23, 28, 33)と 3 つのプリセット(fast, medium, slow)を定義しています。 これにより、合計 12 通りの組み合わせをテストできます。

エンコードループ

bash# 各設定でエンコードと評価を実行
for preset in "${PRESETS[@]}"; do
    for crf in "${CRF_VALUES[@]}"; do
        echo "Testing: CRF=$crf, Preset=$preset"

        # エンコード
        OUTPUT="${RESULTS_DIR}/encoded_${preset}_crf${crf}.mp4"
        ffmpeg -i "$ORIGINAL" -c:v libx264 \
          -crf "$crf" -preset "$preset" \
          -y "$OUTPUT"
    done
done

このループでは、各プリセットと CRF 値の組み合わせでエンコードを実行します。 -y オプションは、既存ファイルを上書きするための指定です。

画質評価の実行

bash        # PSNR測定
        PSNR_LOG="${RESULTS_DIR}/psnr_${preset}_crf${crf}.log"
        ffmpeg -i "$OUTPUT" -i "$ORIGINAL" \
          -filter_complex "psnr=stats_file=${PSNR_LOG}" \
          -f null -

        # SSIM測定
        SSIM_LOG="${RESULTS_DIR}/ssim_${preset}_crf${crf}.log"
        ffmpeg -i "$OUTPUT" -i "$ORIGINAL" \
          -filter_complex "ssim=stats_file=${SSIM_LOG}" \
          -f null -

        # VMAF測定
        VMAF_JSON="${RESULTS_DIR}/vmaf_${preset}_crf${crf}.json"
        ffmpeg -i "$OUTPUT" -i "$ORIGINAL" \
          -filter_complex "[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf=model_path=${VMAF_MODEL}:log_path=${VMAF_JSON}:log_fmt=json" \
          -f null -

        echo "Completed: CRF=$crf, Preset=$preset"

各エンコード済み動画に対して、3 つの指標(PSNR、SSIM、VMAF)を測定します。 結果ファイルには、プリセット名と CRF 値が含まれるため、後から識別しやすくなっています。

結果サマリーの生成

bash# 結果サマリーをCSVで出力
echo "Preset,CRF,PSNR_avg,SSIM_avg,VMAF_mean,FileSize_MB" > "${RESULTS_DIR}/summary.csv"

for preset in "${PRESETS[@]}"; do
    for crf in "${CRF_VALUES[@]}"; do
        OUTPUT="${RESULTS_DIR}/encoded_${preset}_crf${crf}.mp4"
        PSNR_LOG="${RESULTS_DIR}/psnr_${preset}_crf${crf}.log"
        SSIM_LOG="${RESULTS_DIR}/ssim_${preset}_crf${crf}.log"
        VMAF_JSON="${RESULTS_DIR}/vmaf_${preset}_crf${crf}.json"

        # 各指標の平均値を抽出
        PSNR_AVG=$(grep "psnr_avg" "$PSNR_LOG" | awk '{sum+=$6; count++} END {printf "%.2f", sum/count}')
        SSIM_AVG=$(grep "All:" "$SSIM_LOG" | awk '{sum+=$8; count++} END {printf "%.4f", sum/count}')
        VMAF_MEAN=$(jq -r '.pooled_metrics.vmaf.mean' "$VMAF_JSON")
        FILE_SIZE=$(du -m "$OUTPUT" | awk '{printf "%.2f", $1}')

        echo "$preset,$crf,$PSNR_AVG,$SSIM_AVG,$VMAF_MEAN,$FILE_SIZE" >> "${RESULTS_DIR}/summary.csv"
    done
done

echo "Benchmark completed. Results saved in $RESULTS_DIR/summary.csv"

最後に、すべての結果を CSV 形式でまとめます。 このサマリーファイルを使えば、Excel やスプレッドシートで簡単に比較・分析できるでしょう。

スクリプトの実行

bash# スクリプトに実行権限を付与
chmod +x benchmark.sh

# 実行
./benchmark.sh

処理が完了すると、benchmark_results ディレクトリ内に各エンコード動画、ログファイル、サマリー CSV が保存されます。

結果の分析と可視化

サマリー CSV の確認

bash# サマリーファイルの表示
cat benchmark_results/summary.csv

出力例:

textPreset,CRF,PSNR_avg,SSIM_avg,VMAF_mean,FileSize_MB
fast,18,42.34,0.9823,92.45,125.67
fast,23,39.21,0.9654,87.32,78.43
fast,28,36.87,0.9423,79.21,45.89
fast,33,34.12,0.9102,68.54,28.76
medium,18,42.78,0.9856,93.67,118.32
medium,23,39.89,0.9712,88.91,72.14
medium,28,37.45,0.9501,81.43,41.23
medium,33,34.67,0.9178,70.12,25.89
slow,18,43.12,0.9878,94.23,112.45
slow,23,40.34,0.9745,89.87,68.76
slow,28,38.01,0.9567,82.91,38.54
slow,33,35.23,0.9234,71.56,24.12

この結果から、以下のような傾向が読み取れます。

  • CRF 値が小さいほど、すべての指標で高スコアだがファイルサイズも大きい
  • プリセット slow が最も高品質だが、エンコード時間も長い
  • CRF 23 前後が、品質とファイルサイズのバランスが良い

品質とファイルサイズのトレードオフ分析

以下の図は、品質指標とファイルサイズの関係を示しています。

mermaidgraph LR
    size_small["ファイルサイズ<br/>小"]
    size_large["ファイルサイズ<br/>大"]

    quality_low["品質<br/>低"]
    quality_high["品質<br/>高"]

    crf33["CRF 33<br/>VMAF: 68-71"]
    crf28["CRF 28<br/>VMAF: 79-82"]
    crf23["CRF 23<br/>VMAF: 87-89"]
    crf18["CRF 18<br/>VMAF: 92-94"]

    size_small --> crf33
    crf33 --> crf28
    crf28 --> crf23
    crf23 --> crf18
    crf18 --> size_large

    quality_low --> crf33
    crf18 --> quality_high

図で理解できる要点:

  • CRF 値とファイルサイズは反比例の関係にある
  • VMAF 値は 85 以上が推奨範囲であることがわかる
  • CRF 23 が実用的なバランスポイントとなっている

最適設定の選定基準

ベンチマーク結果から最適設定を選ぶ際は、以下の基準を参考にしてください。

#用途推奨 VMAF推奨 SSIM推奨 PSNR推奨 CRF 範囲
1アーカイブ保存95 以上0.98 以上45dB 以上15〜18
2高品質配信85〜940.95〜0.9740〜44dB19〜23
3標準品質配信75〜840.93〜0.9437〜39dB24〜28
4低帯域配信65〜740.90〜0.9234〜36dB29〜33

Python による自動化と可視化

より高度な分析を行う場合は、Python スクリプトを使用すると便利です。

必要なパッケージのインストール

bash# Pythonパッケージをインストール
pip install pandas matplotlib seaborn

pandas はデータ分析、matplotlibseaborn は可視化に使用します。

CSV データの読み込みと分析

pythonimport pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# CSVファイルを読み込み
df = pd.read_csv('benchmark_results/summary.csv')

# データの基本統計を表示
print(df.describe())

このコードは、サマリー CSV を読み込み、各指標の基本統計(平均、標準偏差、最小値、最大値など)を表示します。

品質指標の相関分析

python# 品質指標間の相関係数を計算
correlation = df[['PSNR_avg', 'SSIM_avg', 'VMAF_mean']].corr()
print("Correlation Matrix:")
print(correlation)

# 相関マトリックスのヒートマップを作成
plt.figure(figsize=(8, 6))
sns.heatmap(correlation, annot=True, cmap='coolwarm',
            vmin=-1, vmax=1, center=0)
plt.title('Quality Metrics Correlation')
plt.tight_layout()
plt.savefig('benchmark_results/correlation_heatmap.png')
plt.close()

このコードは、3 つの品質指標間の相関を計算し、ヒートマップとして可視化します。 通常、VMAF、SSIM、PSNR は正の相関を示しますが、相関の強さは動画内容によって異なります。

品質-ファイルサイズのプロット

python# 各プリセットごとに色分けしてプロット
plt.figure(figsize=(12, 8))

for preset in df['Preset'].unique():
    subset = df[df['Preset'] == preset]
    plt.scatter(subset['FileSize_MB'], subset['VMAF_mean'],
                label=preset, s=100, alpha=0.7)

    # CRF値をデータポイントに表示
    for idx, row in subset.iterrows():
        plt.annotate(f"CRF {row['CRF']}",
                    (row['FileSize_MB'], row['VMAF_mean']),
                    xytext=(5, 5), textcoords='offset points',
                    fontsize=8)

plt.xlabel('File Size (MB)', fontsize=12)
plt.ylabel('VMAF Score', fontsize=12)
plt.title('Quality vs File Size Trade-off', fontsize=14)
plt.legend(title='Preset')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('benchmark_results/quality_vs_size.png', dpi=300)
plt.close()

print("Visualization saved to benchmark_results/quality_vs_size.png")

このプロットにより、どのプリセットと CRF 値の組み合わせが最適なのかを視覚的に判断できます。 グラフの左上(小さいファイルサイズで高い VMAF)が理想的な設定です。

エンコード効率の算出

python# エンコード効率を計算(VMAF / ファイルサイズ)
df['Efficiency'] = df['VMAF_mean'] / df['FileSize_MB']

# 効率でソートして上位10件を表示
top_efficient = df.nlargest(10, 'Efficiency')[
    ['Preset', 'CRF', 'VMAF_mean', 'FileSize_MB', 'Efficiency']
]

print("\nTop 10 Most Efficient Settings:")
print(top_efficient)

# 効率ランキングをCSVで保存
top_efficient.to_csv('benchmark_results/efficiency_ranking.csv',
                     index=False)

エンコード効率は、「単位ファイルサイズあたりの品質」を表します。 この指標により、配信コストを考慮した最適設定を見つけることができるでしょう。

4K 動画の評価

高解像度動画では、専用の VMAF モデルを使用することが推奨されます。

4K 用 VMAF モデルの使用

bash# 4K動画用のVMAF測定
ffmpeg -i encoded_4k.mp4 -i original_4k.mp4 \
  -filter_complex "[0:v]setpts=PTS-STARTPTS[dist];[1:v]setpts=PTS-STARTPTS[ref];[dist][ref]libvmaf=model_path=/opt/homebrew/share/libvmaf/model/vmaf_4k_v0.6.1.json:log_path=vmaf_4k_output.json:log_fmt=json" \
  -f null -

vmaf_4k_v0.6.1.json モデルは、4K 解像度の動画に最適化されており、より正確な評価が可能です。 HD 解像度では通常モデル、4K 以上では 4K モデルを使い分けましょう。

解像度別の評価基準の違い

解像度によって、同じ指標値でも視覚的な印象が異なることがあります。

#解像度VMAF 目標値SSIM 目標値備考
1720p(HD)85 以上0.95 以上標準モデル使用
21080p(Full HD)88 以上0.96 以上標準モデル使用
32160p(4K)90 以上0.97 以上4K モデル使用推奨

高解像度になるほど、圧縮による劣化が目立ちやすくなるため、より高い指標値が求められます。

異なるコーデックの比較

H.264(AVC)と H.265(HEVC)、AV1 など、異なるコーデック間の品質比較も可能です。

コーデック別エンコード例

bash# H.264でエンコード
ffmpeg -i original.mp4 -c:v libx264 -crf 23 -preset medium encoded_h264.mp4

# H.265でエンコード
ffmpeg -i original.mp4 -c:v libx265 -crf 28 -preset medium encoded_h265.mp4

# AV1でエンコード(libaom-av1を使用)
ffmpeg -i original.mp4 -c:v libaom-av1 -crf 30 -preset medium encoded_av1.mp4

H.265 と AV1 は、同等の品質でより高い圧縮率を実現できますが、エンコード時間が長くなります。 また、CRF 値の意味がコーデックによって異なるため、同じ CRF 値でも品質は異なることに注意してください。

コーデック間の公平な比較

コーデック間を比較する際は、同じファイルサイズまたは同じ VMAF スコアで比較することが重要です。

python# Pythonでコーデック比較データを分析
codecs_df = pd.DataFrame({
    'Codec': ['H.264', 'H.265', 'AV1'],
    'FileSize_MB': [78.43, 65.21, 58.32],
    'VMAF': [87.32, 88.91, 89.45],
    'PSNR': [39.21, 40.34, 40.87],
    'SSIM': [0.9654, 0.9712, 0.9756]
})

# エンコード効率を計算
codecs_df['Efficiency'] = codecs_df['VMAF'] / codecs_df['FileSize_MB']

print(codecs_df)

この分析により、AV1 が最も高い圧縮効率を持つことが確認できます。 ただし、デコード互換性やエンコード時間も考慮する必要があるでしょう。

まとめ

FFmpeg を使った画質評価は、動画エンコード最適化において欠かせない技術です。 VMAF、SSIM、PSNR という 3 つの指標をそれぞれ理解し、適切に組み合わせることで、客観的かつ科学的な品質評価が実現できます。

重要なポイントのまとめ

  • PSNRは高速だが視覚特性を考慮しない、ベースライン指標として活用
  • SSIMは構造的類似性を評価、PSNR より人間の知覚に近い
  • VMAFは機械学習ベースで最も主観評価に近く、最終判断に最適
  • 3 つの指標を同時に測定することで、多角的な品質評価が可能
  • 自動化スクリプトにより、複数設定の効率的なベンチマークを実現
  • 結果の可視化により、品質とファイルサイズのトレードオフを把握
  • 用途に応じた基準(アーカイブ、高品質配信、標準配信など)で最適設定を選定

これらの手法を活用することで、エンコード設定の根拠を明確にし、配信品質の向上とコスト最適化を両立できるようになります。 ぜひご自身のプロジェクトで実践してみてください。

測定結果をチームで共有し、継続的に品質基準を改善していくことで、より高品質な動画配信サービスの構築につながるはずです。

関連リンク