AVIFの適応的クロマサブサンプリング:4:2:0/4:2:2/4:4:4自動選択でテキスト滲みと容量を両立
AVIFで UI/テキストを含む複合画像を 一律4:2:0 にすると文字滲みや色縁が発生します。Adaptive Chroma は 領域ごとに最小限だけ4:4:4を適用 し容量を押さえる戦略です。
- 勾配+彩度変化で 4:4:4 必要領域を抽出
- 中間は 4:2:2、残り 4:2:0
- PSNR/SSIM/bytes をCI比較し回帰検知
1. なぜ領域別か
全フレーム/全領域4:4:4は容量コストが高く Adaptive Q と併用時にレイテンシを増加させます。領域限定で知覚差損が集中する テキスト/細線/肌色境界 を守ります。
2. 領域検出
// 1. エッジ/彩度勾配検出 (pseudo)
for (const block of blocks){
const g = sobel(block.luma); // 輝度勾配
const sc = saturationGradient(block.rgb);
block.edgeScore = 0.6*g + 0.4*sc;
}
// 2. 閾値判定 -> ターゲットサンプリングレベル
const EDGE_T = 0.42;
for (const block of blocks){
if (block.edgeScore > EDGE_T) block.target = '444';
else if (block.edgeScore > EDGE_T*0.6) block.target = '422';
else block.target = '420';
}
3. ブロックマップ平滑化
// 3. 小領域マージ (8x8未満と孤立)
mergeSmallComponents(blocks, { minArea:4 });
smoothIsolated(blocks);
孤立ノイズは4:4:4メリットが乏しいのでマージ。塗り潰し (flood fill) 後に面積閾値で再評価します。
4. エンコード分割
// 4. サブサンプリングごとにタイル分割 AVIFエンコード
const byMode = groupBy(blocks, b=>b.target);
for (const mode of ['444','422','420']) {
const subset = byMode[mode];
if (!subset?.length) continue;
encodeAvif(subset, { chroma: mode });
}
タイル単位の再構築が困難な実装では 4:4:4レイヤを別エンコードしアルファ合成 という近似も選択肢です。
5. モデル設計
初期段階は 単純加重スコア で十分です。精度拡張時のみ軽量 GBDT / tiny CNN を検討。推論コスト (<0.8ms/MPix) を守ることが最優先。
// 領域特徴量抽出 (拡張例)
for (const block of blocks){
block.features = {
edge: sobel(block.luma).mean(),
satGrad: saturationGradient(block.rgb),
contrast: localContrast(block.luma),
entropy: shannon(block.luma)
};
}
// シンプル線形閾値を学習 (擬似)
function classify(f){
const s = 0.9*f.edge + 0.7*f.satGrad + 0.4*f.contrast + 0.2*f.entropy;
if (s > 1.15) return '444';
if (s > 0.75) return '422';
return '420';
}
6. 評価/指標
テキスト/細線マスク (GT) と推定 4:4:4 領域の F1 が 0.9 付近なら多くのUIで視覚破綻は抑制可能。帯域削減率 (full444比) との二軸を可視化し妥協点を選びます。
# 評価ベンチ (macro F1 / 帯域)
python eval.py --pred adaptive.map --gt text_mask.png --metrics f1,precision,recall
node scripts/size-compare.js adaptive.out 444.out
7. 運用/監視
分布変化 (例: 444割合が月次 +8pp) は検出ルール改悪 or 新UI型登場を示唆。メトリクスを時系列で保持し 割合ドリフト をアラート化。
# 運用メトリクス (Prometheus text)
adaptive_chroma_blocks_total{mode="444"} 1820
adaptive_chroma_blocks_total{mode="422"} 640
adaptive_chroma_blocks_total{mode="420"} 4100
8. 失敗パターン
- 過学習: テキストフォント特化し新フォントで recall 低下
- 閾値硬すぎ: 4:4:4面積が減り文字色縁ぼやけ
- 並列過多: ワーカー立ち上げオーバーヘッドで総時間増
# 失敗パターン再昇格ワークフロー
node scripts/chroma-analyze-drift.js --since 30d --edge-miss-th 0.04
9. CI検証
# CI: 代表画像でクロマ自動 vs フル4:4:4 を比較
node scripts/chroma-adaptive.js --image ui.png --out adaptive.json
node scripts/encode-avif.js --image ui.png --chroma 444 --out full444.json
node scripts/metrics-compare.js adaptive.json full444.json \
--psnr-threshold -0.15 --ssim-threshold -0.002
差分が閾値超過した領域のみ4:4:4へ再昇格する 適応しきい値再学習 を週次で行うと安定します。
10. FAQ
- 4:2:2は必須? → テキスト/写真混在UIで折衷帯を作ると artifacts が減少
- 境界色ズレ? → クロマ補間を Lanczos から Bicubic に変更しハロー抑制
11. まとめ
Adaptive Chroma により “必要な所だけ4:4:4” を実現し、従来4:4:4固定より容量を 8〜18% 削減しながら文字滲みを解消できます。