diff --git a/.gitignore b/.gitignore index ad261b9..8f69504 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ # シミュ・評価結果(コミット対象外) output/ +# 実機実測データ(SmTIAS-Evaluation からの引き渡し入力.コミット対象外) +lightsim/ + # docs 下の生成画像(再生成可能なので追跡しない.生成スクリプトは追跡する) docs/**/figures/*.png diff --git a/CLAUDE.md b/CLAUDE.md index 6c3ed4f..6053285 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ ## 開発進捗 -最新: フェーズ 7(実装)— Step 8 基準設計キャリブレーションの **Step 8-14(物理モデル精緻化)** 進行中.A1 非 Lambertian / A8 反射率 / A9 max_depth はクリア(数値改善のみ or 逆効果),LED 配光(ξ)が形改善の本物候補と判明,**形改善の本筋は B1 筐体幾何精度に確定**(実機 CoV=0.0695 に対しシミュ CoV=0.1826,残差 2.63×).詳細な経緯・設計判断・失敗パターンは [docs/PROGRESS.md](docs/PROGRESS.md). +最新: フェーズ 7(実装)— Step 8 基準設計キャリブレーションの **Step 8-14(物理モデル精緻化)** 進行中.**面光源スケール軸バグを発見・修正**(`lights.py` が LED の高さを潰れた Z 軸に入れ縦 Y を無補正→全 LED が幅3mm×高さ2mの巨大ストリップ化).実機単灯実測との突合で **下灯 2.1× がこのバグのアーティファクト**と判明,修正後は 3 灯均等・下灯最暗で実機一致.ただし合成 CoV は 0.1815→0.1641(残差 2.63×→2.36×)の小改善にとどまり,**残る不一致は各単灯フットプリントの拡散不足**(シミュ peak/mean≈1.7 vs 実機≈1.2)=論点は配光 ξ/ディフューザー拡散のモデル化へ移動([TEST_08](docs/06_TEST/TEST_08_面光源スケール軸バグの発見と修正.md)).A1 非 Lambertian / A8 反射率 / A9 max_depth はクリア(数値改善のみ or 逆効果).詳細な経緯・設計判断・失敗パターンは [docs/PROGRESS.md](docs/PROGRESS.md). 実装済みモジュール: - `src/sm_tias_lightsim/uniformity.py` — Rec.709 輝度変換 + CoV/std/max-min(MiniTias-Evaluation コピー) diff --git "a/docs/06_TEST/TEST_07_LED\343\202\260\343\203\253\343\203\274\343\203\227\345\210\245\345\213\225\344\275\234\347\242\272\350\252\215.md" "b/docs/06_TEST/TEST_07_LED\343\202\260\343\203\253\343\203\274\343\203\227\345\210\245\345\213\225\344\275\234\347\242\272\350\252\215.md" index 5ca1db7..c3b424f 100644 --- "a/docs/06_TEST/TEST_07_LED\343\202\260\343\203\253\343\203\274\343\203\227\345\210\245\345\213\225\344\275\234\347\242\272\350\252\215.md" +++ "b/docs/06_TEST/TEST_07_LED\343\202\260\343\203\253\343\203\274\343\203\227\345\210\245\345\213\225\344\275\234\347\242\272\350\252\215.md" @@ -1,5 +1,7 @@ # LED グループ別動作確認 (LED Group Sanity Check) +> ⚠️ **訂正(重要)**: 本ドキュメントの中心的結論「**下灯 2.1× は直接光効率の幾何的優位で物理的に正しい/意図した設計**」は**誤り**である.その後の実機単灯データとの突き合わせで,`lights.py` の面光源スケールに**座標軸取り違えバグ**(高さを縦 Y でなく潰れた奥行き Z に入れ,LED が「幅 3 mm × 高さ 2 m」の巨大ストリップ化)が見つかり,**下灯 2.1× はこのバグのアーティファクト**だったと判明した.修正後は 3 灯がほぼ均等(実機どおり・下灯最暗)になる.本ドキュメントの数値・「形」の観察・物理的考察(特に §結果〜§bottom が 2.1× 強い理由)は**バグ込みの出力に基づく**ため,そのまま信用しないこと.詳細・修正・再検証は [TEST_08 面光源スケール軸バグの発見と修正](TEST_08_面光源スケール軸バグの発見と修正.md) を参照. + 本ドキュメントは **シミュレーションが下/右/左の 3 つの LED 基板を物理位置どおりに独立にモデル化できているか** を,各グループの LED だけを ON にした輝度マップの形と数値で検証した結果のレポートである.Step 8 系キャリブの主軸ではなく **シミュ動作の基礎健全性確認** として位置付ける. ## 目的・背景 diff --git "a/docs/06_TEST/TEST_08_\351\235\242\345\205\211\346\272\220\343\202\271\343\202\261\343\203\274\343\203\253\350\273\270\343\203\220\343\202\260\343\201\256\347\231\272\350\246\213\343\201\250\344\277\256\346\255\243.md" "b/docs/06_TEST/TEST_08_\351\235\242\345\205\211\346\272\220\343\202\271\343\202\261\343\203\274\343\203\253\350\273\270\343\203\220\343\202\260\343\201\256\347\231\272\350\246\213\343\201\250\344\277\256\346\255\243.md" new file mode 100644 index 0000000..248374e --- /dev/null +++ "b/docs/06_TEST/TEST_08_\351\235\242\345\205\211\346\272\220\343\202\271\343\202\261\343\203\274\343\203\253\350\273\270\343\203\220\343\202\260\343\201\256\347\231\272\350\246\213\343\201\250\344\277\256\346\255\243.md" @@ -0,0 +1,141 @@ +# 面光源スケール軸バグの発見と修正 (Area-Light Scale-Axis Bug) + +本ドキュメントは,**実機の単灯(下/左/右)実測データとシミュレーションを 1 灯ずつ突き合わせた結果,シミュ側の面光源(area emitter)変換に座標軸の取り違えバグが見つかり,これが「下灯がほかの 2 倍強い」という長年の不一致の主因だった**ことの発見・原因・修正・検証を記録する. + +> ⚠️ 本件により [TEST_07 LED グループ別動作確認](TEST_07_LEDグループ別動作確認.md) の結論「下灯 2.1× は物理的に正しい/意図どおり」は**誤り**であったことが判明した(TEST_07 はバグ込みの出力を物理的に正当化してしまっていた).TEST_07 冒頭にも訂正注記を追加済み. + +## 発端 — 実機単灯データとの突き合わせ + +実機(SmTIAS-Evaluation)で 3 灯を 1 灯ずつ単独点灯して撮影した白板 ROI 輝度マップ一式を入力として受領した(`lightsim/`:`luminance_maps/{bottom,left,right}_*.npy`,0〜255 linear / Rec.709 luma / LSC 補正済 / portrait). + +ユーザー観察:**「シミュでは下側がほかの約 2 倍になっているが,実機では 3 灯ほぼ均等」**.これを灯ごとに分解して原因を特定するのが目的. + +灯ごとの白板 ROI 平均輝度(実機)と,既存シミュ単灯出力(`output/led_group_sanity/`,spp=1024)を比較: + +| 灯 | 実機 mean | 実機 比 | シミュ(バグ) mean | シミュ(バグ) 比 | +| --- | --- | --- | --- | --- | +| bottom | 57.75 | **1.00(最暗)** | 98.82 | **2.10** | +| left | 61.46 | 1.06 | 47.08 | 1.00 | +| right | 63.55 | 1.10 | 47.10 | 1.00 | + +→ **実機は 3 灯ほぼ均等で下灯が最暗**.シミュは**下灯だけ 2.10×**で,しかも左右が 47.08 vs 47.10 と完全対称.傾向が逆転している. + +LED 定義(`configs/leds_extracted.yaml`)は全 18 灯が `direction:(0,0,-1)`・`radiance:2807.25` で完全同一.**LED の位置・向き・強度は Fusion 360 STL 由来で実機どおり**であり,radiance 差では説明できない.したがって原因は LED 以外(周囲幾何・変換処理)にあると絞り込んだ. + +## 原因 — 面光源スケールの軸取り違え + +### Mitsuba の rectangle 仕様(実機検証) + +Mitsuba 3 の `rectangle` 形状を実際に生成して確認した: + +``` +中心法線 n = (0, 0, 1) # 面は +Z を向く +面内範囲 = X∈[-1,1], Y∈[-1,1] # XY 平面に寝た 1 辺 2 の正方形 +Z 方向 = 厚みゼロ(潰れている) +``` + +→ **rectangle は「XY 平面・法線 +Z」**.サイズを与えるには **横 = X 軸,高さ = Y 軸**をスケールしなければならない(Z は潰れているのでスケールしても無意味). + +### バグ箇所 + +`src/sm_tias_lightsim/scene/lights.py` `_build_area_light_transform`: + +```python +# 修正前(バグ) +scale = mi.ScalarTransform4f().scale([sw / 2.0, 1.0, sh / 2.0]) +# 横X 縦Y 奥Z +# 横(X) = sw/2 … OK +# 縦(Y) = 1.0 … ★ スケールされず基準のまま = ±1 m(2000 mm) +# 奥(Z) = sh/2 … 高さを“潰れた厚み方向”に入れてしまい無効 +``` + +コード上のコメントは「rectangle は **XZ 平面**上の正方形(**Y 軸正方向が前面**)」と書かれていたが,実体は XY 平面・+Z 前面.**前提を 1 軸勘違いしたまま実装されており,高さ `sh` を縦(Y) ではなく奥行き(Z) に入れ,縦(Y) は無補正のまま放置**されていた. + +単位はメートル系(mm→m に position_scale=0.001 済)なので,縦が無補正 = **±1 m**.結果: + +> 実物 **2.8 × 3.5 mm** の小さな LED が,シミュ内では **「幅 ~3 mm × 高さ 2000 mm」の細長い巨大ストリップ**になっていた. + +法線(発光方向)は look_at で正しく `direction` を向くため,発光の向き自体は正しい(このバグでは形状サイズだけが壊れる). + +### なぜ「下灯だけ 2 倍」になったか + +全 18 灯が同一の look_at(direction=(0,0,-1), up=(0,1,0))を使うため local Y→world Y.つまり**全 LED が world Y 方向に 2 m 伸びた縦ストリップ**になる.各灯列の横位置(X)と評価板の関係: + +| 灯 | LED 横位置 X | 評価板 X 範囲 [-49.5, +49.5] との関係 | +| --- | --- | --- | +| **bottom** | X∈[-10, +18] | **板中央の真裏**(中央に密集) | +| left | X = -66 | 板の外(左端より 16.5 mm 外側)+側壁/カバー脇 | +| right | X = +66 | 板の外(右端より 16.5 mm 外側)+側壁/カバー脇 | + +2 m 縦長化により,**板中央の真裏にある下灯だけが板中央を効率よく照らし**,板の外側にある左右灯は伸びた光の多くが壁/カバーに当たって板に届きにくい.本来の小さな ~3.5 mm 面光源ならこの縦長効果は生じず偏らない.**バグによる巨大化が,たまたま中央裏にある下灯を過剰に効かせて 2.1× を生んでいた**. + +TEST_07 はこの 2.1× を「直接光効率の幾何的優位=意図した設計」と解釈してしまったが,実際は**変換バグによるアーティファクト**だった. + +## 修正 + +スケールの軸を 1 か所入れ替えるのみ: + +```python +# 修正後 +scale = mi.ScalarTransform4f().scale([sw / 2.0, sh / 2.0, 1.0]) +# 横X 縦Y 奥Z +# 横(X) = sw/2 縦(Y) = sh/2 奥(Z) = 1.0(潰れた厚みは無補正でよい) +``` + +高さ `sh` を,誤って入れていた奥行き(Z) から本来の縦(Y) へ移しただけ.これで LED が実物どおりの ~2.8 × 3.5 mm に戻る. + +## 検証結果(spp=1024, max_depth=16 確定) + +修正後に 3 単灯+全灯合成を再レンダ(単灯 `output/sanity_fix_1024/`,全灯 `output/baseline_fix_1024/`,いずれも **spp=1024 / max_depth=16**).暫定 spp=256 出力は `output/led_group_sanity_fix/`. + +### ① 灯間バランス(mean 比,spp=1024) + +| 灯 | バグ 比 | **修正後 比** | 実機 比 | +| --- | --- | --- | --- | +| bottom | 2.099 | **1.000(最暗)** | 1.000(最暗)| +| left | 1.000 | **1.028** | 1.064 | +| right | 1.000 | **1.026** | 1.100 | + +→ **下灯 2 倍は消滅し,3 灯が ±3% で均等化**.さらに**「下灯が最暗」という実機の順序まで一致**した.灯間の振幅バランスは実機パターンに乗った. + +### ② 各灯の形(peak/mean,spp=1024) + +| 灯 | バグ peak/mean | 修正後 peak/mean | 実機 peak/mean | +| --- | --- | --- | --- | +| bottom | 1.60 | 1.78 | 1.22 | +| left | 1.78 | 1.69 | 1.27 | +| right | 1.73 | 1.75 | 1.20 | + +- 絶対 mean は ~98/47 → ~4.5 へ低下:発光面積が 2.8mm×2000mm → 2.8mm×3.5mm へ約 570 倍縮小したため.絶対スケールは下流の露出ソルバ / mean anchor 正規化で吸収されるため灯間比・形が本質. +- **peak/mean はなお実機(1.2 台)に届かず(修正後 1.7 台)**=各単灯フットプリントの**拡散不足**が残る(max_depth=8 の暫定では 2.9 と更に尖っていたが,depth=16 のバウンス増で 1.7 台まで滑らかになった). + +### ③ 線形性(単灯和 ≈ 全灯) + +単灯 3 本の和と全灯合成の ROI mean を比較:**相対誤差 0.34%**.Mitsuba の理論線形性どおりで,レンダの健全性を確認. + +### ④ 全灯合成 CoV(公式 sm-evaluate, ROI image_pixel 464×450) + +| シーン | linear CoV | 対実機残差 | center/周辺比 | gradient | +| --- | --- | --- | --- | --- | +| 修正前(`baseline_v3_leds`,バグ) | 0.1815 | 2.61× | 1.318 | 24.13 | +| **修正後(`baseline_fix_1024`)** | **0.1641** | **2.36×** | 1.311 | 23.71 | +| 実機 | 0.0695 | — | — | — | + +(参考:OETF/sRGB ドメインでは修正後 CoV=0.0790) + +→ **合成 CoV は 0.1815→0.1641(残差 2.63×→2.36×)と小改善にとどまる**.center/周辺比・勾配はほぼ不変.灯間バランスは直ったが,合成の主な不均一(中央明・縦勾配)は**灯間バランスではなく各単灯フットプリントの拡散不足**に由来するため,本バグ修正だけでは合成 CoV は大きく動かない(ユーザーの事前直感どおり). + +## 学びと残課題 + +1. **灯間の量的バランス問題は解決**:下灯 2.1× は変換バグのアーティファクトであり,修正で実機どおり(3 灯均等・下灯最暗)になった. +2. **論点が移動**:残る不一致は「灯間バランス」ではなく**各単灯フットプリントの拡散不足(peak/mean がシミュ 2.9 vs 実機 1.2)**.候補は LED 配光 ξ(指向の広がり)・ディフューザーのモデル化・面光源サイズの実測整合など. +3. **最終目標(3 灯合成 CoV)への効果は小さい(確定)**:spp=1024 で合成 CoV は 0.1815→0.1641(残差 2.63×→2.36×)の小改善にとどまった.残差の主因は灯間バランスではなく**各単灯フットプリントの拡散不足**であり,本修正だけでは合成 CoV は大きく動かない.次の本筋は配光 ξ/ディフューザー拡散のモデル化. +4. **過去の正当化に注意**:TEST_07 のように「シミュ出力を物理で後付け説明」すると,変換バグを設計意図と取り違える危険がある.実機グラウンドトゥルースとの灯ごと突き合わせが有効だった. + +## 関連ドキュメント + +- [TEST_07 LED グループ別動作確認](TEST_07_LEDグループ別動作確認.md) — バグ込み出力に基づく旧結論(本件で訂正) +- [TEST_01 キャリブレーション検証履歴](TEST_01_キャリブレーション検証履歴.md) — Step 8 系残差 2.63× の経緯 +- [TEST_02 シミュレーション実行記録](TEST_02_シミュレーション実行記録.md) — `output/led_group_sanity{,_fix}/` の索引 +- 修正箇所: `src/sm_tias_lightsim/scene/lights.py` `_build_area_light_transform` +- 実機入力: `lightsim/`(単灯実測.出典 SmTIAS-Evaluation TEST_04) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index a701f0c..e84171f 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -57,3 +57,7 @@ - **Step 8-14 候補 α 後半 (A8 反射率スイープ) 完了 — 数値改善のみ・形は逆効果 / 「数値の偽物の一致」の初の定量実証 (2026-06-03)**: `scripts/sweep_reflectance.py` で d=16 新基準のもと R ∈ {0.75, 0.80, 0.85, 0.89, 0.92} を 5 値スイープ.新指標 (shape_metrics) を初めて感度分析に投入.結果: 既存 4 指標 (CoV/勾配/C/P/動径 min/max) は単調改善 (R=0.75→0.92 で CoV 0.186→0.142 で残差 24% 縮小) するが,**新指標 angular_ratio は 0.408→0.494 で実機方向と逆に増加**.実機は中央ピーク楕円 (同心円的) なので **形は実機から遠ざかる**.物理的解釈: R 上昇で ambient 増 → 半径方向の偏りは圧縮されるが角度方向ばらつきは相対的に圧縮されにくい → angular_ratio 増加.**形改善の本筋は B1 筐体幾何精度に最終確定**(A1/A8/A9 すべてクリア済で構造的再配分系のみ残る).副次発見: R=0.85 の CoV=0.1604 が max_depth スイープ d=16 と完全一致.新指標を導入していなければ気づけない発見で,形状指標見直し (TECH_11) の正当性が定量データで裏付けられた. - **Step 8-14 候補 α 前半 (A9 max_depth スイープ) 完了 — 数値改善のみクリア・形不変 (2026-06-03)**: ユーザー仮説「現状 d=8 では 0.85^8≈0.27 で取りこぼし大」を 5 値スイープ {8, 12, 16, 20, 28} で検証 (`scripts/sweep_max_depth.py` 約 85 分 + `scripts/build_max_depth_panel.py`).結果: 全 4 形状指標が **単調に実機方向に改善** (d=8→28 で CoV 0.1817→0.1583 で残差 20.9% 縮小).mean は 207.82 で固定.**実用的な収束は d=16 で達成** (d=16→28 で CoV 1.3% のみ).**「形」は完全に不変** (Y 軸単調勾配のまま contrast が緩むだけ).物理的解釈: 多重反射は **空間的に均一な ambient 成分**で,足しても暗/明の絶対差は縮むが空間配置は不変.「LED が下にしかない」配置の非対称性は除去不能.**ユーザー仮説の評価**: 方向性は正しい (21-28% 残差縮小) だが実用収束は d=16,実機 (CoV=0.0695) には届かず残差は形改善で詰める必要.新キャリブベースライン: **d=16**. - **形状指標の本質的見直しと新指標導入 (2026-06-03)**: 「数値が実機方向に動いても形は変わらない」が続けて確定したため,ユーザー指摘「現状 4 指標は不均一性の絶対量しか測れず,Y 軸単調勾配と中央ピーク楕円を構造的に区別できない」に対応する **形を直接評価する 5 指標** を追加実装.`src/sm_tias_lightsim/evaluation/shape_metrics.py` (新規): `calc_intensity_centroid` / `calc_angular_radial_decomposition` (angular_ratio: 0=同心円, 1=方向依存) / `calc_ssim` (scikit-image 依存) / `calc_ncc` / `calc_intensity_pca` の純関数 5 つ.`MetricsConfig` に `shape_metrics` / `shape_reference` を追加(後方互換).`configs/eval_baseline_dng_target_shape.yaml` を新規作成.`pyproject.toml` に `scikit-image>=0.21.0` を追加.[TECH_11](05_TECH/TECH_11_形状指標の見直しと新指標導入.md) で背景・計算式・予想値・遡及的注記を記録.今後の評価方針: **数値指標 4 + 形指標 5 + 視覚比較の 3 軸評価**で「数値改善のみで形が変わらない候補」と「形を変えうる候補」を明確に区別する. +- **面光源スケール軸バグの発見・修正 — 下灯 2.1× の真因確定 (2026-06-29)**: 実機の単灯(下/左/右)実測データ(`lightsim/`,SmTIAS-Evaluation 引き渡し,各灯単独点灯の白板 ROI 輝度マップ)とシミュ単灯を**灯ごとに突き合わせ**た結果,`scene/lights.py` の `_build_area_light_transform` に**座標軸取り違えバグ**を発見.Mitsuba の rectangle は実体「XY 平面・法線 +Z」(実機 Mitsuba で検証)なのに,コードは旧コメント「XZ 平面・+Y 前面」を前提に `scale([sw/2, 1.0, sh/2])` と書かれ,**高さ `sh` を潰れた奥行き Z に入れ・縦 Y を無補正(±1m)のまま**にしていた → **全 18 LED が「幅 ~3mm × 高さ 2000mm」の縦長ストリップ**化.全灯が同一 look_at(direction=(0,0,-1), up=(0,1,0))のため local Y→world Y で全灯が world Y に 2m 伸び,**板中央の真裏にある下灯だけが過剰に効いて 2.1×** を生んでいた(左右灯は x=±66 で板外・壁脇のため損失大).**修正は 1 行**: `scale([sw/2, sh/2, 1.0])`. + - **検証 (spp=1024, max_depth=16)**: 灯間 mean 比は bottom 2.099→**1.000(最暗)** / left 1.000→1.028 / right 1.000→1.026 となり,**実機の「3 灯均等・下灯最暗」順序に一致**.線形性(単灯和≈全灯)誤差 0.34%.全灯合成 linear CoV は **0.1815→0.1641**(公式 sm-evaluate,残差 2.63×→2.36×)の**小改善にとどまる**.center/周辺比・勾配はほぼ不変. + - **論点の移動**: 灯間バランス問題は解決したが,**残る不一致は各単灯フットプリントの拡散不足**(修正後 peak/mean≈1.7 vs 実機≈1.2,シミュは中央尖り+上暗下明,実機はほぼ一様).本筋は**配光 ξ/ディフューザー拡散のモデル化**へ移った.成果物: 修正後出力 `output/{sanity_fix_1024,baseline_fix_1024}/`,比較図 `output/_slide_panels/{sim_vs_real_shape_fixed,sim_vs_real_colormap_fixed}.png`,記録 [TEST_08](06_TEST/TEST_08_面光源スケール軸バグの発見と修正.md). + - **過去結論の訂正**: [TEST_07](06_TEST/TEST_07_LEDグループ別動作確認.md) の「下灯 2.1× は直接光効率の幾何的優位で物理的に正しい/意図した設計」は**誤り**(バグ込み出力を物理で後付け正当化していた).TEST_07 冒頭に訂正注記を追加.**教訓**: シミュ出力を物理で後付け説明するとバグを設計意図と取り違える危険があり,実機グラウンドトゥルースとの灯ごと突合が有効だった.なお `lightsim/` は実測入力として `.gitignore` 対象. diff --git a/src/sm_tias_lightsim/scene/lights.py b/src/sm_tias_lightsim/scene/lights.py index b8907ba..c80ed03 100644 --- a/src/sm_tias_lightsim/scene/lights.py +++ b/src/sm_tias_lightsim/scene/lights.py @@ -61,7 +61,7 @@ transform = mi.ScalarTransform4f().look_at(origin=origin, target=target, up=up) # サイズに合わせてスケール(rectangle は ±1 なので half-size でスケール) - scale = mi.ScalarTransform4f().scale([sw / 2.0, 1.0, sh / 2.0]) + scale = mi.ScalarTransform4f().scale([sw / 2.0, sh / 2.0, 1.0]) return transform @ scale