# 評価仕様 (Evaluation Specification)

`sm-evaluate` CLI が担う **シミュレーション結果 / 実測 PNG の照明均一性評価** の方法論を定義する．本書はアルゴリズム・物理動機・規約をまとめ，実装の根拠（why）と入出力（what）を記述する．評価設定 YAML のスキーマ詳細は [SPEC_02_シーン定義仕様](SPEC_02_シーン定義仕様.md) の `evaluation` セクションを参照．

関連:

- 評価結果の歴史: [TEST_01_キャリブレーション検証履歴](../06_TEST/TEST_01_キャリブレーション検証履歴.md)
- 物理シーン仕様: [SPEC_03_基準設計の物理構成](SPEC_03_基準設計の物理構成.md)
- 評価 YAML スキーマ: [SPEC_02_シーン定義仕様 §evaluation](SPEC_02_シーン定義仕様.md)
- 実装エントリ: `src/sm_tias_lightsim/cli/evaluate.py`, `src/sm_tias_lightsim/evaluation/`, `src/sm_tias_lightsim/uniformity.py`

## 概要 (Overview)

シミュレーション結果（`output/<run>/luminance.npy` または `render_uint8.png`）と実測 PNG (`reference_measurement/.../*.png`) を **同一の評価パイプライン**に通し，無次元の均一性指標（CoV，勾配 %，中心/周辺比 等）を算出する．シミュと実測を比較可能にする鍵は **OETF 順方向適用** で，シミュの linear ドメインを実機の sRGB 符号化ドメインに揃える操作である（後述）．

設計目的は SmTIAS-Evaluation との **数値整合性**（実測 PNG に対し小数点 14 桁レベルで一致確認済み，[TEST_01](../06_TEST/TEST_01_キャリブレーション検証履歴.md) 既知差分 #9）と，シミュ⇄実測の **公平な比較**．

## 評価パイプライン (Evaluation Pipeline)

```text
入力 (PNG / luminance.npy)
    │
    ▼  uint8 PNG → 0..255 配列, luminance.npy → float32 配列のまま
[1] 読み込み
    │
    ▼  Rec.709 加重和 Y = 0.2126R + 0.7152G + 0.0722B (RGB 入力時)
[2] luma 変換
    │
    ▼  ROI 設定 (image_pixel 型) に従い (x, y, width, height) 領域を切り出す
[3] ROI 抽出
    │
    ▼  normalization.enabled = true のとき: linear ドメインで mean を target_mean に揃える線形ゲイン
[4] mean 正規化 (任意・linear モード用、OETF と排他)
    │
    ▼  oetf.enabled = true のとき: 標準 sRGB OETF を順方向適用 + 実測 mean アンカー
[5] OETF 順方向適用 (任意・シミュ用、normalization と排他)
    │
    ▼  (任意) quantize_uint8: float → uint8 量子化
[6] 量子化
    │
    ▼  CoV, std, max-min, mean, max, min を計算
[7] 全体指標
    │
    ▼  3 ゾーン分類 (center/middle/periphery) → 中心/周辺比，勾配 %
[8] ゾーン指標
    │
    ▼  20 ビンの放射状プロファイル
[9] 動径指標
    │
    ▼  metrics.csv, figures/{luminance_map, histogram, radial_profile, zone_map}.png
[10] 出力
```

各ステージの実装は以下のモジュールに分かれる:

| ステージ | モジュール |
| --- | --- |
| [1] 読み込み | `cli/evaluate.py`（PNG: PIL，npy: numpy） |
| [2] luma 変換 | `uniformity.py`（Rec.709 加重和） |
| [3] ROI 抽出 | `evaluation/roi.py` |
| [4] mean 正規化 | `evaluation/normalize.py` |
| [5] OETF | `evaluation/oetf.py` |
| [6-8] 指標 | `uniformity.py` + `evaluation/zones.py` |
| [9] 動径 | `evaluation/radial.py` |
| [10] 可視化 | `evaluation/visualize.py` |

## ROI 仕様 (Region of Interest)

評価板の **撮影範囲内の有効領域** を矩形で切り出す．評価設定 YAML の `roi` セクションで指定する．

### `image_pixel` 型

