Quality Ladder:PSNR/SSIM/VMAF 指標の実務的な使い分け
Quality Ladder は品質グレード(例: Q90→Q85→Q80...)を並べ主観しきい値を決める手法。 しかし PSNR / SSIM / (MS-)SSIM / VMAF など客観指標を無批判に閾値化すると “文字が滲むが指標OK” という UX崩壊の盲点 を生みます。本稿は Ladder ツール (/quality-ladder) を軸に 指標を 翻訳可能な意思決定単位 に落とすワークフローを提示します。
要点(TL;DR)
- 指標単独ではなく PSNR + SSIM(もしくはMS-SSIM) の 二軸 を最小構成。
- 文字/線/UI領域をマスク し局所再計算→数値と主観の乖離区間を可視化。
- ラダーは Δサイズ vs 知覚差 の折れ点(拐点)で止める。最終1段は余裕バッファ。
- 初回合意した“品質境界スクリーンショット” を 社内ゴールデンセット としてGit管理。
1. 指標の特性マップ
PSNR は単純で高速、SSIM は構造類似、MS-SSIM は複数スケールでの視覚特性近似、VMAF は動画文脈で重特徴量。 静止画バッチ比較なら PSNR + (MS-)SSIM の2指標で 80% 以上の妥当性が得られるケースが多いです。
- PSNR: ノイズ/量子化誤差に敏感。局所ぼやけは拾いにくい。
- SSIM: 構造差異/エッジ保持を評価。極端な色ずれは過小評価されることも。
- MS-SSIM: スケール混合でグラデ/大域構造も反映。
- VMAF(静止画): 計算コスト大。CI秒単位要求では非現実的な場面多。
import sharp from 'sharp';
import { getSSIM } from 'ssim.js'; // npm i ssim.js
// PSNR を計算 (単純二乗誤差から導出)
async function psnr(a: Buffer, b: Buffer) {
if (a.length !== b.length) throw new Error('dimension mismatch');
let mse = 0;
for (let i = 0; i < a.length; i++) {
const d = a[i] - b[i];
mse += d * d;
}
mse /= a.length;
if (mse === 0) return Infinity;
const PIXEL_MAX = 255;
return 10 * Math.log10((PIXEL_MAX * PIXEL_MAX) / mse);
}
async function main() {
const ref = sharp('ref.png').raw().ensureAlpha().toBuffer({ resolveWithObject: true });
const cand = sharp('q85.png').raw().ensureAlpha().toBuffer({ resolveWithObject: true });
const [r, c] = await Promise.all([ref, cand]);
const psnrValue = await psnr(r.data, c.data);
const { mssim } = getSSIM(
{ data: r.data, width: r.info.width, height: r.info.height },
{ data: c.data, width: c.info.width, height: c.info.height },
{ ssim: 'fast' }
);
console.log({ psnrValue, ssim: mssim });
}
main();
簡易 PSNR / SSIM 計測サンプル。実運用ではガンマ補正後(sRGB → linear)に計算し、アルファ分離や領域マスクを適用します。
2. Ladder 取得の標準手順
- 代表 8〜12 枚を カテゴリ(写真/UI/イラスト) 別にピック。
- 各画像で Q=95→90→85... と書き出しサイズと指標を記録。
- 知覚差が初めて“気になる”段の 1つ上 を候補品質に。
- UI文字/線画マスクを適用し再測定→差異が再拡大するなら品質1段戻す。
3. 指標レンジの経験値
状況 | PSNR(dB) | SSIM |
---|---|---|
写真公開許容境界 | ~38 | 0.96+ |
UI文字劣化前 | 40+ | 0.985+ |
過圧縮兆候 | ≤36 | <0.94 |
※ 数値は社内ベンチ一例。自社の表示サイズ/画面PPIで再キャリブレーションを推奨。
4. CI への組込み
差分生成→最小許容PSNR/SSIM下回り or 文字マスク領域で閾値割れ → PR 失敗。可視化ログは /quality-ladder の静的キャプチャをアーティファクト保存。
name: image-quality
on: [pull_request]
jobs:
ladder:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Compute metrics
run: node scripts/compute-quality-ladder.mjs --out report.json
- name: Validate thresholds
run: node scripts/validate-thresholds.mjs report.json thresholds.json
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: ladder-report
path: report/*
5. よくある失敗
- 1画像で閾値決め → 他カテゴリに外挿できず破綻。
- 指標改善を無限追求 → 体感差がゼロ域でコスト浪費。
- ガンマ不一致(sRGB変換漏れ) → 指標と実表示差が乖離。
6. まとめ
Quality Ladder は “指標を鵜呑みする” ためでなく “主観境界を数値で再現” する枠組みです。PSNR+SSIMのペアと領域マスクで再現性を確立し、変更の安全性を CI で自動担保しましょう。
自社に最適化する際は (1) 代表セット更新頻度を四半期ごとに定義 (2) 閾値変更は PR テンプレで理由必須化 (3) UI 文字領域マスク PNG をリポジトリ管理 の 3点を先に制度化するとブレが激減します。
FAQ(よくある質問)
1画像形式の基本方針は?(写真/スクショ/透過)
2圧縮しても画質を落とさないコツは?
srcset/sizes
を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。