P3→sRGB 変換時の肌色保護チューニング:ガンマ再マッピングとLab距離制御
P3 ソースを sRGB にナイーブ変換すると 肌色クラスタが低彩度化 しフラットな印象となることがあります。選択的補正で利用者の自然感覚を守ります。
コア思想
Lab空間距離最小化を最適目標とし “肌色クラスタのみ局所的に彩度保持” する条件付きマッピング。
1. 問題構造
Wide → Narrow 変換は gamut clipping / 彩度圧縮 / 相対明度変化 を伴い肌色クラスタは少しの彩度損失でも “血色の欠乏” と認知されやすい。
# Lch skin cluster (凡例)
L: 55-75
C: 20-38
h: 25-55 (黄〜赤橙)
2. データセット層別
顔向き (正面/斜め)、照度 (昼/低照度)、肌トーン (明/中/暗) を 3 軸層別し偏りを抑制。
{"faces":{"frontal":60,"angled":40},"illumination":{"day":70,"low":30},"tone":{"light":30,"medium":40,"dark":30}}
3. 肌色検出
YCbCr 矩形閾値は高速だが ライティング差 に脆弱。OKLab で Chroma/Hue 再確認し木材/布の偽陽性を除外。
// 1. YCbCr で肌色候補抽出 (簡易)
function skinMask(ycbcr){
const [Y,Cb,Cr] = ycbcr;
return (Cb > 90 && Cb < 120 && Cr > 140 && Cr < 170);
}
// OKLab 二次フィルタ
function refineOkLab(L,C,h){
return (L>0.45 && L<0.85) && (C<0.22) && (h>0.05 && h<0.22);
}
4. 選択的マッピング
彩度保持は 過補正による蛍光色化 を避けるため EOTF (ガンマ) 前で行い非線形性の影響を最小化。
// 2. 選択的彩度補正 (HSLで例示)
function selectiveSaturation(h,s,l, skin){
if(!skin) return [h,s,l];
const target = 0.92; // 目標彩度係数
const adjusted = s + (target - s) * 0.4; // 緩和
return [h, Math.min(1, adjusted), l];
}
// 3. P3→sRGB ガンマ/マトリクス + 補正
const p3Linear = toLinearP3(pixelP3);
let srgb = p3ToSrgbMatrix(p3Linear);
// クリップ前に肌色なら彩度再注入
if (skinMask(rgbToYCbCr(pixelP3))) {
srgb = injectSaturationLab(srgb, 0.05);
}
// Overshoot ガード
if (labDeltaE(origSkin, tunedSkin) > 2.0) rollbackPixel();
5. 検証 & 指標
初期しきい値は “skin delta は非肌領域 delta の 0.7× 以下” を採用し AB 主観で再調整。
# 4. 検証 (Lab距離統計)
for img in dataset:
ref = p3_to_srgb_plain(img)
tuned = p3_to_srgb_tuned(img)
d_skin = mean(LabDeltaE(ref.skin_pixels, tuned.skin_pixels))
d_rest = mean(LabDeltaE(ref.non_skin, tuned.non_skin))
collect(d_skin, d_rest)
# 5. 指標ダッシュボード
skin_delta_p95, skin_delta_mean, non_skin_delta_mean, overshoot_rate, clip_rate
// 指標正規化例
const z = (xs:number[],v:number)=> (v-mean(xs))/(std(xs)||1);
report.skin_delta_z = z(allSkinDeltas, currentSkinDelta);
6. CI 差分監視
plain 変換と tuned 変換の Lab 差分統計を CI で比較し skin_delta_z > 1.5 なら失敗。
node scripts/skin-delta.js --sample sample.json --out tuned.json
node scripts/skin-compare.js base.json tuned.json --z-th 1.5
7. 主観 AB テスト
不自然率 (tuned 不採択 / 総票) 5% 未満を合格ラインに設定。Clopper-Pearson 区間で信頼区間を可視化。
function ci95(k:number,n:number){/* binomial CI */}
8. FAQ
- 過補正? → overshoot_rate をCIでグラフ化し閾値超で係数減
- 高ノイズ? → SNR 低画像は skinMask スキップ
- 顔以外肌色? → 顔ランドマーク併用で誤検出低減
9. まとめ
選択的肌色保持は “知覚品質の一貫性” を守る局所最適層。CI 計測 + 回帰検知 + 主観 AB で過補正を防ぎつつ改善を継続。
関連ツール
公開日: 2025-09-06編集: gazou-compressor.jp