実測 PNG の座標系（左上原点，X 右，Y 下）で **絶対ピクセル座標**を直接指定する．シミュ画像と実測画像で **物理対応する同じ領域**を切り出すために，シミュ側の ROI は実測 ROI の物理対応関係から決定する．

```yaml
roi:
  type: image_pixel
  x: 24          # 左上 X
  y: 37          # 左上 Y
  width: 464
  height: 450
```

### 実機 ⇄ シミュの物理対応（既存値）

| ROI 名 | 用途 | x, y, w, h |
| --- | --- | --- |
| `configs/eval_baseline.yaml` | シミュ 512×512 | 24, 37, 464, 450 |
| `configs/eval_real.yaml` | 実機 1500×1500 ベース | reference_measurement の `roi_config.json` 参照 |

シミュ ROI と実機 ROI は **物理対応済み**（評価板の同じ XY 領域）．シミュの orthographic センサと実機カメラの向き変換は [SPEC_03 画像 ↔ 物理 の向き](SPEC_03_基準設計の物理構成.md#画像--物理-の向き検証済み) を参照．

## OETF 順方向適用の仕様 (Forward OETF Application)

**最重要セクション**．シミュ linear 結果と実測 sRGB PNG を直接比較すると CoV や勾配の絶対値は不一致になる．これを **データに合わせて回す係数ゼロ**で解消するのが OETF 順方向適用．

### 物理動機

- 実機 PNG は ISP 内で **sRGB OETF (IEC 61966-2-1)** が掛かった非線形符号化値．Camera2 `YUV_420_888` → BT.601 は定義上ガンマ済み R'G'B' 上の量．
- シミュは Mitsuba の **linear 放射輝度**．
- **無次元指標 (CoV / 勾配 / C/P) は線形変換に対してのみ不変**．非線形 OETF を介すと値が変わる．
- 両者を同じドメイン（gamma 空間 luma）に揃えれば，「物理が同じなら指標も同じ」が成り立つ．

### 標準 OETF（IEC 61966-2-1）

```text
        ┌─ 12.92 · L                       (L ≤ 0.0031308)
V(L) = ─┤
        └─ 1.055 · L^(1/2.4) − 0.055       (L  > 0.0031308)
```

`L` は linear（0..1 正規化），`V` は sRGB 符号化値（0..1）．実装は `evaluation/oetf.py` の `apply_srgb_oetf()`．

### 露出アンカー（mean 一致）

露出（線形ゲイン）は AE が制御する量で，シミュ側で任意に決められない．**実測 ROI mean に対数二分でアンカー**する：

1. シミュ linear ROI 全体に係数 `α` を掛ける
2. OETF を順方向適用し uint8 量子化
3. 量子化後 mean が実測 mean (`146.7146`) に一致する `α` を対数二分で求める
4. 線形ゲインなので **形状指標 (CoV / 勾配 / C/P) は α に対し不変**．データに合わせて回す自由度はゼロ．

### 二重適用禁止規則

- **実測 PNG には絶対に OETF を適用しない**．ISP で既に sRGB 符号化されているため二重適用になる．
- `configs/eval_real.yaml` は `oetf` セクション無し（無効）．`configs/eval_baseline.yaml` は `oetf.enabled: true`（シミュ用）．
- スキーマ詳細は [SPEC_02 §evaluation](SPEC_02_シーン定義仕様.md) 参照．

### luma vs luminance と演算順序の整合

実測 SmTIAS-Evaluation の「輝度値」は厳密には **Rec.709 luma Y'**（ガンマ済み成分への加重和 `Y' = 0.2126R' + 0.7152G' + 0.0722B'`，ガンマを戻さない）．シミュ側は linear luminance に直接 OETF を当てている．OETF は非線形なので一般に `Σwᵢ·f(xᵢ) ≠ f(Σwᵢ·xᵢ)` だが，**矛盾しない**:

- Rec.709 重みは合計 1 (0.2126 + 0.7152 + 0.0722 = 1.0000)．
- **R=G=B=x の無彩色画素**では「チャネル別 OETF→加重和」`Σwᵢ·f(x) = f(x)` も「加重和→OETF」`f(x·Σwᵢ) = f(x)` も**厳密に一致**．
- シミュは白色 LED（RGB 等価 radiance）＋無彩色拡散材で厳密に R=G=B → 「luminance に直接 OETF」 ＝ 「実測の演算順（チャネル別 OETF → Rec.709 加重和）」と数値同値．
- **本質: OETF は不整合を作る操作ではなく消す操作**．OETF 前のシミュ＝linear 空間 luma，実測＝gamma 空間 luma で別ドメイン（真の不整合）．OETF 後は両者とも gamma 空間 luma になり，突合が正当化される．

### 残る近似（モデル誤差として正直に保持）

1. 無彩色等価が厳密なのはシミュ側のみ．実測白板画像は LED 分光・ISP ホワイトバランス・センサ応答で微小に非グレー（R'≈G'≈B' が完全でない分の luma vs luminance 差）．
2. カメラ実 OETF ≠ 厳密 sRGB（独自トーンマップ）．
3. ISP レンズシェーディング補正の残差（[TEST_01](../06_TEST/TEST_01_キャリブレーション検証履歴.md) 既知差分 #6，未対処）．

### 量子化（任意）

`oetf.quantize_uint8: true` で float → uint8 量子化を施す．実測 PNG が uint8 のため，公平比較には量子化を入れる．量子化ノイズ ≈ ±0.5 単位は無次元指標にほぼ影響しない（spp=1024 の MC ノイズの方が大きい）．

## mean 正規化（linear モード用） (Mean Anchor Normalization for Linear Mode)

OETF を適用しない linear ドメインでの評価結果について **mean を target_mean に揃える線形ゲイン正規化** を行う．OETF の露出アンカーと同じ思想を，OETF を介さない linear 評価モードに対称展開したもの．

### 物理動機

シミュ側の `radiance` 値は scene YAML での暫定値（実測 mean アンカー）で決まるが，**シーン設定を変えると（例: 発光面サイズや反射率の変更）絶対値スケールが大きく動く** （[Step 8-7](../06_TEST/TEST_01_キャリブレーション検証履歴.md#step-8-7-発光面サイズ検証-2835-vs-1515φe-保存) で実証．size を 2.8×3.5 → 1.5×1.5 mm に変えると mean が 2.83× 上昇）．数値指標 (CoV / 勾配 / C/P) は **線形ゲインに対し不変** なので形状指標の比較は問題ないが，**輝度マップを画像で見比べる際に color scale が桁違いになり「形」の比較が阻害される** ．

linear ドメインで mean を共通の target_mean に揃えれば，color scale を揃えた状態で複数シミュ結果の形を視覚的・数値的に公平比較できる．

### 計算式

```text
α = target_mean / values.mean()
normalized = values × α
```

線形ゲイン α を全要素に乗算するのみ．返り値は `(normalized, α)` のタプル．`values.mean() == 0` の場合は `ValueError`．

### 不変性（厳密）

| 指標 | 正規化前後で |
| --- | --- |
| mean | target_mean に厳密一致 |
| CoV (`std / mean`) | **不変**（分子分母同時 α 倍） |
| max/min 比 | **不変** |
| 中心/周辺比 (C/P) | **不変** |
| 勾配 % | **不変** |
| std | α 倍 |
| max, min | α 倍 |

### OETF との排他制御

`oetf.enabled=true` と `normalization.enabled=true` を同時指定すると **ValidationError**（OETF の露出ソルバが既に mean アンカーを行うため二重適用になる）．`EvaluationConfig._validate_oetf_normalization_exclusive` で保証．

| OETF | normalization | 挙動 |
| --- | --- | --- |
| 有効 | 省略 or 無効 | OETF 内の露出ソルバが mean アンカー（既存動作） |
| 有効 | 有効 | **ValidationError**（排他制約違反） |
| 無効 | 有効 | linear ドメインで mean 揃え（**本機能の主用途**） |
| 無効 | 省略 or 無効 | 無変換（生 luminance のまま，後方互換） |

### 後方互換

`normalization` キー省略時は無変換．既存設定（`configs/eval_real.yaml`，`configs/eval_baseline.yaml`）は変更不要．`configs/eval_baseline_linear.yaml` のみ新規追加された設定．

### 主用途

[TEST_03 輝度マップ形状追跡](../06_TEST/TEST_03_輝度マップ形状追跡.md) における **複数 Step の輝度マップ形状比較**．発光面サイズ・反射率等を変えたシミュレーション結果を，共通の色スケールで並べることで「形」の異同を視覚的に判定できる．

## メトリクス定義 (Metrics Definitions)

ROI 内の輝度配列 `Y` を入力に，以下を算出．計算式は `uniformity.py` 実装と一致．

### 全体指標

| 指標 | 記号 | 定義 | 物理的意味 |
| --- | --- | --- | --- |
| 平均 | mean | `Y.mean()` | ROI 平均輝度（露出に依存） |
| 標準偏差 | std | `Y.std(ddof=0)` | ばらつきの絶対量 |
| 変動係数 | **CoV** | `std / mean` | **無次元のばらつき**．均一性の主要指標 |
| max/min 比 | max-min | `Y.max() / Y.min()` | 最大/最小比．外れ値に敏感（MC ノイズ感度高） |

### ゾーン分類 (3 zones)

ROI 中心からの **正規化楕円距離** `d` に基づき分類:

```text
d = sqrt( ((x - cx) / (W/2))² + ((y - cy) / (H/2))² )
```

| ゾーン | 範囲 |
| --- | --- |
| center | `d < 0.33` |
| middle | `0.33 ≤ d < 0.66` |
| periphery | `0.66 ≤ d` |

### ゾーン指標

| 指標 | 定義 | 物理的意味 |
| --- | --- | --- |
| **中心/周辺比 (C/P)** | `center_mean / periphery_mean` | 中心と周辺の明るさ比 (>1 は中心優位) |
| **勾配 %** | `(center_mean − periphery_mean) / center_mean × 100` | 中心からの相対的明るさ低下率 |

ゾーン境界 (0.33, 0.66) は評価 YAML の `zones.boundaries` で変更可．

### 動径プロファイル

正規化距離 `d` を **20 ビン**（既定）に分け，各ビンの平均輝度を取る．`figures/radial_profile.png` でグラフ化．

### 動径 min/max 比（オプション）

`metrics.radial_min_max: true` で有効化．動径プロファイル（20 ビン平均）の **最小ビン値 ÷ 最大ビン値**．画素単位 `max_min` 比よりノイズ・外れ値に対しロバスト．

| 指標 | 定義 | 値域 |
| --- | --- | --- |
| **動径 min/max 比** | `min(bins) / max(bins)`（空ビンは nan で除外） | 0〜1（1.0 で均一．小さいほどムラ大） |
| **動径 min ビン距離** | 最小値ビンの正規化距離 | 0〜1（1.0 が最周辺ビン） |

`metrics.csv` に `radial_min_max_ratio` / `radial_min_distance` / `radial_min` / `radial_max` の 4 列を末尾に追加．既定 False（既存挙動互換）．`metrics.radial_profile` の有無に関わらず内部で動径プロファイルを算出するため独立して有効化可能．実装: `evaluation/radial.py:calc_radial_min_max_ratio`（SmTIAS-Evaluation の `src/analysis/spatial.py` からマージ）．

### 画像全体 min/max 比（オプション，2026-09-14 導入）

`metrics.fullimage_min_max: true` で有効化．**画像全体で評価する，ノイズ補正付きの最小/最大比**．
動径 min/max 比は方位方向に平均するため，同心円状でない輝度分布（オフ軸 LED 配置など）では
明るい側と暗い側が同じ輪帯で混ざり非対称を過小評価する（TEST_18 では非対称が強い条件で
0.97〜0.99 に張り付き，差は非対称の大きさ \|a1\| と相関 0.93〜0.95）．一方，画素単位や平滑化後の
パーセンタイルは MC ノイズの裾を拾い，生の輝度が低い（相対ノイズが大きい）条件で頭打ちになる．
本指標はその両方を避ける．

定義（`evaluation/fullimage.py:calc_fullimage_min_max_ratio`）:

1. ROI 輝度を σ = `fullimage_sigma`（既定 12 px）のガウス平滑化して `smooth` を得る．
2. ノイズ場を推定する: 残差（ROI − ガウス(σ=4)(メディアン 5 px)(ROI)）を空間シャッフルして同じ
   σ で平滑化し，その最小値 `noise_min`・最大値 `noise_max` を 4 回の平均で求める（構造を壊して
   ノイズの統計だけ残す．構造 CoV と同じ発想）．
3. `fullimage_min = min(smooth) − noise_min`，`fullimage_max = max(smooth) − noise_max`，
   `fullimage_min_max_ratio = fullimage_min / fullimage_max`．

| 指標 | 定義 | 値域 |
| --- | --- | --- |
| **画像全体 min/max 比** | ノイズ補正後の `min / max` | 0〜1（1.0 で均一．小さいほどムラ大） |

`metrics.csv` に `fullimage_min_max_ratio` / `fullimage_min` / `fullimage_max` / `fullimage_noise_min` /
`fullimage_noise_max` の 5 列を追加（動径 min/max の後・形状指標の前）．既定 False（既存挙動互換）．
線形ゲインに不変（mean anchor 正規化の前後で同じ値）．同じ画像・同じ設定なら常に同じ値（乱数は
関数内で seed 固定）．入力が 2D でない・σ ≤ 0・`min(H, W) < 4σ`・補正後の max ≤ 0 は ValueError．

検証（`scripts/build_fullimage_minmax.py`，軸上スイープ 20 条件）: 対称な分布では動径 min/max 比と
±0.01 以内で一致し，一様像＋同量のノイズで測ったノイズ床は ±0.004．ブロック平均・多項式フィット
・補正なし平滑化の各候補より偏りとノイズ床の両方で優れた．

**使用上の注意**:

- **平滑化による偏り**: σ の平滑化が構造をなますため，比は真値より少し大きく（均一寄りに）出る
  （256² の線形勾配で真値 0.800 → 0.813，ガウス山で 0.857 → 0.865）．偏りは σ と ROI サイズの比で
  決まるので，**σ を固定して条件間を比較する**用途に使い，絶対値を他の定義と直接比べない．
- **ノイズ床はノイズ量に比例**: 一様像で相対ノイズ 5% → 0.003，10% → 0.006，20% → 0.012．
  構造のある像ではノイズが増えると比が均一寄りに上振れするので，**同じ spp（同じノイズ水準）
  の条件同士で比較する**．
- ROI が小さいときは σ を下げる（`fullimage_sigma`）．4σ 未満の辺は受け付けない．

### 形状指標 (Shape Metrics — オプション、2026-06-03 導入)

**動機**: 既存の 4 指標 (CoV/勾配/C/P/動径 min/max) は **不均一性の絶対量しか測れず、「形」を構造的に区別できない**．シミュ「Y 軸単調勾配」と実機「中央ピーク楕円」のように形が違う 2 つの画像でも、数値だけは似通うことがある．詳細は [TECH_11 形状指標の見直しと新指標導入](../05_TECH/TECH_11_形状指標の見直しと新指標導入.md)．

`metrics.shape_metrics: true` で有効化．`metrics.csv` に末尾 12 列を追加．既定 False（既存挙動互換）．実装: `evaluation/shape_metrics.py`．

#### 5 つの形状指標

| 指標 | 定義 | 値域 | 何を測るか |
| --- | --- | --- | --- |
| **輝度重心 (Intensity Centroid)** | 輝度を重みとする画像内重心 (x, y) | 画像座標 + 正規化 [-1, +1] | 評価板内の **重心位置の偏り**（Y 単調勾配 → 下方偏り） |
| **角度/半径分解 (Angular/Radial Decomposition)** | 極座標で I(r, θ) に分解し角度方向 var と半径方向 var を計算 | angular_ratio: 0〜1 | **0=完全同心円 / 1=完全角度依存**．中央ピーク楕円は小，Y 単調勾配は大 |
| **SSIM (Structural Similarity Index)** | 参照画像との構造的類似度 (skimage) | 0〜1 (1=完全一致) | **シミュ vs 実機の直接比較**．`shape_reference` が必要 |
| **NCC (Normalized Cross-Correlation)** | mean 中心化した画像同士の相関 | -1〜+1 (1=完全相関) | **絶対輝度スケール不変な形の一致度** |
| **PCA 主軸 (Intensity PCA)** | 輝度を重みとする weighted PCA で主軸方向と楕円率 | 角度 [-90°, +90°] / 楕円率 [0, 1] | 不均一性の **主軸方向と楕円率** |

#### CSV 列名（末尾 12 列）

| カテゴリ | 列名 |
| --- | --- |
| 重心 | `intensity_centroid_x_px` / `intensity_centroid_y_px` / `intensity_centroid_x_norm` / `intensity_centroid_y_norm` / `intensity_centroid_offset_norm` |
| 角度/半径分解 | `angular_variance` / `radial_variance` / `angular_ratio` |
| PCA | `pca_primary_axis_angle_deg` / `pca_eccentricity` |
| 構造的類似度 | `ssim` / `ncc`（`shape_reference` 未指定なら NaN） |

#### 設定例

```yaml
metrics:
  shape_metrics: true
  shape_reference: ../reference_measurement/quantitative/expected_results/MiniTIAS_QM_20260601_124500_luminance_map.npy  # 任意
```

`shape_reference` 相対パスは config YAML のディレクトリ基準で解決．未指定（null）なら SSIM/NCC は NaN になり他の指標は計算される．

#### 注意

- **完全均一画像で `angular_ratio` は NaN**（variance ゼロ割り）．呼び出し側で扱うこと
- **合成純粋 Y 勾配は `angular_ratio = 1.0`**（角度方向 var 支配）．実シミュ画像（ノイズや非一様性込み）の典型値は 0.3〜0.5
- **shape_reference の shape mismatch** は SSIM/NCC で NaN を返しエラーは出さない（警告ログのみ）

## 可視化規約 (Figure Conventions)

`sm-evaluate` は `figures/` に以下 4 種を生成．SmTIAS-Evaluation 互換スタイル．

| ファイル | 内容 | カラーマップ・スタイル |
| --- | --- | --- |
| `luminance_map.png` | ROI 輝度マップ（X-Y） | `hot` cmap，個別正規化（min-max を自動），カラーバー付き |
| `histogram.png` | 輝度ヒストグラム | 256 bin，ROI 全体 |
| `radial_profile.png` | 動径プロファイル | 20 ビン平均輝度の line plot |
| `zone_map.png` | 3 ゾーン分類マップ | luminance 背景 + 境界線（楕円） |

実装: `evaluation/visualize.py`．

## 出力ファイル仕様 (Output Files)

`sm-evaluate --output <dir>/` で以下を生成:

```text
<dir>/
├── metrics.csv          # 1 行 ヘッダ + 1 行 値（評価結果サマリ）
└── figures/
    ├── luminance_map.png
    ├── histogram.png
    ├── radial_profile.png
    └── zone_map.png
```

`--no-figures` を付けると `figures/` を作らず `metrics.csv` だけを出す（指標の値は同一．
スイープで図の描画分 約 2 秒/条件 を省くため）．本体は `evaluation/pipeline.py:evaluate_image`
で，スイープスクリプトはこれをプロセス内で呼ぶ（`save_figures=False`）．

### metrics.csv カラム定義（ヘッダ順）

```text
cov, std, max_min_ratio, mean, max, min,
center_mean, center_std, middle_mean, middle_std, periphery_mean, periphery_std,
center_periphery_ratio, gradient_magnitude
```

すべて float．`gradient_magnitude` は上記「勾配 %」と同じ値．

## キャリブレーション目標 (Calibration Target)

[PLAN_02_開発ステップ](../03_PLAN/PLAN_02_開発ステップ.md) より，実測 baseline 1 枚 (`MiniTIAS_20260408_144434`) に対する数値:

| 指標 | 実測値 | 仮置き許容範囲 |
| --- | --- | --- |
| CoV | 0.0426 | ±0.005 |
| 中心/周辺比 | 1.064 | ±0.02 |
| 勾配 (%) | 6.23 | ±1.5 |

シミュ側はこれらの値に **数値だけでなく形（輝度マップ分布）も**近づけることが目標．形の追跡は [TEST_01 輝度マップ形状の追跡](../06_TEST/TEST_01_キャリブレーション検証履歴.md#輝度マップ形状の追跡-luminance-map-shape-tracking) を参照．

## キャリブレーション経過（要約） (Calibration Progression Summary)

Step 8 で実施した検証イテレーションの**結論サマリ**．各 Step の仮説・変更・数値・解釈・学びの**詳細**は [TEST_01_キャリブレーション検証履歴](../06_TEST/TEST_01_キャリブレーション検証履歴.md) を参照．

### 確定基準

- 設定: `R=0.85, max_depth=8, spp=1024, OETF=on`
- 評価: sRGB ドメイン（OETF 順方向適用後の uint8 量子化値で比較）
- 残差: CoV 0.089 vs 実測 0.0427 = **2.1×**（初期 spp=128 時の 5.5× から大幅縮小）

### 寄与順（訂正済み）

> **OETF（最大・約 2/3，解消済み）＞ ISP LSC（残差主因候補・未対処）＞ 反射率（弱いレバー）**

旧版「主因は ISP レンズシェーディング (83%)」は OETF を抜かした交絡判断で誤りだった．OETF 適用後で寄与順を訂正．

### 主要な発見（時系列）

| Step | 内容 | 主要な学び |
| --- | --- | --- |
| 8-1 | spp 校正 128→1024 | spp は CoV 0.183→0.174 で頭打ち．残差はノイズではなく物理勾配． |
| 8-2 | 反射率スイープ (0.75〜0.92) | 弱い二次レバー（∂勾配/∂R ≈ -38%/単位）．これ単独では差を閉じられない． |
| 8-3 | **OETF 順方向適用** | パラメータフリーで**見かけ差の 2/3 を解消**．非線形変換下では無次元指標は不変ではない． |
| 8-4 | max_depth 検証 (8→12) | depth=8 は勾配を過大評価（-2.6pt/linear）．確定 YAML は 8 のまま，3D ベイクは独立化． |
| 8-5 | 3D 可視化改善 | 「見え方」と「物理」を厳格に分離．同手法で計算すれば物理は均一． |
| 8-6 | LED 物性確定 (LP-2835D90VW) | 規格推定 radiance 2030 vs 校正値 2807 = 1.38× 差は反射率 0.89 で説明可能（既知差分 #3 クローズ）． |
| 8-7 | 発光面サイズ検証 (2.8×3.5 vs 1.5×1.5) | 形状指標は不変（点光源近似実証），絶対値は LED 周辺幾何と縮退．**校正値は局所幾何依存と理解する**． |

### 次のアクション

残差 1.6〜1.9× に対する **LSC（ISP レンズシェーディング）順方向モデル**の設計・実装．物理動機づけ低自由度形（cos⁴ ベース等）に限定し overfitting を回避．詳細は [TEST_01 Step 8-N 候補](../06_TEST/TEST_01_キャリブレーション検証履歴.md#step-8-n-候補-lsc-順方向モデル未着手) 参照．

反射率は均一性からは一意に定まらない（物理範囲内で縮退）ため暫定 0.85 据え置き．実フィラメント分光反射率データ入手時に再校正．

## 参照 (References)

- [SPEC_02_シーン定義仕様 §evaluation](SPEC_02_シーン定義仕様.md): 評価 YAML スキーマ
- [SPEC_03_基準設計の物理構成](SPEC_03_基準設計の物理構成.md): シーン物理仕様
- [TEST_01_キャリブレーション検証履歴](../06_TEST/TEST_01_キャリブレーション検証履歴.md): 評価値の歴史と検証経緯
- [TEST_02_シミュレーション実行記録](../06_TEST/TEST_02_シミュレーション実行記録.md): output/ 索引
- [TEST_03_輝度マップ形状追跡](../06_TEST/TEST_03_輝度マップ形状追跡.md): 形を実機に近づける観点
- [PLAN_02_開発ステップ](../03_PLAN/PLAN_02_開発ステップ.md): 実測ターゲット出典
- 実装エントリ:
  - `src/sm_tias_lightsim/cli/evaluate.py`: CLI
  - `src/sm_tias_lightsim/uniformity.py`: Rec.709 luma + CoV/std/max-min
  - `src/sm_tias_lightsim/evaluation/oetf.py`: 標準 sRGB OETF + 露出アンカー
  - `src/sm_tias_lightsim/evaluation/normalize.py`: mean 正規化（linear モード用）
  - `src/sm_tias_lightsim/evaluation/roi.py`: ROI 抽出
  - `src/sm_tias_lightsim/evaluation/zones.py`: 3 ゾーン分類 + C/P + 勾配 %
  - `src/sm_tias_lightsim/evaluation/radial.py`: 動径プロファイル
  - `src/sm_tias_lightsim/evaluation/visualize.py`: 4 種 figures 生成
