ブログに戻る
2026年10月27日Sergei Solod13 分で読めます

画像ごとに最適化せず、SSIMULACRA2でAVIFの画質を決める方法

画像ごとに何度もエンコードする代わりに、SSIMULACRA2と目視確認でAVIFの固定画質値を決める。基準達成率とファイルサイズを比較し、実用的な設定を選ぶ方法を解説する。

AVIFSSIMULACRA2画像圧縮Sharp画像最適化

AVIFの画質設定を決めるのは、ファイルサイズだけを重視するなら簡単だ。難しいのは、目に見える劣化をどこまで許容するかという判断である。画質値40は知覚品質スコア40を意味しないし、すべての画像で同じ結果になる保証もない。同じエンコーダー設定でも、元画像によって結果は大きく異なる。

SSIMULACRA2は、圧縮画像をデコードしたものと参照画像を比較することで、この判断を助けてくれる。ただし、画像ごとに画質値を総当たりで探すと、本来は単純な変換処理が大量のエンコードを伴う重い作業になりかねない。すべてのファイルを完全に最適化するのではなく、Web向けとして十分な画質を得たいなら、もっと簡単な方法がある。代表的なサンプルでAVIFの画質値を一つ決め、見た目を確認したうえで、残りの画像にも同じ値を使う。

AVIFの画質値とSSIMULACRA2のスコアは別物

AVIFエンコーダーに渡すqualityは圧縮方法を指定する設定値だ。一方、SSIMULACRA2はエンコード結果を測定した値である。両者の尺度を同一視してはいけない。AVIFのquality: 40から、SSIMULACRA2のスコアが40、60、あるいはその他の一定値になるとは判断できない。

エンコード結果を測定するには、画像をデコードして参照画像と比較する。

ssimulacra2 reference.png encoded-decoded.png

SSIMULACRA2のスコアが高いほど、知覚上の類似度は高い。公式ドキュメントでは、50前後を中程度の品質、70前後を高品質と説明している。60というしきい値は実務上の目安になり得るが、画像の許容品質を普遍的に定義するものではなく、運用上の判断基準にすぎない。代表的な画像は必ず実際の表示サイズで目視確認する。

画像ごとの徹底的な最適化が高コストになる理由

厳密な最適化処理では、同じ画像を異なる画質値で繰り返しエンコードし、各候補をSSIMULACRA2で測定して、許容基準を満たす最小サイズの結果を残す。この方法なら、圧縮しやすい画像には低い画質値を、難しい画像には高い画質値を割り当てられるため、圧縮効率をさらに高められることが多い。

欠点はエンコード回数である。画像数をN、許容値が見つかるまでに試す候補数を画像当たりKとすると、エンコードはおおむねN × K回になり、さらにデコードとスコア計算も必要になる。実際には、高い計算負荷を指定したAVIFエンコードが処理時間の大部分を占めることがあり、SSIMULACRA2の計算自体が必ずしも高コストなわけではない。

フォルダーごとに画質値を一つ選ぶ方法なら探索量は減るが、グループごとのサンプリング、測定、設定管理は依然として必要だ。画像の配信要件が共通していて、一部の外れ値を許容できるコレクションなら、その複雑さに見合う価値がない場合もある。

方式キャリブレーション作業主なトレードオフ
画像ごとの適応的な画質調整各画像に対して複数の候補をエンコード細かく制御できるが、調整コストが最も高い
グループごとの適応的な画質調整各グループのサンプルで複数の候補をエンコード作業は減るが、設定が増え、結果もグループに依存する
単一の固定画質値限定した調整用サンプルだけで候補をエンコード運用は単純だが、圧縮しすぎる画像と圧縮が不十分な画像を一部許容する

重要なのは平均スコアと基準達成率の違い

画質値を選ぶ前に、「十分な品質」の意味を定義する必要がある。似て見える次の二つの目標からは、まったく違うエンコーダー設定が導かれる。

  • 平均スコアを目標にする:サンプル全体のSSIMULACRA2平均値を約60にする。
  • 基準達成率を目標にする:画像のおよそ90%でSSIMULACRA2 ≥ 60を達成し、残りは60未満でも許容する。

