Color Heatmap の疑似ΔEをどう読むか:解釈と閾値設計
ヒートマップは差分の “量” 以上に “質” を早期把握できます。擬似ΔEの階級を閾値分類し 無駄な再エンコード を減らす指針を提示。
TL;DR
- ΔE <5 は多くの閲覧環境で知覚困難
- 5–10 はUI要素近傍なら注視
- 10+ は再検討候補、面積比 >=0.5% で要調査
- 閾値はコンテンツ種別ごとに差別化
1. 階級設計
初期設定は 過検知回避 の保守的レンジ。ΔE は測色環境やガンマ差で揺らぐため境界サンプルが多い場合はヒストグラム中央値で再調整します。
class,deltaE,meaning,first_action
ok,<5,知覚困難,放置
watch,5-10,局所で気付く可能性,追加観察
warn,10-15,要原因分析,差分領域抽出
fail,>15,再生成検討,元画像/設定再確認
1.5 判定ユーティリティ
環境 / コンテンツ種別 (写真 / UI アイコン / 透過PNG) で閾値を差し替え可能にしテスト容易性を確保。
export interface Thresholds { ok:number; watch:number; warn:number; }
export function classify(deltaE:number, t:Thresholds={ ok:5, watch:10, warn:15 }){
if (deltaE < t.ok) return 'ok';
if (deltaE < t.watch) return 'watch';
if (deltaE < t.warn) return 'warn';
return 'fail';
}
2. 面積比の使い方
1px の ΔE15 はノイズ。階級 × 面積 の重み合計で “総劣化スコア” を算出し孤立点を無視します。
interface Region { area:number; deltaE:number }
function weight(deltaE:number){ if (deltaE<5) return 0; if (deltaE<10) return 1; if (deltaE<15) return 3; return 6; }
export function aggregateScore(rs:Region[]){
return rs.reduce((acc,r)=>acc + r.area * weight(r.deltaE),0);
}
2.5 領域抽出 (擬似ΔE→ポリゴン)
4近傍ラベリングで高ΔE 領域を抽出し閾値未満小領域を形態学的膨張後にマージ。
function label(dE:number[][], th=10){
const h=dE.length, w=dE[0].length; const visited:boolean[][]=[]; const regions:number[]=[];
for(let y=0;y<h;y++){ visited[y]=[]; }
for(let y=0;y<h;y++) for(let x=0;x<w;x++) if(!visited[y][x] && dE[y][x]>=th){
let stack=[[x,y]], area=0; visited[y][x]=true;
while(stack.length){ const [cx,cy]=stack.pop()!; area++; for(const [dx,dy] of [[1,0],[-1,0],[0,1],[0,-1]]){ const nx=cx+dx, ny=cy+dy; if(nx>=0&&ny>=0&&nx<w&&ny<h&&!visited[ny][nx]&&dE[ny][nx]>=th){ visited[ny][nx]=true; stack.push([nx,ny]); } } }
regions.push(area);
}
return regions.map(a=>({ area:a }));
}
3. ヒストグラム蓄積
CIで ΔE ヒストグラム CSV を集計し warn/fail 比率トレンド を可視化。増加はライブラリアップデートやビルド設定逸脱のシグナル。
# 差分ツール出力ヒストを統合
awk -F, 'NR>1{b[$1]+=$2} END{for(k in b) print k","b[k]}' artifacts/*-hist.csv | sort -n > merged-hist.csv
3.5 CI 逸脱判定
7日移動平均との差で fail 比率が +0.2pp 超 → Warning。
interface Day { date:string; failRatio:number }
export function isSpike(history:Day[], today:Day){
const recent = history.slice(-7);
const avg = recent.reduce((a,b)=>a+b.failRatio,0)/Math.max(1,recent.length);
return today.failRatio - avg > 0.002; // 0.2pp
}
関連ツール / 補助
4. よくある誤判定
- 透過領域 (α=0) を差分計上
- ガンマ未統一 (sRGB 変換前後混在)
- 縮小アルゴリズム差 (Lanczos vs Bicubic)
- ICC プロファイル不一致
5. まとめ
絶対値より 階級 × 面積比 + トレンド の多軸評価で過剰反応を抑え劣化兆候を早期捕捉。
公開日: 2025-09-06編集: gazou-compressor.jp
FAQ
FAQ(よくある質問)
1画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド と スクショ最適化 を参照してください。
2圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、
srcset/sizes
を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。