画像品質バジェットと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: 参考値としてのみ。閾値での判定利用は非推奨
色管理と併用:
- 入力ICCを尊重し、評価は配信色空間(通常 sRGB)に統一。
- P3→sRGB の落とし込み過程でのクリップ/トーンマップ差は、数字で良でも目視でNGになることあり(関連記事: P3→sRGB 変換で崩れない色管理 実務ガイド 2025, HDR→sRGBトーンマッピング実務 2025 — 破綻させない配信フロー)。
設計(アーキテクチャ)
- ゴールデンセット(代表画像)を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 配線、失敗時の自動処理
- セキュリティ/リーガル: 著作権/編集権限の監査(関連記事: 報道・エディトリアル画像の権利と安全配信 2025 — 顔/未成年/機微情報)
チェックリスト
- [ ] ベースラインを保存
- [ ] 閾値は資産ごとに最適化
- [ ] CIゲートが有効
追加チェック:
- [ ] 色管理の統一(sRGB)と ICC の扱いが文書化されている
- [ ] UI/ロゴの 4:4:4/ロスレス方針が決まっている
- [ ] ルート/ページ別の容量バジェットが設定されている
- [ ] ベースライン更新の承認フロー/監査ログがある
よくある質問(FAQ)
- Q. 指標の値が良いのに、目視で違和感がある
- A. 色域/トーンマッピングの差やサブサンプリングの影響が疑われます。P3→sRGB と 4:4:4 の評価を見直してください
- Q. 閾値をどこに置けば良い?
- A. まずは厳しめに置き、Warning 継続や誤検知が多ければ段階的に緩める。ゴールデンセットの網羅性を優先
- Q. 画像数が多く計算が重い
- A. 代表 N 枚をゴールデンに、残りはサンプリング + 容量バジェットでカバーするハイブリッドが現実的