# SmTIAS-LightSim

SmTIAS（スマートフォン舌画像撮影装置）の照明均一性を実機制作前に検討するための光散乱シミュレーションツール．
LED 配置・ディフューザー形状を仮想的に変えながら，SmTIAS-Evaluation と同じ指標（CoV／3 ゾーン分析等）で均一性を定量評価できるようにする．

## 外部参照リポジトリ

関連プロジェクトのローカルコピーは置かない．参照が必要になったときに `gh` CLI で GitHub から直接最新を読むこと．

- SmTIAS（スマホ撮影アプリ本体）→ [rintoHasegawa/SmTIAS-Capture](https://github.com/rintoHasegawa/SmTIAS-Capture)
- SmTIAS-Evaluation（照明均一性評価ツール）→ [rintoHasegawa/SmTIAS-Evaluation](https://github.com/rintoHasegawa/SmTIAS-Evaluation)

```bash
# ファイル一覧（リポジトリ構造の把握）
gh api "repos/rintoHasegawa/SmTIAS-Evaluation/git/trees/main?recursive=1" --jq '.tree[].path'

# ファイル内容の取得（常に main の最新）
gh api -H "Accept: application/vnd.github.raw" \
  "repos/rintoHasegawa/SmTIAS-Evaluation/contents/docs/PROGRESS.md"
```

これらの内容の修正は各リポジトリ側で行う（本リポジトリからは読み取り専用として扱う）．

## 開発進捗

最新: フェーズ 7（実装）— Step 8 基準設計キャリブレーションの **Step 8-14（物理モデル精緻化）** 進行中．[TEST_12](docs/06_TEST/TEST_12_シェーディング法線アーティファクトの発見と修正.md) で **Step 8 残差の支配成分がレンダリングバグ（PLY 自動スムーズ法線 × 評価板の巨大三角形）だったことを発見・修正**（TEST_11 球体筐体実験の完全軸対称化で顕在化，builder に `face_normals: true` を強制）．修正後は現行筐体全灯 **CoV 0.1717→0.0610**（実機 0.0695 を下回る）・勾配 6.8%（実機 8.35%）・C/P 1.073（実機 1.091）＝**振幅系指標は実機とほぼ整合し，「残差 2.4×」と Y 軸単調勾配の見かけは大半がアーティファクトだったと判明**．球ロフト（CoV 0.0628・完全同心円）は現行筐体と同等＝現行筐体はすでに理想積分球級で球形化の優位性なし（TEST_11 の結論は撤回・BEFORE FIX 注記参照）．単灯再検証（TEST_12 追補）で **TEST_09 中核結論「3 灯同形＝非対称相補性を再現できない」も撤回**: 修正後は灯間 NCC（平滑）0.85/0.93/0.73 の非対称で「left-right 最違い」は実機（0.92/0.66/0.44）と一致（振幅・最類似ペアは不一致＝残課題）．ロフト w1.4 の修正後再検証では**「全灯・単灯とも実質不感」が維持**（実機 w1.4 測定への予測も不変）される一方「bottom 平ら化＝幾何レバー実証」は撤回相当に弱まった．実験4（漏れ点検）の主動機は大幅に弱まり，残る差分は「形」（相補性の振幅・実機の斜め構造）の切り分けへ再設定．実機側はロフト w=1.4 版 `MiniTIAS_QM_20260726_183416.dng` が SmTIAS-Evaluation 分析待ち．詳細な経緯・設計判断・失敗パターンは [docs/PROGRESS.md](docs/PROGRESS.md)．

実装済みモジュール:
- `src/sm_tias_lightsim/uniformity.py` — Rec.709 輝度変換 + CoV/std/max-min（SmTIAS-Evaluation コピー）
- `src/sm_tias_lightsim/evaluation/zones.py` — 正規化楕円距離マップ + 3 ゾーン分類 + ゾーン別統計
- `src/sm_tias_lightsim/evaluation/radial.py` — 20 ビン放射状輝度プロファイル
- `src/sm_tias_lightsim/evaluation/roi.py` — `image_pixel` 型矩形 ROI 抽出（2D/3D 配列対応）
- `src/sm_tias_lightsim/evaluation/oetf.py` — 標準 sRGB OETF（IEC 61966-2-1）+ 露出ソルバ（OETF 後 ROI mean を実測値に対数二分でアンカー）+ 順方向適用．シミュ linear を実測 sRGB ドメインへ変換
- `src/sm_tias_lightsim/evaluation/normalize.py` — linear mean anchor 正規化（`apply_mean_anchor`）．OETF と排他適用，形状指標は厳密不変で mean のみ target_mean に揃う．`configs/eval_baseline_linear.yaml` で有効化
- `src/sm_tias_lightsim/evaluation/visualize.py` — matplotlib 可視化（輝度マップ / ヒストグラム / 放射プロファイル / ゾーンマップ）．SmTIAS-Evaluation 互換スタイル（hot cmap，zone_map は luminance 背景 + 境界線）
- `src/sm_tias_lightsim/scene/schema.py` — Pydantic ベース YAML スキーマ + ローダ．SensorConfig は perspective / orthographic 両対応．materials は `DiffuseMaterial` / `RoughConductorBSDFConfig` (alpha + distribution=ggx/beckmann，material="none" 固定で完全反射体) / `BlendBSDFConfig` (weight + components×2，再帰ネスト可能，forward reference + `model_rebuild()`) を Union として並列サポート．後方互換維持
- `src/sm_tias_lightsim/geometry/loader.py` — STL 読込（trimesh）+ `LoadedMesh` データクラス
- `src/sm_tias_lightsim/geometry/transforms.py` — mm⇄m 単位スケール変換
- `src/sm_tias_lightsim/scene/{lights,materials,sensors}.py` — Mitsuba emitter / BSDF / sensor dict 変換（sensors は orthographic 対応，lights は `area` / `point` / `directional` / `spot` 対応．Step 8-10 で `SpotLight` を追加，Step 8-14 β で `materials.py` に `_build_bsdf_dict` 再帰ヘルパを追加して `blendbsdf` ネスト対応）
- `scripts/select_white_board.py` — 実測 PNG で白色板境界を OpenCV GUI で選択（Step 8 キャリブ用）
- `scripts/visualize_roi_3d.py` — 評価板 STL に ROI を 3D 投影（PyVista，Step 8 キャリブ用）
- `src/sm_tias_lightsim/scene/builder.py` — SceneConfig → Mitsuba scene dict（PLY 一時ファイル経由）
- `src/sm_tias_lightsim/render/runner.py` — variant 管理 + `mi.render` 実行
- `src/sm_tias_lightsim/render/output.py` — PNG / luminance.npy 出力
- `src/sm_tias_lightsim/cli/render.py` — `sm-render` CLI（YAML → PNG / luminance.npy）
- `src/sm_tias_lightsim/cli/evaluate.py` — `sm-evaluate` CLI（PNG / luminance.npy → metrics.csv + figures/）．`oetf.enabled` 時は ROI 抽出後・指標算出前に sRGB OETF を順方向適用，`normalization.enabled` 時は OETF 前段に linear mean 揃え正規化（OETF と排他）．後方互換、シミュ用設定のみ
- `src/sm_tias_lightsim/visualize/baker.py` — face 中心の照度ベイク．`mode="direct"`（shadow ray + cosine + 距離減衰、直接光のみ、後方互換）/ `mode="path"`（scalar_rgb・完全シーン・コサイン重点 hemisphere MC、多重反射込み per-face、`exclude_mesh_ids` 対応）．`max_depth` 引数で path ベイクの反射回数を render 設定から独立設定（None で render 継承＝後方互換）
- `src/sm_tias_lightsim/visualize/projection.py` — orthographic センサ投影の純関数（評価板メッシュ特定 / `project_luminance_to_vertices` 頂点 bilinear 投影 / `compute_orthographic_uv` テクスチャ UV / `compute_face_camera_mask` 幾何 Z ベース巻き順非依存の評価面選択 / `compute_inward_face_mask` 法線がシーン中心側を向く内向き面判定＝幾何カリング用）．`sensors.py` look_at@scale 規約と整合（SPEC_03「画像↔物理の向き」検証済）
- `src/sm_tias_lightsim/visualize/viewer.py` — PyVista 3D ビューア．既定＝全メッシュ統一ベイク着色（**lit のみ共有 clim**・log/linear・単一カラーバー・`bake_opacity` 半透明・`hide_below` 非表示・`bake_smooth` 平滑化・depth peeling・viridis）＋**`cull_outward`（既定 ON）で外向き ≈0 面を非描画**＝暗殻の濁り/見かけ段差を解消・clim も可視内向き面に密着；`hide_sensor_marker`/`hide_led_markers` でマーカー個別非表示；`luminance_map` 指定時＝評価板 512² テクスチャ投影（E=π·L/ρ で壁と同一 W/m² スケール、内側カメラ向き face のみ、外側ニュートラル、マスク無効時 per-vertex フォールバック）．**発表スライド向け表示機能（Step 8-14 期に追加）**: `style`（"default"／"slide"．slide=白背景・座標軸/bounds 非表示・アンチエイリアス・黒文字スカラーバー `_build_scalar_bar_args`・センサマーカー自動非表示．未知値は warning で default フォールバック）／ `cmap`（None で style 既定を解決：default→viridis・slide→hot＝照明均一性評価と同一．未知名は warning で style 既定にフォールバック，`_make_irradiance_texture(cmap_name=)` も同一 cmap）／ `subdivide_max_edge`（m 単位．ベイク前に三角形を均一分割→巨大三角形のブロック感解消）／ `bake_denoise`（`denoise_face_scalars_median` 頂点経由メディアン近似でMCノイズ/firefly除去，エッジ保存．log変換後・smooth前に適用，`_get_triangle_faces` で PolyData/UnstructuredGrid 両対応）／ `bake_smooth_radius`（m 単位．`smooth_face_scalars_radius` ボクセル splat/gather 物理半径ガウス平滑化＝メモリ有界・純NumPy・sigma=radius/2．denoise後・bake_smooth前に適用，三角形スケールのチェッカーノイズ除去）／ `backface_cull`（VTK 背面カリング `culling="back"`，opacity=1.0 併用で手前壁が隠れ混色なし）
- `src/sm_tias_lightsim/geometry/subdivide.py` — 三角形メッシュ均一分割の純関数（新規）．`subdivide_mesh_to_max_edge(vertices, faces, max_edge)`＝`trimesh.remesh.subdivide_to_size` で「最大辺長 ≤ max_edge」まで決定論的に三角形分割（同一入力→同一出力，baker と viewer が独立呼び出しでも face 配列一致）．単位は呼び出し側座標系に従う（m 系で呼べば m）．STL の三角形サイズが極端に不均一（曲面 ~1mm² vs 平面壁 最大 4794mm²）でも均一な空間解像度の per-face ベイクを可能にする
- `src/sm_tias_lightsim/cli/visualize.py` — `sm-visualize` CLI（YAML → 3D 表示 / screenshot）．`--bake-mode {direct,path}` / `--bake-samples` / `--bake-max-depth`（path ベイクの反射回数、既定 16、render 設定から独立）/ `--bake-cache <npz>`（1回計算→以後即時．`--bake-subdivide` 設定値も npz に記録し不一致なら自動再ベイク）/ `--cull-outward|--no-cull-outward`（既定 ON）/ `--opacity` / `--hide-below` / `--bake-smooth` / `--bake-scale {linear,log}` / `--clip-percentile` / `--no-sensor-marker`・`--no-led-markers`・`--no-markers` / `--render <luminance.npy>`（任意・テクスチャ投影）．**発表スライド向け（Step 8-14 期に追加）**: `--style {default,slide}` / `--cmap NAME`（既定: style に応じ viridis/hot）/ `--bake-subdivide MM`（三角形を最大辺長 MM mm 以下に均一分割）/ `--bake-denoise N`（メディアンデノイズ反復）/ `--bake-smooth-radius MM`（物理半径 MM mm のガウス平滑化）/ `--backface-cull`（背面カリング）．mm 引数は内部で ×0.001 して m 系で show_scene/baker へ．`--render` 無し＝全面統一ベイク（既定）
- `src/sm_tias_lightsim/runtime/llvm_init.py` — LLVM JIT 初期化ヘルパ．`ensure_drjit_libllvm_path()` で `DRJIT_LIBLLVM_PATH` 未設定時に Windows/POSIX 標準パスを自動探索して `os.environ` 設定．`log_variant_status()` で variant 経路（llvm_/cuda_/scalar_）を INFO 明示，要求 llvm_* で実 scalar_* なら WARNING 追加．`runner.py` / `baker.py` から呼ばれ環境変数未設定時のサイレントクラッシュを防ぐ
- `src/sm_tias_lightsim/evaluation/dng_loader.py` — DNG 読み込みモジュール．SmTIAS-Evaluation `src/io/dng_loader.py` を SmTIAS-LightSim にコピーしたもの (オリジナルは SmTIAS-Evaluation 側で管理．バグ修正・機能追加はそちらで反映)．主要 API: `load_image_dng(path, demosaic, black_level, output_format)`．`output_format` は `rgb_float32` / `luma_float32` / `rgb_uint8`．linear 系出力 (`rgb_float32` / `luma_float32`) は `output_color=raw` でカラーマトリクス非適用・per-channel 比例性保持．`gamma=(1,1) + no_auto_bright=True + use_camera_wb=False + user_wb=[1,1,1,1]` で linear raw を保つ．依存: `rawpy>=0.21.0`
- `src/sm_tias_lightsim/evaluation/radial.py` — 20 ビン放射状輝度プロファイル．既存 `calc_radial_profile` に加えて SmTIAS-Evaluation `src/analysis/spatial.py:calc_radial_min_max_ratio` をマージ．動径 min/max 比 (20 点輪帯平均) は画素単位 max/min よりノイズに対しロバスト．nan ビン除外を移植時に補強 (既存 `calc_radial_profile` は空ビンを nan で返す)
- `src/sm_tias_lightsim/evaluation/shape_metrics.py` — 輝度分布「形」評価指標モジュール（新規）．現状 CoV/勾配/C/P/動径 min/max は不均一性の絶対量しか測れず，Y 軸単調勾配と中央ピーク楕円を構造的に区別できない問題を解決する 5 つの純関数: `calc_intensity_centroid`（輝度重心，5 値）/ `calc_angular_radial_decomposition`（極座標分解，angular_ratio が 0=同心円〜1=方向依存，3 値）/ `calc_ssim`（構造的類似度，scikit-image 依存，shape_reference 指定時のみ）/ `calc_ncc`（正規化相関係数，絶対輝度不変）/ `calc_intensity_pca`（weighted PCA で主軸角度と eccentricity，4 値）．mask 省略可能，完全均一画像で angular_ratio/NCC は NaN を返す．`MetricsConfig.shape_metrics=true` / `shape_reference` フィールドで有効化，後方互換保持．依存追加: `scikit-image>=0.21.0`．詳細: [TECH_11 形状指標の見直し](docs/05_TECH/TECH_11_形状指標の見直しと新指標導入.md)
※ 本欄は**最新ステップ 1 行のみ上書き更新**．詳細な進捗履歴（動機・設計判断・失敗パターン）は [docs/PROGRESS.md](docs/PROGRESS.md) に追記する．運用ルールは `.claude/rules/progress-log.md` を参照（該当ファイル編集時に自動ロードされる）．

## 必須ルール（コード実装時）

<!-- プロジェクト固有の必須ルールの要点があればここに追記する．コーディング規約等の本体は規約整備フェーズで .claude/rules/ に path-scoped rule として作成する（GUIDE_01 参照） -->

### Git 運用

- ブランチ名・コミットメッセージの書式は `.claude/rules/git-conventions.md` に従う（常時ロードされる）
- コミットは `/commit` を使用する（push・PR 作成は `/commit push`，マージ・プルまで行う場合は `/commit merge`）
- **`/commit` はユーザーが明示的に指示した時のみ実行する．Claude が自発的に `/commit` や `git commit` を呼んではならない**（`/implement` 完了後も，案内するだけで自分ではコミットしない．`/commit` skill は `disable-model-invocation` によりユーザー起動限定として強制されている）
  - **例外**: 以下の無人運転ループはユーザー承認済みの例外として専用ブランチに自律コミットする．いずれも push・PR・マージ・`main` への操作はしない（取り込みは人間が `/commit push` 等で行う）
    - `/auto-refactor`（リファクタ／ドキュメント整理ループ）→ `refactor/` 専用ブランチ
    - `/auto-audit`（バグ／脆弱性の巡回監査ループ）→ `fix/` 専用ブランチ
  - **例外（`main` へのマージ）**: `/deps-update` は，Dependabot が作成した依存更新 PR に限り，ゲート（メジャー更新でない・競合なし・CI 緑またはローカル検証緑）を満たすものを `gh pr merge` で `main` に取り込む（ユーザーが明示的に起動した時のみ．メジャー更新・CI 赤・PR が付かない alert は報告して人間が判断する）
- **ops-runner によるマージは承認済みの定常操作**: `/commit merge` および `/deps-update` では，ユーザーが明示的に起動したことを受けて，ops-runner サブエージェント（軽量モデル）が手順書に従い `git push` → `gh pr create` → `gh pr merge --merge --delete-branch` で自リポジトリの PR を `main` にマージする．force push・`--admin`・`main` への直接 push は行わない（権限設定は `docs/01_GUIDE/GUIDE_01` の「Claude Code の権限設定」）

### テンプレート同期

- テンプレートの更新は `/sync-template` で取り込む
- テンプレート由来のファイル（`.claude/agents/`・`skills/`・`rules/`・`hooks/`，`docs/01_GUIDE/`）を**このプロジェクトの都合で意図的に変更した**ときは，`.claude/template-overrides.md`（テンプレート改変台帳）に理由付きで記録する（`.claude/rules/template-customization.md`．該当ファイル編集時に自動ロードされる）．記録が無いと次回同期で改変が消えうる

### タスク管理（任意）

- やるべきことを GitHub Issues で管理してよい（solo / team 共通）．`/task-create`（起票・事前計画）→ `/task-start <Issue番号>`（着手準備）→ `/task-handoff`（中断時の引継ぎメモ投稿）を使う
- Issue は必須ではない．小さい変更や試験的作業は Issue 無しで進めてよい
- solo モードでは進捗記録（`.claude/rules/progress-log.md`）は従来どおり運用し，Issue はタスクの管理・引継ぎの補助として使う

### エージェントチーム（GUIDE_02 準拠）

- 実装は `/implement <タスク>` で開始する（コーディング → テスト → リファクタリング → ドキュメント更新）
- Phase 1 後に人間が動作確認，Phase 2 でテスト失敗時のみ方針判断
- Phase 2 成功後は Phase 3 → Phase 4 まで自動で進む（Phase 4 で更新がある場合のみ確認）
- テスト・リファクタリングをスキップしない

### 開発コマンド

- 起動・テスト・lint 等の開発コマンドは `docs/02_ENV/ENV_04_開発コマンド.md` にまとめる．**存在する場合は実行前に必ず参照し，推測でコマンドを試さない**
- コマンドが新しく確定・変更された時は同ファイルに反映する（無ければその時点で作成する．`/implement` の Phase 4 でも見直される）

### シミュレーション実験の進め方

- **幾何（メッシュ・光源・センサ位置）を変えるシーンは，本番レンダを回す前に必ず 3D で配置確認する**（`sm-visualize --no-bake --screenshot` / `scripts/show_led_geometry.py`）．確認画像をユーザーに提示し，位置の OK をもらってからレンダを実行する（2026-08-18 ユーザー指示）

### 手動確認が必要な作業（自分で完了させないこと）

以下は実装完了後にユーザーへ報告し，確認・実施を依頼すること．

- 外部サービスの設定（コンソール操作，セキュリティルール変更等）
  - 例外: `gh` CLI で操作できる GitHub リポジトリ設定（Dependabot alerts / security updates の有効化）は Claude が `gh api` で行う（手順は `.claude/skills/setup/reference.md`，背景は GUIDE_01「環境構築」）
- 実機・ブラウザでの動作確認
- ストアへのアップロード・リリース作業

## ドキュメント

設計・規約に関する情報は docs/ にある．
コードを書く前に関連するファイルを読むこと．

### 01_GUIDE（規約・ルール）

- プロジェクト立ち上げフロー: docs/01_GUIDE/GUIDE_01_プロジェクト立ち上げフロー.md
- エージェント運用ルール: docs/01_GUIDE/GUIDE_02_エージェント運用ルール.md
- ※ コーディング規約・テスト方針・構成規則・エラー処理・Git 規約・ドキュメント書式・命名規則・進捗記録・テンプレート改変記録は `.claude/rules/`（coding-style / testing / project-structure / error-handling / git-conventions / markdown-style / docs-naming / progress-log / template-customization）に path-scoped rule として定義されている（git-conventions は常時，他は該当ファイル編集時に自動ロード）

### 02_ENV（環境）

- 技術スタック: docs/02_ENV/ENV_01_技術スタック.md
- 環境構築手順: docs/02_ENV/ENV_02_環境構築手順.md
- 管理者用環境構築手順: docs/02_ENV/ENV_03_管理者用環境構築手順.md

### 03_PLAN（計画）

- 要件定義書: docs/03_PLAN/PLAN_01_要件定義書.md
- 開発ステップ: docs/03_PLAN/PLAN_02_開発ステップ.md

### 04_SPEC（仕様・設計）

- アーキテクチャ設計: docs/04_SPEC/SPEC_01_アーキテクチャ設計.md
- シーン定義仕様: docs/04_SPEC/SPEC_02_シーン定義仕様.md（シーン YAML + 評価 YAML のスキーマ）
- 基準設計の物理構成: docs/04_SPEC/SPEC_03_基準設計の物理構成.md（評価関連は SPEC_04 に分離済み）
- 評価仕様: docs/04_SPEC/SPEC_04_評価仕様.md（評価パイプライン・ROI・OETF・メトリクス・可視化・キャリブ目標と経過要約）

### 05_TECH（技術設計）

<!-- 必要に応じて実装フェーズで追加する -->

### 06_TEST（テスト）

- キャリブレーション検証履歴: docs/06_TEST/TEST_01_キャリブレーション検証履歴.md（Step 8 イテレーション 8-1〜8-7 のフル記録．仮説／変更／数値／解釈／学びを時系列で）
- シミュレーション実行記録: docs/06_TEST/TEST_02_シミュレーション実行記録.md（output/ 各サブディレクトリの索引．Step との対応・シーン/評価 config・主要指標・再現コマンド・削除推奨）
- 輝度マップ形状追跡: docs/06_TEST/TEST_03_輝度マップ形状追跡.md（数値とは独立に「形」を実機に近づける観点．比較パネル画像と LSC モデル化の構造的意義）