この二つは同じではない。平均値を使うと、圧縮しやすい画像の高得点で、難しい画像の低得点が相殺される。例えば、半数が75、残り半数が55なら平均は65だが、しきい値60を満たす画像は半分しかない。

大半の画像が許容できる見た目であればよく、少数の不合格を許容できる一般的なWeb画像処理では、基準達成率のほうが有用なことが多い。次の値を測る。

pass_rate = count(scores >= 60) / count(all_scores)

基準未達のスコア分布も記録し、見た目が特に悪い画像を確認する。9%の画像が59点のサンプルと、9%の画像が30点のサンプルは、達成率が同じでも実質的に異なる。

実際のキャリブレーションで分かったこと

探索的に行ったAVIFのキャリブレーションでは、適応的な手順によって画像グループごとに異なる画質値が選ばれ、おおむね30台半ばから40台半ばに分布した。これは予想どおりだった。低い画質値の段階でSSIMULACRA2のしきい値を超える画像を含むグループもあれば、難しいサンプルの一部ではかなり高い画質値が必要になった。

ただし、その適応的なルールは実際の配信要件より厳しかった。各グループで試した画像のほぼすべてがしきい値を超えることを目指し、例外として許す画像はごくわずかだった。難しい画像を保護できる半面、特異なサンプルが一枚あるだけで、グループ全体の画質値が引き上げられることもある。

測定結果を別の目標、つまり約10枚中9枚がSSIMULACRA2で60以上になるという条件で見直すと、AVIFの画質値を39~40程度に固定することが妥当な折衷案として浮かび上がった。この比較では、39は出力サイズの小ささを優先し、40はしきい値に近い画像に多少の余裕を持たせる値だった。これらの値はキャリブレーションデータとその後の分析から導いたものであり、固定画質による全画像の変換も、コレクション全体を対象とした独立検証も実施していない。39と40の正確なファイルサイズ差も確認できていない。

ここから得られる教訓は、AVIFの画質値39や40があらゆる場面で通用するということではない。許容する品質分布を明示し、それに合致するなら固定画質値は合理的な選択になり得るということだ。エンコーダー、画像の構成、色処理、知覚品質のしきい値が違えば、結論も変わり得る。

AVIFの画質値を一度のキャリブレーションで決める方法

  1. 許容する不合格率を決める。例えば、画像の約90%がSSIMULACRA2 ≥ 60を達成することを条件にする。この目標に多少の幅を持たせるのか、公開時に必ず満たす厳格な条件にするのかも決めておく。
  2. 実際の画像群を代表するサンプルを作る。さまざまな画像サイズ、元の形式、グラデーション、鮮明な輪郭、テクスチャ、ディテールの少ない画像を含める。実際の処理対象から無作為抽出し、一部の種類が漏れるなら層化抽出を使う。圧縮済みの1ピクセル当たりバイト数だけでファイルを選ぶ方法は、幅広いサンプルをそろえるための目安にはなるが、知覚品質の面で代表性があることまでは保証しない。
  3. エンコーダーの画質値を狭い範囲で試す。品質値の全範囲を探索するのではなく、目視で許容できそうな設定の近くから数個の候補を選ぶ。例えば、同じ画像について38、39、40、42を比較する。
  4. エンコード処理の条件をそろえる。解像度、画像の向き、色変換、エンコーダーの実装、effort、クロマサブサンプリング、ビット深度、前処理を一定に保つ。透かしを入れる場合は、圧縮後の画像と比較する参照画像にも、同じ透かしと変換を適用しておく必要がある。
  5. スコアとサイズをセットで記録する。候補ごとに60点以上の画像の割合、しきい値未満の分布、同じサンプルに対するエンコード後の総バイト数を計算する。画質値だけから帯域幅の節約量を推測してはいけない。
  6. 不合格画像を目視確認する。顔、文字、細い線、グラデーションなど、用途にとって重要なディテールを見る。拡大表示だけでなく、実際に近い表示サイズでも確認する。
  7. 条件を満たす最も低い画質値を選び、検証する。選択に使っていない別のランダムなホールドアウトサンプルで候補を試す。90%の目標を下回ることの影響が大きいなら、安全余裕を持たせ、標本誤差も慎重に扱う。まれな不合格が許容できるなら、さらに攻めた設定も合理的だ。

