sm-evaluate CLI が担う シミュレーション結果 / 実測 PNG の照明均一性評価 の方法論を定義する.本書はアルゴリズム・物理動機・規約をまとめ,実装の根拠(why)と入出力(what)を記述する.評価設定 YAML のスキーマ詳細は SPEC_02_シーン定義仕様 の evaluation セクションを参照.
関連:
src/sm_tias_lightsim/cli/evaluate.py, src/sm_tias_lightsim/evaluation/, src/sm_tias_lightsim/uniformity.pyシミュレーション結果(output/<run>/luminance.npy または render_uint8.png)と実測 PNG (reference_measurement/.../*.png) を 同一の評価パイプラインに通し,無次元の均一性指標(CoV,勾配 %,中心/周辺比 等)を算出する.シミュと実測を比較可能にする鍵は OETF 順方向適用 で,シミュの linear ドメインを実機の sRGB 符号化ドメインに揃える操作である(後述).
設計目的は MiniTias-Evaluation との 数値整合性(実測 PNG に対し小数点 14 桁レベルで一致確認済み,TEST_01 既知差分 #9)と,シミュ⇄実測の 公平な比較.
入力 (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 |
評価板の 撮影範囲内の有効領域 を矩形で切り出す.評価設定 YAML の roi セクションで指定する.
image_pixel 型実測 PNG の座標系(左上原点,X 右,Y 下)で 絶対ピクセル座標を直接指定する.シミュ画像と実測画像で 物理対応する同じ領域を切り出すために,シミュ側の ROI は実測 ROI の物理対応関係から決定する.
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 画像 ↔ 物理 の向き を参照.
最重要セクション.シミュ linear 結果と実測 sRGB PNG を直接比較すると CoV や勾配の絶対値は不一致になる.これを データに合わせて回す係数ゼロで解消するのが OETF 順方向適用.
YUV_420_888 → BT.601 は定義上ガンマ済み R'G'B' 上の量. ┌─ 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().
露出(線形ゲイン)は AE が制御する量で,シミュ側で任意に決められない.実測 ROI mean に対数二分でアンカーする:
α を掛ける146.7146) に一致する α を対数二分で求めるconfigs/eval_real.yaml は oetf セクション無し(無効).configs/eval_baseline.yaml は oetf.enabled: true(シミュ用).実測 MiniTias-Evaluation の「輝度値」は厳密には Rec.709 luma Y'(ガンマ済み成分への加重和 Y' = 0.2126R' + 0.7152G' + 0.0722B',ガンマを戻さない).シミュ側は linear luminance に直接 OETF を当てている.OETF は非線形なので一般に Σwᵢ·f(xᵢ) ≠ f(Σwᵢ·xᵢ) だが,矛盾しない:
Σwᵢ·f(x) = f(x) も「加重和→OETF」f(x·Σwᵢ) = f(x) も厳密に一致.oetf.quantize_uint8: true で float → uint8 量子化を施す.実測 PNG が uint8 のため,公平比較には量子化を入れる.量子化ノイズ ≈ ±0.5 単位は無次元指標にほぼ影響しない(spp=1024 の MC ノイズの方が大きい).
OETF を適用しない linear ドメインでの評価結果について mean を target_mean に揃える線形ゲイン正規化 を行う.OETF の露出アンカーと同じ思想を,OETF を介さない linear 評価モードに対称展開したもの.
シミュ側の radiance 値は scene YAML での暫定値(実測 mean アンカー)で決まるが,シーン設定を変えると(例: 発光面サイズや反射率の変更)絶対値スケールが大きく動く (Step 8-7 で実証.size を 2.8×3.5 → 1.5×1.5 mm に変えると mean が 2.83× 上昇).数値指標 (CoV / 勾配 / C/P) は 線形ゲインに対し不変 なので形状指標の比較は問題ないが,輝度マップを画像で見比べる際に color scale が桁違いになり「形」の比較が阻害される .
linear ドメインで mean を共通の target_mean に揃えれば,color scale を揃えた状態で複数シミュ結果の形を視覚的・数値的に公平比較できる.
α = 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.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 輝度マップ形状追跡 における 複数 Step の輝度マップ形状比較.発光面サイズ・反射率等を変えたシミュレーション結果を,共通の色スケールで並べることで「形」の異同を視覚的に判定できる.
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 ノイズ感度高) |
ROI 中心からの 正規化楕円距離 d に基づき分類:
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 でグラフ化.
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(MiniTias-Evaluation の src/analysis/spatial.py からマージ).
動機: 既存の 4 指標 (CoV/勾配/C/P/動径 min/max) は 不均一性の絶対量しか測れず、「形」を構造的に区別できない.シミュ「Y 軸単調勾配」と実機「中央ピーク楕円」のように形が違う 2 つの画像でも、数値だけは似通うことがある.詳細は TECH_11 形状指標の見直しと新指標導入.
metrics.shape_metrics: true で有効化.metrics.csv に末尾 12 列を追加.既定 False(既存挙動互換).実装: evaluation/shape_metrics.py.
| 指標 | 定義 | 値域 | 何を測るか |
|---|---|---|---|
| 輝度重心 (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 | 不均一性の 主軸方向と楕円率 |
| カテゴリ | 列名 |
|---|---|
| 重心 | 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) |
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 ゼロ割り).呼び出し側で扱うことangular_ratio = 1.0(角度方向 var 支配).実シミュ画像(ノイズや非一様性込み)の典型値は 0.3〜0.5sm-evaluate は figures/ に以下 4 種を生成.MiniTias-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.
sm-evaluate --output <dir>/ で以下を生成:
<dir>/
├── metrics.csv # 1 行 ヘッダ + 1 行 値(評価結果サマリ)
└── figures/
├── luminance_map.png
├── histogram.png
├── radial_profile.png
└── zone_map.png
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 は上記「勾配 %」と同じ値.
PLAN_02_開発ステップ より,実測 baseline 1 枚 (MiniTIAS_20260408_144434) に対する数値:
| 指標 | 実測値 | 仮置き許容範囲 |
|---|---|---|
| CoV | 0.0426 | ±0.005 |
| 中心/周辺比 | 1.064 | ±0.02 |
| 勾配 (%) | 6.23 | ±1.5 |
シミュ側はこれらの値に 数値だけでなく形(輝度マップ分布)も近づけることが目標.形の追跡は TEST_01 輝度マップ形状の追跡 を参照.
Step 8 で実施した検証イテレーションの結論サマリ.各 Step の仮説・変更・数値・解釈・学びの詳細は TEST_01_キャリブレーション検証履歴 を参照.
R=0.85, max_depth=8, spp=1024, OETF=onOETF(最大・約 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 候補 参照.
反射率は均一性からは一意に定まらない(物理範囲内で縮退)ため暫定 0.85 据え置き.実フィラメント分光反射率データ入手時に再校正.
src/sm_tias_lightsim/cli/evaluate.py: CLIsrc/sm_tias_lightsim/uniformity.py: Rec.709 luma + CoV/std/max-minsrc/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 生成