画像品質バジェットとCIゲート 2025 — 破綻を未然に防ぐ運用

公開: 2025年9月23日 · 読了目安: 5 · 著者: Unified Image Tools 編集部

TL;DR

  • 閾値は用途別(サムネ/ヒーロー/高精細)で段階設定
  • 指標は補助—最終判断は目視の運用を確立
  • バジェット逸脱時はロールバック/再エンコードの自動フロー

内部リンク: 画像の画質評価指標 SSIM/PSNR/Butteraugli 実践ガイド 2025, 画像圧縮 完全戦略 2025 ─ 画質を守りつつ体感速度を最適化する実践ガイド, P3→sRGB 変換で崩れない色管理 実務ガイド 2025, HDR→sRGBトーンマッピング実務 2025 — 破綻させない配信フロー, エッジ時代の画像配信最適化 CDN 設計 2025

背景

現場では「いつの間にか画質が落ちた/サイズが膨らんだ」が起きがちです。CIに予算と指標を組み込むことで、静かに進む劣化を止めます。

スコープ設計(どの資産を守るか)

  • クラス分け: サムネ(一覧)/ ヒーロー(LP)/ UI(ロゴ/アイコン)/ 商品詳細(ズーム対応)
  • 目標の違い: サムネは転送量/キャッシュ効率、ヒーローは視覚品質、UI はピクセル厳密性が最優先
  • それぞれに「容量バジェット」「品質ゲート」「色管理ポリシー」を定義

例)

  • サムネ: 平均 ≤ 25KB(AVIF/WebP)、SSIM ≥ 0.98、LPIPS ≤ 0.08
  • ヒーロー: 平均 ≤ 180KB、Butteraugli ≤ 1.2、色域は sRGB に正規化
  • UI/ロゴ: 必要に応じ Lossless(WebP/PNG)、サブサンプリング禁止(4:4:4)

指標の選び方(いつ/どれを使うか)

  • SSIM/MS-SSIM: 早い/安定、テクスチャに強いがカラー微妙差は見逃すことあり
  • LPIPS: 知覚寄り、差が小さい場合の感覚と近い。計算コストは上がる
  • Butteraugli: 色ズレ検出に強い。ICC/色域差の顕在化に有効
  • PSNR: 参考値としてのみ。閾値での判定利用は非推奨

色管理と併用:

設計(アーキテクチャ)

  • ゴールデンセット(代表画像)をGitでバージョン管理し、/testdata/golden/ のように固定パス化
  • PR で生成物(/run/_/generated/…)と比較し、差分メトリクスを集計
  • 閾値超過で CI を fail。ラベル/コメントで「どの画像が、どの指標で、どれだけ超過」を可視化
  • ベースライン更新は、専用の Approver(デザイナ/リード)承認でのみ許可(権限ガード)

運用

ゴールデンセット運用

  • 収集: 代表的な肌/テクスチャ/細線/グラデ/低照度/高彩度を網羅
  • 更新: レイアウトやブランディング刷新時のみ。通常の画質議論ではベースライン据え置き
  • 監査: 誰が/いつ/なぜ ベースラインを更新したかを CHANGELOG に記録

可視化/アラート

  • 各クラスの時系列(平均/95パーセンタイル)と、直近 PR の偏差をダッシュボード表示
  • 閾値近傍は Warning、超過で Block。継続的な Warning は「閾値かプリセット」見直しの合図

人間の目の位置づけ

  • メトリクス合格でも、ロゴの境界滲み/肌の階調破綻などは見逃す場合あり→ 代表パターンだけ目視を義務化

CI 実装パターン(例)

GitHub Actions の例(擬似):

name: image-quality-gates
on: [pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm run build:images # 生成物を作る
      - run: node scripts/ci/collect-metrics.mjs --golden ./testdata/golden --actual ./run/_/generated --out ./run/_/metrics.json
      - run: node scripts/ci/enforce-budgets.mjs --config ./scripts/ci/budgets.json --metrics ./run/_/metrics.json

budgets.json のイメージ:

{
  "thumbnail": { "sizeKB": 25, "ssim": 0.98, "lpips": 0.08 },
  "hero": { "sizeKB": 180, "butteraugli": 1.2 },
  "ui": { "lossless": true, "chroma": "4:4:4" }
}

差分生成/メトリクス収集(擬似コード)

import { readFileSync } from 'node:fs';
// 画像のペアを列挙して SSIM/LPIPS/Butteraugli を実行(外部ツール呼び出しでもOK)
type Pair = { golden: string; actual: string; class: 'thumbnail' | 'hero' | 'ui' };
function evaluate(pairs: Pair[]) {
  return pairs.map(p => ({
    ...p,
    sizeKB: bytes(readFileSync(p.actual)) / 1024,
    ssim: ssim(p.golden, p.actual),
    lpips: lpips(p.golden, p.actual),
    butteraugli: butter(p.golden, p.actual)
  }));
}

バジェット/ゲートの考え方(容量×品質×色)

  • 容量: ルート/ページ/一覧単位で上限(合計KB)を設け、PRで差分が+X%を超えたらブロック
  • 品質: 画像クラスごとに SSIM/LPIPS/Butteraugli のしきい値を設定
  • 色: sRGB で統一。P3 や HDR ソースは配信前に落とし込み(トーンマッピング含む)を固定手順化

ロールバック/自動修復

  • 失敗時に自動で「プリセット B に再エンコード」し、差分が許容内なら提案コメントにアーティファクト画像を添付
  • 明らかなリグレッションは自動で quality -5 などの候補を提示。最終決定は人手
  • ダークテーマでのみ破綻する等、環境依存の事象は A/B 設計(関連記事: 画像のA/Bテスト設計 2025 — 画質・速度・CTRを同時に最適化)で検証

ガバナンス/役割分担

チェックリスト

  • [ ] ベースラインを保存
  • [ ] 閾値は資産ごとに最適化
  • [ ] CIゲートが有効

追加チェック:

  • [ ] 色管理の統一(sRGB)と ICC の扱いが文書化されている
  • [ ] UI/ロゴの 4:4:4/ロスレス方針が決まっている
  • [ ] ルート/ページ別の容量バジェットが設定されている
  • [ ] ベースライン更新の承認フロー/監査ログがある

よくある質問(FAQ)

  • Q. 指標の値が良いのに、目視で違和感がある
    • A. 色域/トーンマッピングの差やサブサンプリングの影響が疑われます。P3→sRGB と 4:4:4 の評価を見直してください
  • Q. 閾値をどこに置けば良い?
    • A. まずは厳しめに置き、Warning 継続や誤検知が多ければ段階的に緩める。ゴールデンセットの網羅性を優先
  • Q. 画像数が多く計算が重い
    • A. 代表 N 枚をゴールデンに、残りはサンプリング + 容量バジェットでカバーするハイブリッドが現実的

関連記事

関連ツール

関連記事