# 定量撮影モード要求仕様 (Quantitative Capture Mode Specification)

本ドキュメントは SmTIAS-LightSim プロジェクトから MiniTIAS プロジェクトに対する **撮影モード拡張の要求仕様** である．SmTIAS-LightSim のキャリブレーション作業 (Step 8 系) で判明した「実機 PNG が定量データとして揺らぐ」問題を解消するための撮影アプリ側改修を依頼する．

## 概要 (Overview)

MiniTIAS（[MiniTias](https://github.com/rintoHasegawa/MiniTias) リポジトリ）の撮影アプリに，現状の通常撮影モードに加えて **「定量撮影モード (Quantitative Capture Mode)」** を追加することを要求する．

定量撮影モードでは：

- Camera2 API のオート制御 (AE/AWB/NR/EDGE/TONEMAP) を **すべて固定または OFF** にする．
- ISP が適用した **LSC ゲインマップ** を撮影ごとに同時保存する．
- 可能なら出力フォーマットを **RAW_SENSOR (DNG)** に切り替えられるようにする．

これにより同じシーンを何度撮影しても再現性のある定量データが得られ，SmTIAS-LightSim 側のシミュレーション結果との突合が安定する．

## 背景 (Background)

### SmTIAS-LightSim と MiniTIAS の関係

- **MiniTIAS**: スマートフォンアプリ．AQUOS sense3 で白板・舌模型を撮影し PNG として保存する．
- **MiniTias-Evaluation**: PC 側．MiniTIAS が保存した PNG を読み込み照明均一性 (CoV／勾配／C/P 等) を算出する．
- **SmTIAS-LightSim**: PC 側．Mitsuba 3 で同じ筐体・LED 配置を仮想撮影し，MiniTias-Evaluation と同じ指標を計算する．

→ 3 つのプロジェクトで **同じ評価指標** を出すことで「シミュ vs 実機」を比較する．

### 現状の問題（Step 8 キャリブで判明）

Step 8 キャリブレーション ([TEST_01](../06_TEST/TEST_01_キャリブレーション検証履歴.md) 参照) で，シミュと実機の比較を試みた結果，以下の現象が観測された：

- **形状指標残差 1.6〜1.9 倍** が解消されない（OETF 順方向適用後でも）
- 輝度マップの「形」が実機（中央ピーク楕円）とシミュ（Y 軸単調勾配）で **構造的に違う** ([TEST_03](../06_TEST/TEST_03_輝度マップ形状追跡.md) 参照)
- LED 位置・反射率・配光・センサ方向 ([TEST_01](../06_TEST/TEST_01_キャリブレーション検証履歴.md) Step 8-9/8-10/8-11/8-12 系) すべてを試したが「形」は変わらず

「形を変える唯一の道」として **LSC (Lens Shading Correction) モデル化** が候補に挙がったが，さらに調査した結果，**より根本的な問題** が判明した：

- 既存 baseline 画像 (`MiniTIAS_20260408_144434`) は **MiniTIAS の通常撮影モード**で取得されており，**ISP がすべて自動制御**で動作している．
- 具体的には [MiniTIAS/docs/04_SPEC/SPEC_01](../../MiniTIAS/docs/04_SPEC/SPEC_01_画面機能仕様書.md) のとおり：
  - AE/AF 安定 1 秒待ち → STILL_CAPTURE → YUV_420_888 取得 → BT.601 で RGB → PNG 保存
  - AE / AWB / NR / EDGE / TONEMAP / SHADING はすべて Camera2 API のデフォルト (オート/ON) のまま
- これは「人間が見て自然な画像」用設定であり，**定量解析向けではない**．
- 撮影ごとに露光・WB・補正量が変動する可能性があり，**同じシーンを 2 回撮っても同じ値にならない可能性** がある．

つまり「定量比較の対象である実機 PNG 自体が揺らいでいる」状態．まずここを固定化しない限り，シミュ側でいくらモデルを精緻化しても比較できない．

## 目的 (Objective)

定量撮影モードの導入により以下を達成する：

1. **撮影再現性の確保**: 同条件で繰り返し撮影しても mean / std / 形状指標が同じ値になる．
2. **ISP 介入の最小化**: AE/AWB/NR/EDGE/TONEMAP/SHADING を固定/OFF し，センサ出力に近い値を取得する．
3. **メタデータの可視化**: ISP が適用した LSC ゲインマップを取得して別ファイル保存する．
4. **シミュ比較の土台**: SmTIAS-LightSim 側のキャリブレーション・LSC 検証が安定したデータで進められる．

## 要求仕様 (Requirements)

### 1. 撮影モード切替 UI

撮影画面 (S-01) に「定量撮影モード」のトグルスイッチを追加する．

- デフォルト: OFF（既存通常モード）
- ON 時: 後述の Camera2 API 設定をすべて適用
- 後方互換: 既存ユーザー・既存撮影ワークフローには影響しない

### 2. Camera2 API 設定（定量モード ON 時）

| キー | 値 | 目的 |
| --- | --- | --- |
| `CONTROL_MODE` | `OFF`（マニュアル全制御） | ISP オート制御の完全停止 |
| `CONTROL_AE_MODE` | `OFF` | 自動露光停止 |
| `SENSOR_EXPOSURE_TIME` | 固定値（要キャリブ，例 1/60 s） | 露光時間固定 |
| `SENSOR_SENSITIVITY` | 最低 ISO（端末依存，例 50） | ノイズ最小化 + ゲイン固定 |
| `CONTROL_AWB_MODE` | `OFF` | 自動 WB 停止 |
| `COLOR_CORRECTION_GAINS` | 固定値 (R=G=B=1.0 推奨) | チャネルゲイン固定 |
| `COLOR_CORRECTION_TRANSFORM` | 単位行列 | カラーマトリクス無効化 |
| `NOISE_REDUCTION_MODE` | `OFF` | NR 無効化（アーティファクト排除） |
| `EDGE_MODE` | `OFF` | エッジ強調無効化 |
| `TONEMAP_MODE` | `CONTRAST_CURVE` (線形カーブ) または `PRESET_CURVE` (SRGB) | トーンマッピング固定 |
| `SHADING_MODE` | **`HIGH_QUALITY`** (LSC 補正適用) または **`OFF`** (LSC 無効化) のいずれか選択可 | LSC の挙動制御（次節参照） |
| `STATISTICS_LENS_SHADING_MAP_MODE` | `ON` | LSC ゲインマップ取得有効化 |

> ※ `SHADING_MODE = OFF` を選んだ場合は cos⁴ falloff が画像に残る．後段のシミュ側で物理 cos⁴ を加える前提なら有用．通常運用では `HIGH_QUALITY` を推奨．

### 3. LSC ゲインマップの保存

撮影成功時に `CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP` から `LensShadingMap` を取得して保存する．

| 項目 | 仕様 |
| --- | --- |
| 取得 API | `result.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP)` |
| データ形状 | 4 × N × M float 配列（4 = R/Geven/Godd/B チャネル，N = 列，M = 行） |
| 値の意味 | ≥ 1.0 のゲイン係数．`corrected = raw × gain` |
| 保存形式 | NPZ（推奨）または JSON．画像ファイル名と同 prefix で `<basename>.lsc.npz` 等 |
| 保存先 | 画像と同ディレクトリ |
| マップサイズ | `CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE` から取得 |

### 4. 撮影メタデータの保存（撮影パラメータの可視化）

LSC マップに加えて，撮影時の Camera2 API パラメータを JSON で保存する．後段でシミュレーションの境界条件を再現するために必要．

```json
{
  "capture_timestamp_utc": "2026-05-28T10:00:00Z",
  "device_model": "SH-02M",
  "android_version": "9",
  "camera_id": "1",
  "hardware_level": "LIMITED",
  "capture_mode": "quantitative",
  "settings": {
    "control_mode": "OFF",
    "ae_mode": "OFF",
    "exposure_time_ns": 16666666,
    "sensor_sensitivity_iso": 50,
    "awb_mode": "OFF",
    "color_correction_gains": [1.0, 1.0, 1.0, 1.0],
    "color_correction_transform": "identity",
    "noise_reduction_mode": "OFF",
    "edge_mode": "OFF",
    "tonemap_mode": "CONTRAST_CURVE",
    "shading_mode": "HIGH_QUALITY",
    "statistics_lens_shading_map_mode": "ON"
  },
  "image_format": "YUV_420_888",
  "image_size": [4000, 3000],
  "yuv_to_rgb_matrix": "BT.601"
}
```

ファイル名規則: `<image_basename>.meta.json`

### 5. 推奨出力フォーマット

優先順位は以下：

| 優先度 | フォーマット | 説明 |
| --- | --- | --- |
| 第 1 | **`RAW_SENSOR` (DNG)** | ISP 介入ゼロ．センサ出力そのまま．[DNG GainMap 仕様](https://www.adobe.com/content/dam/acom/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf) と整合 |
| 第 2 | **`YUV_420_888` + 上記メタデータ + LSC マップ** | 現状フォーマットに情報追加．後段で逆処理可能 |
| 第 3 | **PNG（現状） + メタデータ + LSC マップ** | 最小実装．BT.601 YUV→RGB 経由のため情報量は最小 |

第 1 は実機が DNG 出力をサポートしている必要がある（AQUOS sense3 で要確認）．サポートしていない場合は第 2 を採用．

### 6. ハードウェアサポート判定

定量モード起動時に `CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL` を取得して以下を判定：

- `LEGACY`: 定量モード **不可**（マニュアル制御サポートなし）→ ユーザーに警告表示
- `LIMITED`: 部分サポート．SHADING_MODE と LSC マップ取得が可能か個別確認
- `FULL` / `LEVEL_3`: **完全サポート**

必要な能力フラグ：

- `REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR` (露光・ISO 固定)
- `REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING` (NR/EDGE/TONEMAP/SHADING 固定)
- `REQUEST_AVAILABLE_CAPABILITIES_RAW` (DNG 出力，第 1 優先時のみ必須)

判定結果は前述メタデータ JSON の `hardware_level` フィールドに記録する．

## 推奨実装方針 (Recommended Implementation)

### Flutter / camera パッケージの限界

[camera パッケージ](https://pub.dev/packages/camera) は AE/AWB/SHADING_MODE 等の詳細制御を **直接公開していない** ことが多い．定量モードを実現するには以下のいずれかが必要：

- **Platform Channel で Camera2 API を直接制御**（推奨）
  - Android Kotlin/Java 側で Camera2 API を直接叩く
  - Flutter 側は MethodChannel で「定量モード撮影」要求を送る
- **camerax_android** など低レベル制御を公開した代替パッケージへの移行
- **Native Plugin の自作**

実装規模が大きい場合は段階的に：

1. **Phase 1**: Camera2 詳細制御の MethodChannel 実装（露光・WB 固定，LSC マップ取得）
2. **Phase 2**: LSC マップ・メタデータ保存ロジック
3. **Phase 3**: 撮影画面 UI に定量モードトグル追加
4. **Phase 4**: ハードウェアサポート判定とユーザー案内
5. **Phase 5**: RAW_SENSOR (DNG) 対応（任意）

### ディレクトリ構成提案

MiniTIAS 既存構造に従い：

```text
android/app/src/main/kotlin/<package>/
└── camera/
    └── QuantitativeCameraController.kt   # Camera2 直接制御

lib/services/
├── quantitative_camera_service.dart      # Platform Channel ラッパ
├── lsc_map_service.dart                  # LSC マップ NPZ 保存
└── capture_metadata_service.dart         # メタデータ JSON 保存

lib/providers/
└── camera_provider.dart                  # 定量モード状態追加（既存 Provider 拡張）
```

## 期待される効果 (Expected Effects)

| 項目 | 改善内容 |
| --- | --- |
| 撮影再現性 | 同条件 10 連射で mean / std が ±1% 以内に揃う |
| 色温度ドリフト | AWB 固定により無彩色撮影で色シフト排除 |
| LSC 補正可視化 | ISP が適用したゲインマップが取得でき，シミュ側で逆適用可能 |
| 物理 cos⁴ 検証 | SHADING_MODE = OFF モードで実機 raw 相当を取得し，シミュ + cos⁴ と比較可能 |
| RAW 比較 (任意) | DNG 経路が確立すれば，ISP 完全排除でセンサ生値 vs Mitsuba 直接比較 |
| キャリブ作業の安定化 | SmTIAS-LightSim 側で同じ baseline を繰り返し再撮影しても結果が動かない |

## 後方互換 (Backward Compatibility)

- 通常撮影モードは現状仕様のまま維持（既存撮影ワークフローを壊さない）
- 既存 baseline 画像 (`MiniTIAS_20260408_144434`) は **「通常モードで撮影された」** ことを SmTIAS-LightSim 側で明示記録（[TEST_03](../06_TEST/TEST_03_輝度マップ形状追跡.md) に追記予定）
- 新規定量モード撮影は別命名規則を推奨：
  - 例: `MiniTIAS_QM_YYYYMMDD_HHmmss.png`（QM = Quantitative Mode）
  - メタデータ: `MiniTIAS_QM_YYYYMMDD_HHmmss.meta.json`
  - LSC マップ: `MiniTIAS_QM_YYYYMMDD_HHmmss.lsc.npz`

## 検証方法 (Verification)

定量モード実装完了後，以下を実施して受け入れ判定とする：

### V1. 再現性検証

同条件（光源 ON・同じ白板・三脚固定）で **10 回連続撮影** し，全画像で：

- mean / std の変動係数 (CoV of means) < 1%
- 形状指標 (CoV／勾配／C/P) の標準偏差 < 0.005

### V2. LSC マップ取得検証

- 全撮影で `<basename>.lsc.npz` が保存されている
- マップサイズが `CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE` と一致
- ゲイン値がすべて ≥ 1.0
- 中央 (col=N/2, row=M/2) のゲインが ≈ 1.0

### V3. SHADING_MODE 切替検証

- `SHADING_MODE = OFF` で撮影した画像が `HIGH_QUALITY` より周辺で明らかに暗い（cos⁴ falloff 可視化）
- `HIGH_QUALITY × (1 / LSC マップ)` ≈ `OFF` の関係性が成立する（数値差 < 5%）

### V4. SmTIAS-LightSim 側での突合

- 定量モード白板撮影 (SHADING_MODE = HIGH_QUALITY) を baseline として再キャリブ
- シミュ vs 実機の残差が現状 2.1× → 1.5× 以下に縮むこと（仮目標）

## 開放質問 (Open Questions)

実装着手時に確認が必要な事項：

| ID | 質問 | 確認方法 |
| --- | --- | --- |
| Q1 | AQUOS sense3 のハードウェアレベルは何か | デバッグビルドで `CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL` を読みログ出力 |
| Q2 | SH-02M のフロントカメラで `REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING` がサポートされているか | 同上．未サポートなら定量モードは部分実装 |
| Q3 | DNG (RAW_SENSOR) 出力は可能か | `REQUEST_AVAILABLE_CAPABILITIES_RAW` を確認 |
| Q4 | LSC マップサイズはどの程度か（例 13×17 等） | 実機でログ出力．データ量設計の参考 |
| Q5 | 露光時間・ISO の最適値（固定値の決定） | 白板撮影で被写体の明るさが適切かを実機で試行錯誤 |
| Q6 | 既存 `MiniTIAS_20260408_144434` を定量モードで再撮影するか | 研究方針との整合（再撮影なら現 baseline は参考扱い） |
| Q7 | 既存 PNG 保存形式 (BT.601 経由) を維持するか YUV/RAW 直接保存に変えるか | SmTIAS-LightSim 側読み込みコストとのトレードオフ |

## 関連ドキュメント (Related Documents)

- [TEST_01 キャリブレーション検証履歴](../06_TEST/TEST_01_キャリブレーション検証履歴.md) — Step 8-1〜8-12 で「実機 PNG が定量データとして揺らぐ」を発見した経緯
- [TEST_03 輝度マップ形状追跡](../06_TEST/TEST_03_輝度マップ形状追跡.md) — 形のずれの定性的記録．LSC モデル化議論の出発点
- [SPEC_04 評価仕様](../04_SPEC/SPEC_04_評価仕様.md) — sm-evaluate の OETF 適用・mean アンカー処理
- [SPEC_03 基準設計の物理構成](../04_SPEC/SPEC_03_基準設計の物理構成.md) — シミュ側物理モデル
- [MiniTIAS/docs/04_SPEC/SPEC_01 画面機能仕様書](../../MiniTIAS/docs/04_SPEC/SPEC_01_画面機能仕様書.md) — 現状のカメラ制御仕様（変更前ベースライン）
- [Android Camera2 API LensShadingMap](https://developer.android.com/reference/android/hardware/camera2/params/LensShadingMap)
- [Android Camera2 API CaptureRequest](https://developer.android.com/reference/android/hardware/camera2/CaptureRequest)
