diff --git a/CLAUDE.md b/CLAUDE.md index c2ea6bd..78ef078 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ ## 開発進捗 -最新: フェーズ 7(実装)— Step 8 基準設計キャリブレーションの **Step 8-14(物理モデル精緻化)** 進行中.面光源スケール軸バグ修正([TEST_08](docs/06_TEST/TEST_08_面光源スケール軸バグの発見と修正.md))後の「形の不一致」切り分け([TEST_09](docs/06_TEST/TEST_09_照明分布の形不一致の原因切り分け.md))で,bottom 単灯 spp=1024・d=16 の統一条件により**実験1 反射回数・実験2 反射率・実験3 配光(spot cutoff)・実験5 壁の光沢化(blend BSDF w×α 12 ジョブ,バグ込み旧 A1 の正式再検証)をすべてクリア=光源側・材料側レバー出尽くし**(いずれも実機の平らさ peak/mean 1.22 に届かず,光沢化は全条件で悪化).実験5 で**実機の灯間 NCC を初定量化: 0.85/0.61/0.41 と強く非対称な相補性**(シミュ diffuse は 0.96 で 3 灯ほぼ同形,光沢化でも一様に 0.86 止まり)=等方な材料変更では原理的に再現不能で,**形と相補性の残差はともに B1 筐体幾何に確定**.次は実験4(幾何の漏れ点検=漏れの定量化・開口の閉じテスト,到達目標アンカーは実機灯間 NCC).詳細な経緯・設計判断・失敗パターンは [docs/PROGRESS.md](docs/PROGRESS.md). +最新: フェーズ 7(実装)— Step 8 基準設計キャリブレーションの **Step 8-14(物理モデル精緻化)** 進行中.[TEST_09](docs/06_TEST/TEST_09_照明分布の形不一致の原因切り分け.md) で光源側・材料側レバー出尽くし=**形と相補性の残差は B1 筐体幾何に確定**(実機灯間 NCC 0.85/0.61/0.41 が到達目標アンカー)を受け,[TEST_10](docs/06_TEST/TEST_10_後側ロフト変形w1p4実験.md) で**初の幾何変更実験**(後側ロフト 0.5→1.4,blend w0.05_a0.1・spp=1024・d=16,単灯 3+全灯 2)を完了.**bottom 単灯 peak/mean 1.672→1.508 と幾何レバーが形を動かすことを初実証**したが,**灯間 NCC は不動(0.95x→0.96x)=対称なキャビティ拡大では非対称相補性に近づかない**(全灯 CoV もほぼ不変).非対称相補性の再現には灯ごとに光路が変わる非対称幾何が必要=次は実験4(幾何の漏れ点検=漏れの定量化・開口の閉じテスト).詳細な経緯・設計判断・失敗パターンは [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_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" "b/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" index 682b340..7cfa9ef 100644 --- "a/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" +++ "b/docs/06_TEST/TEST_02_\343\202\267\343\203\237\343\203\245\343\203\254\343\203\274\343\202\267\343\203\247\343\203\263\345\256\237\350\241\214\350\250\230\351\214\262.md" @@ -58,6 +58,9 @@ | `output/blend_bottom/{left,right}_w{W}_a{A}/` (各 5 ディレクトリ) | 8-14 TEST_09 実験5 拡張 | `configs/_tmp_blend_bottom/{left,right}_w{W}_a{A}.yaml`(縮小グリッド w ∈ {0.05, 0.3} × α ∈ {0.1, 0.6} + Phase 2 の w0.6_a0.1) | 同上 (`--side left/right`) | 「w 単調悪化」3 灯共通.灯間 NCC は diffuse 0.96 → w0.6 で 0.86 だが常に 3 ペア均等(実機 0.85/0.61/0.41 の非対称に届かず) | TEST_09 実験5 Phase 2+拡張.**実機の灯間 NCC(相補性の非対称)を初定量化=実験4 の到達目標アンカー** | | `output/blend_bottom/summary{,_left,_right}.csv` | 8-14 TEST_09 実験5 | — | — | 各灯の形指標集約 (job, weight, alpha, peak_mean, top_bot, ncc) | `scripts/build_blend_bottom_panel.py` の集約出力 | | `output/_slide_panels/blend_{bottom,left,right}_shape_roi.png` | 8-14 TEST_09 実験5 | — | — | 基準+実機+w×α グリッドの形比較パネル(ROI 揃え・各セル独立 1–99% 正規化・実寸アスペクト) | TEST_09 実験5 視覚比較用・発表スライド素材 | +| `output/loft_w1p4/{bottom,left,right,full,full_base}/` (5 ディレクトリ) | TEST_10 | `configs/_tmp_loft_w1p4/{bottom,left,right,full,full_base}.yaml`(blend w0.05_a0.1・spp=1024・d=16.単灯 3 本+全灯は後側を `後側_w1p4.stl` に差し替え,full_base のみ元 `後側.stl` の全灯 blend 基準) | full 系は `configs/eval_baseline_dng_target_shape.yaml` | bottom 単灯 peak/mean 1.672→1.508 と初の幾何レバー実証.灯間 NCC は 0.95x→0.96x で相補性不動.全灯 CoV 0.1717→0.1701 — [TEST_10](TEST_10_後側ロフト変形w1p4実験.md) 参照 | TEST_10 後側ロフト変形実験.対称な幾何変更では非対称相補性は動かないことを確定 | +| `output/loft_w1p4/summary.csv` | TEST_10 | — | — | 単灯 3+全灯 2 の形指標集約 (side, version, peak_mean, top_bot, ncc_vs_real) | TEST_10 数値追跡用 | +| `output/_slide_panels/loft_w1p4_shape_roi.png` | TEST_10 | — | — | 基準・w1p4・実機の 4 行 × 3 列形比較パネル(ROI 揃え・各セル独立 1–99% 正規化) | TEST_10 視覚比較用 | ### sweep_summary.csv の値(参考、TEST_01 line と一致確認用) @@ -101,6 +104,9 @@ | LED 配光比較パネル生成 | `uv run python scripts/build_led_distribution_panel.py` (輝度マップ 2 段 + 既存指標 + 新指標カーブ.要 `output/led_distribution_sweep/summary.csv` + 各 `luminance.npy`) | | blend 単灯スイープ (TEST_09 実験5) | `configs/_tmp_blend_bottom/` の各 YAML を `uv run sm-render --scene configs/_tmp_blend_bottom/{job}.yaml --output output/blend_bottom/{job}` で順次実行 (1 ジョブ約 10.5 分) | | blend 単灯の形指標解析・パネル生成 | `uv run python scripts/build_blend_bottom_panel.py --side {bottom,left,right}` (要 `output/blend_bottom/` の各 `luminance.npy` + 実機 `lightsim/luminance_maps/*.npy`) | +| loft_w1p4 レンダ (TEST_10) | `$env:DRJIT_LIBLLVM_PATH = "C:\Program Files\LLVM\bin\LLVM-C.dll"; uv run sm-render --scene configs/_tmp_loft_w1p4/{job}.yaml --output output/loft_w1p4/{job}/` (job ∈ {bottom, left, right, full, full_base}.単灯 約 10 分・全灯 約 10 分/本) | +| loft_w1p4 全灯評価 (TEST_10) | `uv run sm-evaluate --image output/loft_w1p4/{full,full_base}/luminance.npy --config configs/eval_baseline_dng_target_shape.yaml --output output/loft_w1p4/{full,full_base}/` | +| loft_w1p4 形指標解析・パネル生成 (TEST_10) | `uv run python scripts/build_loft_w1p4_panel.py` (要 `output/loft_w1p4/` と元メッシュ基準 `output/blend_bottom/` の各 `luminance.npy` + 実機 `lightsim/luminance_maps/*.npy`.summary.csv と 4×3 パネルを再生成) | ## 過去の整理実績 (Cleanup History) diff --git "a/docs/06_TEST/TEST_10_\345\276\214\345\201\264\343\203\255\343\203\225\343\203\210\345\244\211\345\275\242w1p4\345\256\237\351\250\223.md" "b/docs/06_TEST/TEST_10_\345\276\214\345\201\264\343\203\255\343\203\225\343\203\210\345\244\211\345\275\242w1p4\345\256\237\351\250\223.md" index 6f10a27..029adf2 100644 --- "a/docs/06_TEST/TEST_10_\345\276\214\345\201\264\343\203\255\343\203\225\343\203\210\345\244\211\345\275\242w1p4\345\256\237\351\250\223.md" +++ "b/docs/06_TEST/TEST_10_\345\276\214\345\201\264\343\203\255\343\203\225\343\203\210\345\244\211\345\275\242w1p4\345\256\237\351\250\223.md" @@ -39,30 +39,82 @@ ## シミュレーション計画 (Simulation Plan) -### 比較条件(TEST_09 統一条件を踏襲) +### 材料・光源条件の選定(確定: blend w=0.05, α=0.1) -筐体幾何の効果を測る到達目標アンカーは **実機の灯間 NCC = 0.85 / 0.61 / 0.41(非対称な相補性)**(TEST_09 実験5 で初定量化.シミュ diffuse は 0.96 で 3 灯ほぼ同形). +TEST_09 の結果パネルを見比べて,材料条件は **blendbsdf(diffuse 0.85 + roughconductor ggx α=0.1), weight=0.05** に確定した(ユーザー選定).選定意図: -| 項目 | 値 | 根拠 | -| --- | --- | --- | -| シーン | `configs/_tmp_sanity_1024/scene_{bottom,left,right}.yaml` の後側メッシュを `後側_w1p4.stl` に差し替え | TEST_09 統一条件(単灯・spp=1024・max_depth=16・diffuse R=0.85) | -| 出力 | `output/loft_w1p4/{bottom,left,right}/` | TEST_02 命名規則 | -| 評価 | `scripts/build_blend_bottom_panel.py` 系の形指標(peak/mean・top/bot・灯間 NCC) | TEST_09 実験5 と直接比較可能 | -| 判定 | 灯間 NCC が diffuse 基準 0.96 から実機の非対称(0.85/0.61/0.41)方向へ動くか,peak/mean が 1.585 → 実機 1.22 方向へ動くか | 幾何レバーの有効性判定 | +- 純 diffuse(Lambertian)壁は多重反射が光源・幾何の情報を洗い流すため(TEST_09 §追加観察),筐体変形の効果が相補性に出にくい.弱い光沢を入れることで LED 位置・幾何の効きを残す +- w=0.05 は単灯の形が diffuse 基準と同等(peak/mean 1.604 vs 1.585,NCC 0.717 vs 0.721)=形を壊さない範囲で最弱の光沢 +- 光源は area(Lambertian 面光源)のまま,spp=1024・max_depth=16 の TEST_09 統一条件を踏襲 -### 補助条件(全灯・全体均一性) +### 実行構成(5 ジョブ) -形レバーとして唯一実証済みの spot 配光(cutoff=90°,[TEST_02 の候補 ξ](TEST_02_シミュレーション実行記録.md) で CoV=0.165・angular_ratio=0.352)を全灯で流し,CoV・3 ゾーン等の全体指標も比較する. +筐体幾何の効果を測る到達目標アンカーは **実機の灯間 NCC = 0.85 / 0.61 / 0.41(非対称な相補性)**(TEST_09 実験5 で初定量化.シミュ diffuse は 0.96,w0.05_a0.1 は 0.954〜0.959 で 3 灯ほぼ同形). -| 項目 | 値 | -| --- | --- | -| シーン | `configs/scene_baseline_spot.yaml` の後側メッシュ差し替え版 | -| 評価 | `configs/eval_baseline_dng_target_shape.yaml`(新形状指標込み) | -| 出力 | `output/loft_w1p4/full_spot/` | +| ジョブ | シーン config | 出力 | 比較基準(元メッシュ側) | +| --- | --- | --- | --- | +| 単灯 bottom | `configs/_tmp_loft_w1p4/bottom.yaml` | `output/loft_w1p4/bottom/` | `output/blend_bottom/w0.05_a0.1/`(既存) | +| 単灯 left | `configs/_tmp_loft_w1p4/left.yaml` | `output/loft_w1p4/left/` | `output/blend_bottom/left_w0.05_a0.1/`(既存) | +| 単灯 right | `configs/_tmp_loft_w1p4/right.yaml` | `output/loft_w1p4/right/` | `output/blend_bottom/right_w0.05_a0.1/`(既存) | +| 全灯 (w1p4) | `configs/_tmp_loft_w1p4/full.yaml` | `output/loft_w1p4/full/` | 下記 full_base | +| 全灯 (元メッシュ基準) | `configs/_tmp_loft_w1p4/full_base.yaml` | `output/loft_w1p4/full_base/` | 新規(全灯 blend は修正後 LED では未実施のため基準も新規レンダ) | + +単灯/全灯とも `scene_baseline.yaml` 系の LED 配置(bottom 6 灯/全 18 灯)で,差分は後側メッシュのみ(`後側.stl` → `後側_w1p4.stl`). + +### 判定基準 + +- **相補性**: 灯間 NCC が w0.05_a0.1 基準(0.954〜0.959)から実機の非対称(0.85/0.61/0.41)方向へ動くか +- **平らさ**: 単灯 peak/mean が 1.60 前後 → 実機 1.22 方向へ動くか(ROI 揃え,`scripts/build_blend_bottom_panel.py` 系の形指標) +- **全体均一性**: 全灯どうし(full vs full_base)で CoV・形状指標を比較 ## 結果 (Results) -(実行後に追記) +実行日: 2026-07-26.全 7 レンダ(単灯 3 + 全灯 2,途中 full 系 2 本は相対メッシュパス起因の失敗後に絶対パス修正で再実行). +数値は `output/loft_w1p4/summary.csv`,比較図は `output/_slide_panels/loft_w1p4_shape_roi.png`. + +### 単灯の形(ROI 揃え) + +| 灯 | peak/mean (基準 → w1p4) | top/bot (基準 → w1p4) | NCC 対実機 (基準 → w1p4) | 実機 | +| --- | --- | --- | --- | --- | +| bottom | 1.672 → **1.508** | 0.872 → 0.887 | 0.713 → 0.701 | p/m 1.218, t/b 0.931 | +| left | 1.613 → 1.584 | 0.875 → 0.878 | 0.664 → 0.640 | p/m 1.267, t/b 0.922 | +| right | 1.546 → 1.564 | 0.890 → 0.891 | 0.789 → 0.777 | p/m 1.201, t/b 0.946 | + +### 灯間 NCC(相補性,mean 正規化・フル frame) + +| ペア | 基準 (元メッシュ) | w1p4 | 実機 | +| --- | --- | --- | --- | +| bottom vs left | 0.959 | 0.962 | **0.853** | +| bottom vs right | 0.961 | 0.963 | **0.606** | +| left vs right | 0.954 | 0.956 | **0.414** | + +### 全灯(18 灯,`eval_baseline_dng_target_shape.yaml` + 形指標) + +実機全灯マップは `reference_measurement/quantitative/MiniTIAS_QM_20260601_124500.dng`(Step 8-13 正規ターゲット・暗闇撮影)から単灯と同一手順(linear raw + LSC 順適用 + Rec.709 + REAL_ROI)で生成し `lightsim/luminance_maps/full_MiniTIAS_QM_20260601_124500.npy` に保存(ROI mean=207.82・CoV=0.0695 で Step 8-13 記録値と一致確認済み). + +| 指標 | 基準 (full_base) | w1p4 (full) | 実機 | +| --- | --- | --- | --- | +| CoV | 0.1717 | 0.1701 | 0.0695 | +| C/P ratio | 1.328 | 1.326 | 1.091 | +| gradient [%] | 24.72 | 24.61 | 8.35 | +| angular_ratio | 0.352 | 0.345 | — | +| peak/mean (ROI) | 1.491 | 1.486 | **1.155** | +| top/bot (ROI) | 0.878 | 0.885 | **0.919** | +| NCC 対実機 (ROI) | 0.744 | 0.737 | 1.0 | +| 相互 NCC (ROI, mean 正規化) | 0.920 | — | — | + +### 読み取れること + +1. **bottom 単灯は明確に平ら化**(peak/mean 1.672 → 1.508,縦勾配も実機方向)=**筐体幾何レバーが形を動かせることの初実証**.ロフト重み増でフレア部キャビティが広がり,bottom 灯の光の混合が増えた効果と整合する. +2. しかし **left / right はほぼ不動**で,NCC 対実機は 3 灯とも微減(-0.01〜-0.02). +3. **相補性(灯間 NCC)は全く動かず**(0.95x → 0.96x でむしろ微増=さらに同形化).対称なキャビティ拡大は「混ぜる力」を強めるだけで,実機の非対称相補性(0.85/0.61/0.41)には近づかない. +4. 全灯の均一性指標はほぼ不変(CoV -0.9%,NCC 対実機 0.744→0.737)=このロフト変形は**全灯運用での均一性・形をほぼ変えない**.実機全灯(peak/mean 1.155・CoV 0.0695)との差は依然大きい. + +### 解釈と次への示唆 + +- ロフト重み(対称なフレア拡大)は「bottom の平ら化」には効くが,**形の残差の本丸(非対称相補性)を動かすレバーではない**.TEST_09 実験5 の結論「等方な変更では 3 ペアを一様にしか動かせない」は,**等方な材料変更だけでなく対称な幾何変更にも当てはまる**ことが示された. +- 実機の非対称相補性を再現するには,**灯ごとに光路が変わる非対称な幾何**(開口・仕切り・カバー形状の左右非対称,箱の閉じ方の違い)を検討する必要がある.TEST_09 実験4(幾何の漏れ点検)の優先度が改めて裏付けられた. +- 印刷判断の観点では,本変形は全灯均一性を悪化させない(CoV 微改善)ため**印刷して実機検証するリスクは低い**が,シミュ上の期待効果も小さい. ## 参照 (References) diff --git a/docs/PROGRESS.md b/docs/PROGRESS.md index a48c36d..6798088 100644 --- a/docs/PROGRESS.md +++ b/docs/PROGRESS.md @@ -70,3 +70,4 @@ - **修正後 3 単灯は形がほぼ同一**(TEST_09 に追記): 板が間接光のみ+Lambertian 拡散壁で照らされるため形が光源でなく筐体で決まる(積分球原理=多重反射が光源形状を洗い流す).結果,下・左・右の単灯フットプリントが互いに **NCC≈0.92** とほぼ同形(縦勾配 top/bot≈0.85/1.06 が 3 灯共通).**実機(TEST_04)の「3 灯が相補的に別方向を照らす」を再現できていない**=シミュでは LED 位置の効きが弱すぎる→ (a) B1 幾何不正確 or (b) 間接化しすぎ(実機は各灯の指向性がもっと残る)を示唆.実験3 の追加手がかり. - **実験5(壁の光沢化 blend BSDF)再検証完了 — 材料側レバー出尽くし・実機の非対称相補性を初定量化 (2026-07-14)**: 旧 Step 8-14 β の A1 blend 実験はバグ込み・3 灯・絶対指標のため,修正後 LED・bottom 単灯・形指標(TEST_09 流)で正式に再検証.`printer_white` を blendbsdf 化し w∈{0.05,0.15,0.3,0.6}×α∈{0.1,0.3,0.6} の 12 ジョブ(spp=1024, d=16,約 2h)+最強光沢 w0.6_a0.1 で left/right 単灯を追加レンダ.**結果 ①(平らさ)**: 全 12 ジョブが純 diffuse(peak/mean 1.585・NCC 0.721)より悪化し w に単調悪化(最悪 w0.6_a0.1 で 2.145/0.667)=光沢はホットスポットを作り実機の平らさと逆方向.**結果 ②(相補性)**: 灯間 NCC は diffuse 0.96 → blend 0.86 に低下し「Lambertian 壁が光源形状を洗い流す」機構自体は実証されたが,**実機の灯間 NCC は 0.85/0.61/0.41 と強く非対称**(今回初定量化,実験4 以降の到達目標アンカー)で,等方な材料変更では原理的に再現不能.単灯の対実機 NCC も 3 灯とも悪化するトレードオフ.**結論: 壁の反射特性は大きさ(実験2)・種類(実験5)とも本筋でなく,B1 筐体幾何が唯一の残存候補として確定**.副次修正: TEST_09 §追加観察の灯間 NCC≈0.92 は低 spp ノイズによる過小(spp=1024 で 0.96)と判明し訂正.成果物: `output/blend_bottom/`(summary.csv 含む),解析 `scripts/build_blend_bottom_panel.py`,図 `output/_slide_panels/blend_bottom_shape_roi.png`,記録 [TEST_09 §実験5](06_TEST/TEST_09_照明分布の形不一致の原因切り分け.md).次: 実験4(幾何の漏れ点検=漏れの定量化・開口の閉じテスト). - **left/right 拡張 (2026-07-14)**: 時間短縮のため縮小グリッド w∈{0.05,0.3}×α∈{0.1,0.6}(+w0.6_a0.1)を left/right 単灯でも実走(8 ジョブ).**「w 単調悪化」は 3 灯共通**(w0.6_a0.1 で left 2.03/right 2.20 まで尖る).3 灯揃う 5 材料条件の灯間 NCC は w に対し一様に低下(0.96→0.92→0.86)するが**常に 3 ペア均等で実機の非対称(0.85/0.61/0.41)に近づかず**=「等方な材料変更は 3 ペアを一様にしか動かせない」をスイープ全域で確認.解析スクリプトは --side 引数で 3 灯対応化. +- **TEST_10 後側ロフト変形 w=1.4 実験完了 — 幾何レバーが形を動かすことを初実証・対称変形では相補性は不動 (2026-07-26)**: 実験4 に先行し,ユーザーが Fusion で後側筐体のロフトパラメータを 0.5→1.4 に変えた `後側_w1p4.stl`(下側フレア部 Z=-150〜-86 が外側へ最大 10mm 膨張・体積 +6.3%,LED/評価板側は不変)をシミュで評価.TEST_09 パネルの見た目からユーザーが選定した blend w=0.05・α=0.1(形を壊さない最弱光沢で幾何の効きを残す意図)・spp=1024・d=16 で単灯 3+全灯 2(元メッシュ全灯 blend 基準含む)の 5 レンダ.**結果 ①**: bottom 単灯 peak/mean 1.672→1.508 と明確に平ら化=**筐体幾何が形を動かせることの初実証**(TEST_09 の光源側・材料側レバーはすべて不発だった).ただし left/right は不動で NCC 対実機は 3 灯とも微減.**結果 ②**: 灯間 NCC は 0.95x→0.96x と不動(むしろ同形化)=**対称なキャビティ拡大では実機の非対称相補性(0.85/0.61/0.41)に近づかない**.TEST_09 実験5 の「等方な材料変更では 3 ペアを一様にしか動かせない」が対称幾何変更にも当てはまると判明.**結果 ③**: 全灯 CoV 0.1717→0.1701 でほぼ不変=印刷リスクは低いが期待効果も小.**次への示唆**: 非対称相補性の再現には灯ごとに光路が変わる非対称幾何(開口・仕切り・カバー形状)が必要=実験4(幾何の漏れ点検)の優先度を再確認.失敗パターン: 全灯 config を `_tmp_loft_w1p4/` に置いたら scene_baseline.yaml 由来の相対メッシュパスが config 起点解決で file not found→絶対パス化で解決.成果物: `output/loft_w1p4/`(summary.csv 含む),図 `output/_slide_panels/loft_w1p4_shape_roi.png`,記録 [TEST_10](06_TEST/TEST_10_後側ロフト変形w1p4実験.md). diff --git a/scripts/build_loft_w1p4_panel.py b/scripts/build_loft_w1p4_panel.py new file mode 100644 index 0000000..20ed111 --- /dev/null +++ b/scripts/build_loft_w1p4_panel.py @@ -0,0 +1,191 @@ +"""TEST_10 後側ロフト変形 w=1.4 実験の形比較パネル生成. + +output/loft_w1p4/ のレンダ完了後に実行すること. +行 = bottom / left / right / full,列 = 元メッシュ基準 / 後側_w1p4 / 実機 の 4×3 グリッド. +シミュ ROI と実機マップは解像度が異なる(450×464 vs 約 1400×1409)ため, +全セルを共通解像度へ resize してから描画し,行・列の枠を完全に揃える. +各セルは独立 1–99 パーセンタイル正規化(形の比較専用・明るさの絶対比較は不可). + +生成するファイル: + output/loft_w1p4/summary.csv -- 全ジョブの形指標 + output/_slide_panels/loft_w1p4_shape_roi.png -- 4×3 形比較パネル(ROI 揃え) + +使い方: + uv run python scripts/build_loft_w1p4_panel.py + +前提: + - output/loft_w1p4/{bottom,left,right,full,full_base}/luminance.npy が存在すること + - 元メッシュ単灯基準 output/blend_bottom/{,left_,right_}w0.05_a0.1/luminance.npy + - 実機マップ lightsim/luminance_maps/{side}_*.npy(単灯 3 種 + full.full は + reference_measurement/quantitative/MiniTIAS_QM_20260601_124500.dng から + 単灯と同一手順(linear raw + LSC 順適用 + Rec.709 + REAL_ROI)で生成したもの) +""" + +from __future__ import annotations + +import csv +import sys +from pathlib import Path + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +from skimage.transform import resize + +from sm_tias_lightsim.evaluation.roi_presets import SIM_ROI, extract_roi_xywh +from sm_tias_lightsim.evaluation.shape_metrics import calc_ncc + +matplotlib.rcParams["font.family"] = ["Meiryo", "Yu Gothic", "DejaVu Sans"] + +ROOT = Path(__file__).resolve().parents[1] +PANEL_PATH = ROOT / "output" / "_slide_panels" / "loft_w1p4_shape_roi.png" +SUMMARY_CSV = ROOT / "output" / "loft_w1p4" / "summary.csv" + +REAL = { + "bottom": ROOT / "lightsim" / "luminance_maps" / "bottom_SmTIAS_QM_20260615_141420.npy", + "left": ROOT / "lightsim" / "luminance_maps" / "left_SmTIAS_QM_20260615_150300.npy", + "right": ROOT / "lightsim" / "luminance_maps" / "right_SmTIAS_QM_20260615_170948.npy", + "full": ROOT / "lightsim" / "luminance_maps" / "full_MiniTIAS_QM_20260601_124500.npy", +} +BASE = { + "bottom": ROOT / "output" / "blend_bottom" / "w0.05_a0.1" / "luminance.npy", + "left": ROOT / "output" / "blend_bottom" / "left_w0.05_a0.1" / "luminance.npy", + "right": ROOT / "output" / "blend_bottom" / "right_w0.05_a0.1" / "luminance.npy", + "full": ROOT / "output" / "loft_w1p4" / "full_base" / "luminance.npy", +} + +SIDES = ["bottom", "left", "right", "full"] +VERSIONS = ["base", "w1p4", "real"] +VERSION_LABELS = {"base": "元メッシュ基準", "w1p4": "後側_w1p4", "real": "実機"} + +# 全セルの共通描画解像度(シミュ ROI と同程度.実機はダウンサンプル) +CELL_SHAPE = (450, 464) +VMIN_PERCENTILE = 1.0 +VMAX_PERCENTILE = 99.0 + + +def _load_luminance(path: Path) -> np.ndarray: + """luminance.npy を読み込み 2D 輝度にする(3D RGB は Rec.709 変換).""" + arr = np.load(path) + if arr.ndim == 3: + arr = 0.2126 * arr[..., 0] + 0.7152 * arr[..., 1] + 0.0722 * arr[..., 2] + return arr.astype(np.float64) + + +def _metrics(img: np.ndarray, real: np.ndarray | None) -> dict[str, float]: + """TEST_09/10 の形指標(peak/mean・top/bot・NCC 対実機)を算出する.""" + h = img.shape[0] + m = { + "peak_mean": float(img.max() / img.mean()), + "top_bot": float(img[: h // 2].mean() / img[h // 2 :].mean()), + } + if real is not None: + m["ncc"] = float(calc_ncc(resize(img, real.shape, anti_aliasing=True), real)) + else: + m["ncc"] = float("nan") + return m + + +def main() -> int: + """パネルと summary CSV を生成するメインエントリポイント. + + Returns: + 終了コード(成功なら 0,入力不足なら 1). + """ + cells: dict[tuple[str, str], np.ndarray] = {} + rows: list[list[str]] = [] + for side in SIDES: + real = _load_luminance(REAL[side]) if side in REAL else None + for ver in ["base", "w1p4"]: + if ver == "base": + path = BASE[side] + else: + path = ROOT / "output" / "loft_w1p4" / side / "luminance.npy" + if not path.exists(): + print(f"エラー: {path} が見つかりません", file=sys.stderr) + return 1 + roi = extract_roi_xywh(_load_luminance(path), SIM_ROI) + cells[(side, ver)] = roi + m = _metrics(roi, real) + rows.append( + [side, ver, f"{m['peak_mean']:.4f}", f"{m['top_bot']:.4f}", f"{m['ncc']:.4f}"] + ) + if real is not None: + cells[(side, "real")] = real + rm = _metrics(real, real) + rows.append([side, "real", f"{rm['peak_mean']:.4f}", f"{rm['top_bot']:.4f}", "1.0000"]) + + SUMMARY_CSV.parent.mkdir(parents=True, exist_ok=True) + with SUMMARY_CSV.open("w", encoding="utf-8", newline="") as f: + writer = csv.writer(f) + writer.writerow(["side", "version", "peak_mean", "top_bot", "ncc_vs_real"]) + writer.writerows(rows) + print(f"summary 保存: {SUMMARY_CSV}") + + # 全セルを共通解像度に resize して行・列の枠を完全に揃える + fig, axes = plt.subplots( + len(SIDES), + len(VERSIONS), + figsize=(len(VERSIONS) * 4.2, len(SIDES) * 4.2), + constrained_layout=True, + ) + for r, side in enumerate(SIDES): + for c, ver in enumerate(VERSIONS): + ax = axes[r][c] + ax.set_axis_off() + if (side, ver) not in cells: + ax.set_facecolor("0.15") + if r == 0: + ax.set_title(VERSION_LABELS[ver], fontsize=11, pad=6) + ax.text( + 0.5, + 0.5, + "実機全灯 npy 無し", + ha="center", + va="center", + transform=ax.transAxes, + fontsize=10, + color="0.4", + ) + continue + img = cells[(side, ver)] + img = resize(img / img.mean(), CELL_SHAPE, anti_aliasing=True) + ax.imshow( + img, + cmap="hot", + vmin=float(np.percentile(img, VMIN_PERCENTILE)), + vmax=float(np.percentile(img, VMAX_PERCENTILE)), + aspect="equal", + ) + m = _metrics(cells[(side, ver)], None) + title = f"p/m={m['peak_mean']:.3f} t/b={m['top_bot']:.3f}" + if r == 0: + title = f"{VERSION_LABELS[ver]}\n{title}" + ax.set_title(title, fontsize=10, pad=6) + # 行ラベル(左端セルの外側) + axes[r][0].text( + -0.06, + 0.5, + side, + ha="right", + va="center", + rotation=90, + transform=axes[r][0].transAxes, + fontsize=13, + fontweight="bold", + ) + + fig.suptitle( + "TEST_10 後側ロフト変形 w=1.4 — blend w0.05_a0.1, spp=1024, d=16, ROI 揃え\n" + "各セル独立 1–99% 正規化(形の比較専用)・全セル共通解像度", + fontsize=12, + ) + PANEL_PATH.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(PANEL_PATH, dpi=150, bbox_inches="tight") + plt.close(fig) + print(f"パネル保存: {PANEL_PATH}") + return 0 + + +if __name__ == "__main__": + sys.exit(main())