gazou-compressor.jp

Quality Ladder をCIに組み込む最小パイプライン

品質劣化を レビュー者の主観疲弊 に委ねると境界が徐々に緩む“品質ドリフト”が起きます。本稿は Quality Ladder ツールで得る指標を GitHub Actions 上で自動判定し PR をブロックする最小構成を示します。

目的明確化
目標は“過圧縮を未然に検知する”ことであり“常に最高PSNRを目指す”ことではない。高速性と再現性を優先。

要点 (TL;DR)

1. ディレクトリ設計

repo/
  ladder/
    golden/          # 代表元画像
    masks/           # 領域マスク PNG (text/ui 等)
    baseline.json    # 各画像のベース PSNR/SSIM
    thresholds.json  # カテゴリ別下限
    reports/         # CI生成 (差分 PNG / JSON)
    scripts/         # compute-quality-ladder.mjs 等
LFS を避ける理由
golden / baseline / thresholds は頻繁に差分レビューしたいテキストや画像です。Git LFS 化するとレビュー粒度が落ちるため通常管理を推奨。

2. Actions ワークフロー骨子

  1. チェックアウト + Node セットアップ
  2. キャッシュ復元 (node_modules, ladder/.cache)
  3. 差分検知: git diff --name-only origin/main...HEAD | grep assets/
  4. 差分対象のみ再エンコード → PSNR/SSIM 計測
  5. thresholds.json 比較 → 下回り箇所を markdown / png 化
  6. 失敗時 artifact + Slack 通知
name: ladder
on: [pull_request]
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci --ignore-scripts
      - name: Compute metrics
        run: node ladder/scripts/compute-quality-ladder.mjs --changed > report.json
      - name: Validate
        run: node ladder/scripts/validate-thresholds.mjs report.json ladder/thresholds.json
      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: ladder-report
          path: ladder/reports

3. 閾値設計

カテゴリ別に初期値を置き 2 週間の False Positive / Negative ログで微調整。頻繁な改変は境界の流動化を招くため“理由付き PR”でのみ変更。

{
  "photo": { "psnr": 38, "ssim": 0.96 },
  "ui":    { "psnr": 40, "ssim": 0.985 },
  "illust":{ "psnr": 37, "ssim": 0.95 }
}

4. レポート自動化

失敗時は品質低下画像タイル + 指標差分 (ΔPSNR, ΔSSIM) を markdown テーブル化しレビューを 30s 以内に。

// ladder/scripts/validate-thresholds.mjs (抜粋)
import fs from 'node:fs';
const thresholds = JSON.parse(fs.readFileSync(process.argv[3],'utf8'));
const report = JSON.parse(fs.readFileSync(process.argv[2],'utf8'));
const failed = [];
for (const r of report.results) {
  const th = thresholds[r.category];
  if (r.psnr < th.psnr || r.ssim < th.ssim) failed.push(r);
}
if (failed.length) {
  const lines = failed.map(f => '|'+f.file+'|'+f.psnr.toFixed(2)+'|'+f.ssim.toFixed(4)+'|'+f.category+'|').join('\n');
  fs.writeFileSync('ladder/reports/fail.md', '|file|psnr|ssim|cat|\n|---|---|---|---|\n'+lines);
  process.exitCode = 1;
}

5. 改善ループ

6. まとめ

CI 化で品質境界を “口頭合意” から “再現可能なコード” へ。履歴は Git / PR に残り説明責任を担保。

公開日: 2025-09-06編集: gazou-compressor.jp
次のステップ
Slack 通知・メトリクス時系列化・文字領域マスク導入で MTTR をさらに短縮。
FAQ

FAQ(よくある質問)

1画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド スクショ最適化 を参照してください。
2圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、srcset/sizes を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。
3CLSを悪化させない画像の置き方は?
すべての画像に width/height(または親に aspect-ratio)を与え、広告・埋め込みは 予約サイズを先に確保します。詳しくは CLSゼロ設計ガイド を参照。

gazou-compressor.jp 編集部

画像圧縮・変換・背景除去などの実践テクニックと、Webで“速く・軽く・崩さない”ためのノウハウを発信しています。

関連記事

トピック/更新日の近いコンテンツ