一度決めたら、その後の変化を無視してよいわけではない。元画像の傾向、画像サイズ、エンコーダーのバージョン、配信時の変換処理、画質要件が変わったら、小規模なサンプル検証をやり直す。

SharpとSSIMULACRA2による最小限の比較例

以下の簡略化した例では、SharpのAVIFエンコーダーを使い、同じ入力から二つの候補を生成する。これはキャリブレーション方法を示すコード例であり、すべてのプロジェクトで同じオプションや画質値を使うべきだという主張ではない。sharpをインストールしたあと、ESモジュールとして実行する。

import sharp from 'sharp';

const source = 'sample.png';
const encoding = {
  effort: 9,
  chromaSubsampling: '4:4:4',
  bitdepth: 8,
  tune: 'iq',
  lossless: false
};

// Use the same orientation and color conversion in both paths.
await sharp(source)
  .rotate()
  .toColourspace('srgb')
  .png()
  .toFile('reference.png');

for (const quality of [39, 40]) {
  const encoded = `candidate-q${quality}.avif`;
  await sharp(source)
    .rotate()
    .toColourspace('srgb')
    .avif({ ...encoding, quality })
    .toFile(encoded);

  await sharp(encoded)
    .png()
    .toFile(`candidate-q${quality}.png`);
}

次に、デコードした候補を、用意した参照画像と比較する。

ssimulacra2 reference.png candidate-q39.png
ssimulacra2 reference.png candidate-q40.png

実際のテストでは、サンプル画像全体についてこの処理を自動化し、各候補のスコアとファイルサイズを収集する。実運用の処理でリサイズ、切り抜き、オーバーレイなどの変換を行うなら、比較用の参照画像にも同じ処理を適用する。そうしないと、SSIMULACRA2は圧縮劣化だけでなく、変換による差まで一部測定してしまう。

全体で使う画質値が決まれば、本番処理は通常の単一パスのAVIF変換になる。画像ごと、あるいはフォルダーごとに画質値を探索する必要はなくなる。繰り返し試行エンコードするコストはなくなるが、出力画像一枚につき一回のエンコードが必要なことは変わらない。

AVIFの画質値を一つに固定すべきでない場合

固定画質値はトレードオフであって、どんな場合にも最適な解決策ではない。個々の画像に対する品質の下限が契約で定められている場合、見た目の失敗が許されない場合、元画像の特徴に大きな差がある場合、あるいは計算コストや実装の複雑さを増やしてでも全バイトを最適化する価値がある場合は、適応的エンコードが引き続き有効だ。

特に、画像の種類が混在するときは注意したい。写真、単純な図形、細かな文字、すでに圧縮された画像が同じように反応するとは限らない。特定の種類だけが繰り返し受け入れ基準を満たさないなら、すべてに一つの画質値を適用する方法と完全な画像別最適化の中間として、形式や内容ごとに少数のプロファイルを用意するほうが効率的かもしれない。

また、エンコード速度とキャリブレーション速度は分けて考える必要がある。画質値を一つにすれば繰り返しの試験は不要になるが、すべての画像を非常に高いeffortでエンコードすれば、計算負荷は依然として大きい。より低いeffortの設定も有効な最適化手段だが、その場合は再キャリブレーションが必要になる。エンコーダー設定によって、画質値とスコアの関係が変わるためだ。

実践的な指針

SSIMULACRA2は、必ずしもすべての画像に対して本番処理で実行するものではなく、画質値を決めるためのキャリブレーション手段として使えばよい。実際の閲覧条件に合う品質目標を定め、代表的なサンプルを近接した数個の設定でエンコードし、知覚品質のスコアとバイト数の両方を比較する。不合格画像を目視で確認し、許容条件を満たす最も低い画質値を採用する。

ある探索的なキャリブレーションでは、AVIFの画質値39~40程度が妥当な落としどころに見えた。これは特定のしきい値と許容度に基づく判断であり、どこでも使えるプリセットでも、全画像群で検証済みのベンチマークでもない。より一般化できる教訓は単純だ。時間と計算資源に制約があるなら、慎重に検証した固定画質値を使うほうが、個々の画像を際限なく最適化し続けるより実用的な場合が多い。