diff --git a/CLAUDE.md b/CLAUDE.md index 5173ceb..0c614c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -73,11 +73,13 @@ ### 04_SPEC(仕様・設計) -- 画面機能仕様書: docs/04_SPEC/SPEC_01_画面機能仕様書.md +- 画面機能仕様書(画面・遷移・UI・パーミッション): docs/04_SPEC/SPEC_01_画面機能仕様書.md +- カメラ撮影仕様書(カメラ制御・撮影フロー・ファイル管理): docs/04_SPEC/SPEC_02_カメラ撮影仕様書.md +- アーキテクチャと非機能(コード構成・非機能要件): docs/04_SPEC/SPEC_03_アーキテクチャと非機能.md ### 05_TECH(技術設計・外部要求仕様) -- 定量モード設計の経緯と合意(旧 TECH_01/03/04 の往復書簡を統合・要約.現行確定仕様は SPEC_01 参照): docs/05_TECH/TECH_01_定量モード設計の経緯と合意.md +- 定量モード設計の経緯と合意(旧 TECH_01/03/04 の往復書簡を統合・要約.現行確定仕様は SPEC_02 参照): docs/05_TECH/TECH_01_定量モード設計の経緯と合意.md ### 06_TEST(テスト・実機検証) diff --git a/README.md b/README.md index 3ee47bc..1ea374e 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ ├── 01_GUIDE/ # 開発規約・ルール ├── 02_ENV/ # 環境構築 ├── 03_PLAN/ # 要件定義・開発ステップ -├── 04_SPEC/ # 画面機能仕様 +├── 04_SPEC/ # 画面・カメラ撮影・設計/非機能の各仕様 ├── 05_TECH/ # 技術設計・SmTIAS-LightSim 側との外部要求/合意 └── 06_TEST/ # 実機検証結果(カメラ診断など) ``` @@ -102,7 +102,7 @@ - [要件定義書](docs/03_PLAN/PLAN_01_要件定義書.md) - [開発ステップ](docs/03_PLAN/PLAN_02_開発ステップ.md) -- [画面機能仕様書](docs/04_SPEC/SPEC_01_画面機能仕様書.md) +- [画面機能仕様書](docs/04_SPEC/SPEC_01_画面機能仕様書.md) / [カメラ撮影仕様書](docs/04_SPEC/SPEC_02_カメラ撮影仕様書.md) / [アーキテクチャと非機能](docs/04_SPEC/SPEC_03_アーキテクチャと非機能.md) - [定量モード設計の経緯と合意](docs/05_TECH/TECH_01_定量モード設計の経緯と合意.md) - [開発進捗ログ](docs/PROGRESS.md) - [Git 運用ルール](docs/01_GUIDE/GUIDE_04_Git運用ルール.md) diff --git "a/docs/01_GUIDE/GUIDE_06_\343\202\263\343\203\274\343\203\207\343\202\243\343\203\263\343\202\260\350\246\217\347\264\204.md" "b/docs/01_GUIDE/GUIDE_06_\343\202\263\343\203\274\343\203\207\343\202\243\343\203\263\343\202\260\350\246\217\347\264\204.md" index 47a2c96..93fddf0 100644 --- "a/docs/01_GUIDE/GUIDE_06_\343\202\263\343\203\274\343\203\207\343\202\243\343\203\263\343\202\260\350\246\217\347\264\204.md" +++ "b/docs/01_GUIDE/GUIDE_06_\343\202\263\343\203\274\343\203\207\343\202\243\343\203\263\343\202\260\350\246\217\347\264\204.md" @@ -50,7 +50,7 @@ ## ディレクトリ構造 (Directory Structure) -[SPEC_01_画面機能仕様書](../04_SPEC/SPEC_01_画面機能仕様書.md) のアーキテクチャセクションで定義したディレクトリ構成に従う. +[SPEC_03 アーキテクチャと非機能](../04_SPEC/SPEC_03_アーキテクチャと非機能.md) のアーキテクチャセクションで定義したディレクトリ構成に従う. ```text lib/ diff --git "a/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" "b/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" index 1f8aa9d..3dad683 100644 --- "a/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" +++ "b/docs/03_PLAN/PLAN_02_\351\226\213\347\231\272\343\202\271\343\203\206\343\203\203\343\203\227.md" @@ -1,13 +1,13 @@ # 開発ステップ (Development Steps) -[SPEC_01_画面機能仕様書](../04_SPEC/SPEC_01_画面機能仕様書.md) の仕様をもとに,実装の順序とステップを定義する. +[04_SPEC の仕様書群(SPEC_01 画面 / SPEC_02 カメラ撮影 / SPEC_03 設計・非機能)](../04_SPEC/SPEC_01_画面機能仕様書.md) をもとに,実装の順序とステップを定義する. 各ステップは 1 つのブランチ・PR に対応させる. > **本書は初期フェーズ(Step 1〜5)の開発計画である.** 当時は撮影フォーマットとして > PNG(非圧縮)を前提としていた.その後,撮影高速化のネイティブ化 → カメラ診断 → > **DNG 定量モードへの転換** → プレビュー JPEG → 画素統計,と大きく展開している. > 定量モード転換以降の実装の経緯・設計判断は [PROGRESS.md](../PROGRESS.md) に, -> 最新の確定仕様は [SPEC_01_画面機能仕様書](../04_SPEC/SPEC_01_画面機能仕様書.md) に記録している. +> 最新の確定仕様は 04_SPEC([SPEC_01 画面](../04_SPEC/SPEC_01_画面機能仕様書.md) / [SPEC_02 カメラ撮影](../04_SPEC/SPEC_02_カメラ撮影仕様書.md) / [SPEC_03 設計・非機能](../04_SPEC/SPEC_03_アーキテクチャと非機能.md))に記録している. > 以下の各ステップ(Step 3 の PNG 保存など)は**当時の計画**であり,現行仕様とは異なる点がある. ## ステップ一覧 (Overview) diff --git "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" index 5d64ede..ab5a31a 100644 --- "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" +++ "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" @@ -1,6 +1,9 @@ -# 画面機能仕様書 (Screen & Function Specification) +# 画面機能仕様書 (Screen Specification) -本ドキュメントでは,MiniTIAS アプリの画面設計,画面遷移,カメラ制御,ファイル管理,およびアーキテクチャを定義する. +本ドキュメントでは,MiniTIAS アプリの画面設計・画面遷移・各画面の UI と動作・パーミッションを定義する. + +- カメラ制御・撮影フロー・ファイル管理は [SPEC_02 カメラ撮影仕様書](SPEC_02_カメラ撮影仕様書.md) を参照 +- アーキテクチャ・非機能要件は [SPEC_03 アーキテクチャと非機能](SPEC_03_アーキテクチャと非機能.md) を参照 ## 画面一覧 (Screen List) @@ -65,7 +68,7 @@ | シャッターボタン | 丸型ボタン(画面下部中央).タップで撮影実行.連続撮影可 | | タイマーボタン | 画面左下に配置.ON(黄色)/ OFF(グレー)を切り替える.ON 時はシャッター押下後 3 秒のカウントダウン表示の後に撮影 | | サウンドボタン | 画面右下に配置.シャッター音・保存完了音の ON(黄色)/ OFF(グレー)を切り替える.設定は次回起動時も保持(`shared_preferences`) | -| 明るさスライダー | ナビバーの直下に配置.画面の輝度を手動で調整する(デフォルト: 0.8) | +| 明るさスライダー | ナビバーの直下に配置.画面の輝度を手動で調整する(デフォルト: 0.8.詳細は SPEC_03 非機能要件) | | BottomNavigationBar | 撮影タブ(アクティブ)/ 一覧タブ.背景色は黒系で統一.保存中はタップ無効 | ### 動作仕様 @@ -78,6 +81,8 @@ - **フィードバック**: 撮影成功時にスナックバーで「保存しました: [ファイル名]」を表示する - **エラー時**: カメラ初期化失敗やストレージ書き込み失敗時は,エラーメッセージをスナックバーで表示する +> 撮影の内部処理(Camera2 マニュアル制御・撮影フロー・保存ファイル)は [SPEC_02 カメラ撮影仕様書](SPEC_02_カメラ撮影仕様書.md) を参照. + ## 一覧画面 (S-02: Gallery Screen) ### レイアウト @@ -127,166 +132,9 @@ - **画面表示時**: `Pictures/MiniTIAS/` 内のプレビュー JPEG(`.preview.jpg`)と旧通常モードの `.png` を走査し,一覧を構築する(DNG 本体は一覧では直接表示しない) - **リフレッシュ**: 撮影画面から戻った際に一覧を再取得する - **詳細表示**: サムネイルタップで詳細表示オーバーレイを開く.× ボタンで閉じる -- **削除**: 詳細表示の削除ボタンから確認ダイアログで「削除」を選択すると,ファイルをストレージから削除し,一覧から除去する +- **削除**: 詳細表示の削除ボタンから確認ダイアログで「削除」を選択すると,ファイルをストレージから削除し,一覧から除去する(連動削除の詳細は SPEC_02 ファイル管理仕様) - **空状態**: 画像が 0 件の場合は「撮影した画像がありません」のメッセージを表示する -## カメラ制御仕様 (Camera Control) - -### プレビュー設定(フレーミング用) - -| 項目 | 値 | -| --- | --- | -| 使用カメラ | フロントカメラ(インカメラ) | -| 解像度 | `ResolutionPreset.max` | -| 制御方式 | `camera` パッケージ(オート任せ) | -| フラッシュ | OFF(LED ライトはアタッチメント側で制御) | - -プレビューは見やすさ重視.撮影時は別の Camera2 セッションが走る. - -### 撮影設定(定量モード.Camera2 API マニュアル制御) - -| 項目 | 値 | -| --- | --- | -| 使用カメラ | フロントカメラ(インカメラ) | -| 解像度 | 3264×2448(センサ最大) | -| 画像フォーマット | `RAW_SENSOR`(10-bit BGGR Bayer).`DngCreator` で DNG ファイル出力 | -| `CONTROL_MODE` | OFF(マニュアル全制御) | -| `CONTROL_AE_MODE` | OFF | -| `SENSOR_EXPOSURE_TIME` | 8,333,333 ns(1/120 s)固定(アタッチメント + LED 環境で線形領域中央狙い.定量モード合意の目標 mean G = 400〜600 達成済み.05_TECH 参照) | -| `SENSOR_SENSITIVITY` | 40(センサ最低 ISO)固定 | -| `SENSOR_FRAME_DURATION` | 33,333,333 ns(30fps 相当) | -| `BLACK_LEVEL_LOCK` | true | -| `CONTROL_AWB_MODE` | OFF | -| `COLOR_CORRECTION_GAINS` | (1.0, 1.0, 1.0, 1.0) | -| `COLOR_CORRECTION_TRANSFORM` | 単位行列 | -| `NOISE_REDUCTION_MODE` | OFF | -| `EDGE_MODE` | OFF | -| `TONEMAP_MODE` | `CONTRAST_CURVE` 線形 `[(0,0),(1,1)]` | -| `SHADING_MODE` | HIGH_QUALITY(LSC マップ実値取得のため.画像は LSC 補正済み.PC 側で逆適用可) | -| `STATISTICS_LENS_SHADING_MAP_MODE` | ON | -| `CONTROL_AF_MODE` | OFF(AQUOS sense3 は固定焦点) | -| `FLASH_MODE` | OFF | - -### 既存 PNG 撮影パス(無効化済み・コードは温存) - -旧 YUV→BT.601→PNG 経路は `RawCapturePlugin.captureFullResolutionPng` として残存.現状の `takePicture` フローからは呼ばれない.差分比較やフォールバック用に保持. - -### プレビュー表示 - -- `camera` パッケージの `CameraPreview` ウィジェットを使用する -- UI 全体を `Transform.rotate(angle: pi)` で 180° 回転する -- カメラ映像に `Matrix4.diagonal3Values(-scale, -scale, 1.0)` を適用する - - X 軸反転: 鏡像表示(操作者が自然に見える向き) - - Y 軸反転: 端末が逆さのため上下を補正 -- プレビューは画面いっぱいに拡大し,はみ出す部分はクロップする(`ClipRect` + スケール計算) - -### 撮影フロー(定量モード) - -```text -シャッターボタンタップ - │ - ▼ -プレビュー停止(CameraController.dispose()) - │ - ▼ -baseName 生成(MiniTIAS_QM_YYYYMMDD_HHmmss.重複時 _1, _2 ...) - │ - ▼ -Camera2 API でフロントカメラを開く(RAW_SENSOR,3264×2448) - │ - ▼ -上記マニュアル制御パラメータを CaptureRequest に適用 - │ - ▼ -連続 3 枚キャプチャ(設定反映のため最初の 2 枚は破棄) - │ - ▼ -3 枚目の Image + TotalCaptureResult を取得 - │ - ▼ -DngCreator で DNG ファイルを直接書き込み(FileOutputStream) - → Pictures/MiniTIAS/{baseName}.dng(約 16 MB) - │ - ▼ -LSC マップ取得(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP) - │ - ▼ -メタデータ Map 構築(settings / actual / sensorCharacteristics / lscMap / image_statistics / hot_pixel_map) - │ - ▼ -Dart 側で JSON 保存 - → Pictures/MiniTIAS/{baseName}.meta.json - │ - ▼ -[並行] シャッター押下時に取得済みのライブプレビュースナップショット JPEG を保存 - → Pictures/MiniTIAS/{baseName}.preview.jpg - ※ スナップショット取得失敗時は preview.jpg を保存しない(撮影全体は失敗扱いにしない) - │ - ▼ -MediaStore 通知(DNG / meta.json / preview.jpg すべて scanFile) - │ - ▼ -Camera2 を閉じ,プレビューを再開 - │ - ▼ -スナックバーで保存完了を通知 -``` - -### ライフサイクル管理 - -- `WidgetsBindingObserver` を使用し,アプリのライフサイクルを監視する -- **バックグラウンド遷移時**(`paused`): カメラリソースを解放する(`CameraController.dispose()`) -- **フォアグラウンド復帰時**(`resumed`): カメラを再初期化する -- 画面遷移でカメラを使用しない画面に移動した場合もカメラリソースを解放する - -## ファイル管理仕様 (File Management) - -### 保存先 - -- パス: `Pictures/MiniTIAS/`(Android 共有ストレージ) -- ネイティブ側で共有ストレージに直接書き込み,保存後に `MediaScannerConnection.scanFile`(MethodChannel `scanFile`)で MediaStore に登録する -- ディレクトリが存在しない場合は自動作成する - -### ファイル命名規則 - -撮影 1 回で 3 ファイルを生成する(baseName は共通.重複時 `_1`, `_2` ...): - -| 種別 | ファイル名 | 概要 | -| --- | --- | --- | -| RAW 画像 | `MiniTIAS_QM_YYYYMMDD_HHmmss.dng` | 解析の本体(10-bit BGGR Bayer,約 16 MB) | -| メタデータ | `MiniTIAS_QM_YYYYMMDD_HHmmss.meta.json` | 撮影設定 + 実適用値 + LSC マップ + センサ特性(約 24 KB) | -| プレビュー | `MiniTIAS_QM_YYYYMMDD_HHmmss.preview.jpg` | アプリ内表示用の縮小プレビュー.シャッター瞬間のライブプレビュー(自動 ISP 処理済み YUV→JPEG)をそのまま保存.解像度はプレビューストリーム依存(おそらく 1280×720 程度) | - -- タイムスタンプはデバイスのローカル時刻を使用する -- 旧通常モードの `MiniTIAS_YYYYMMDD_HHmmss.png` は引き続き一覧に表示可能(後方互換).新規撮影は QM 形式で行われる - -### 重複回避ロジック - -```text -baseName = "MiniTIAS_QM_{timestamp}" - -IF "{baseName}.dng" が存在しない: - RETURN baseName - -suffix = 1 -WHILE "{baseName}_{suffix}.dng" が存在する: - suffix += 1 - -RETURN "{baseName}_{suffix}" -``` - -※ baseName をもとに `.dng` / `.meta.json` / `.preview.jpg` の 3 ファイルを共通名で生成する. - -### 削除 - -- 一覧画面の詳細表示オーバーレイから削除を実行する -- 削除前に確認ダイアログを表示する - - `.preview.jpg` の場合:「この画像を削除しますか?(DNG ファイルとメタデータも一緒に削除されます)」 - - `.png`(旧通常モード)の場合:「この画像を削除しますか?」 -- `.preview.jpg` 削除時は同 baseName の `.dng` と `.meta.json` も**連動削除**する -- ファイルシステムから物理削除する(ゴミ箱機能なし) -- 削除後,MediaStore からも当該エントリを除去する - ## パーミッション管理 (Permission Handling) ### 必要なパーミッション @@ -320,90 +168,3 @@ - ストレージ権限は撮影時・一覧表示時にそれぞれ確認する - `permission_handler` パッケージを使用する - -## アーキテクチャ (Architecture) - -### 状態管理 - -- `provider` パッケージを使用する -- 画面ごとに ChangeNotifier を作成し,ビジネスロジックを UI から分離する - -### Provider 構成 - -| Provider | 責務 | -| --- | --- | -| `CameraProvider` | カメラの初期化・プレビュー制御・撮影実行・ライフサイクル管理 | -| `GalleryProvider` | 画像一覧の取得・キャッシュ・削除操作 | -| `DiagnosticsProvider` | カメラ診断の実行・結果保存(インフラのみ・UI 非表示) | - -### ディレクトリ構成 - -```text -lib/ -├── main.dart # アプリエントリポイント,Provider 登録 -├── app.dart # MaterialApp 定義,テーマ設定,180° 回転 -├── providers/ -│ ├── camera_provider.dart # カメラ制御の状態管理 -│ ├── gallery_provider.dart # 画像一覧の状態管理 -│ └── diagnostics_provider.dart # カメラ診断(インフラのみ) -├── screens/ -│ ├── home_screen.dart # BottomNavigationBar による画面切替 -│ ├── capture_screen.dart # 撮影画面 (S-01) -│ └── gallery_screen.dart # 一覧画面 (S-02) -├── widgets/ -│ ├── camera_preview.dart # カメラプレビューウィジェット -│ ├── shutter_button.dart # シャッターボタン -│ └── image_grid.dart # サムネイルグリッド -└── services/ - ├── file_service.dart # ファイル保存・命名・削除 - ├── permission_service.dart # パーミッション管理 - ├── raw_capture_service.dart # ネイティブ撮影(DNG/PNG)・診断の MethodChannel - └── sound_service.dart # シャッター音・保存完了音の制御 -``` - -※ 撮影・画像変換・診断の実処理は Android ネイティブ -`android/.../RawCapturePlugin.kt`(Camera2 / `DngCreator`)が担い, -`raw_capture_service.dart` が MethodChannel で橋渡しする。 - -### レイヤー構成 - -```text -┌─────────────────────────────────┐ -│ Screens(画面) │ UI 層: ウィジェット構築のみ -├─────────────────────────────────┤ -│ Widgets(部品) │ 再利用可能な UI コンポーネント -├─────────────────────────────────┤ -│ Providers(状態管理) │ ビジネスロジック・状態保持 -├─────────────────────────────────┤ -│ Services(サービス) │ 外部リソースとのインタフェース -└─────────────────────────────────┘ -``` - -- **Screens** は Provider を `context.watch` / `context.read` で参照し,直接 Service を呼ばない -- **Providers** は Service を呼び出し,結果を状態として保持する -- **Services** はプラットフォーム API やファイルシステムとの橋渡しを行う - -## 非機能要件 (Non-functional Requirements) - -### 画面輝度 - -- アプリ起動時に画面の輝度をデフォルト値(0.8)に設定する -- ユーザーが明るさスライダーで任意に調整できる -- アプリ終了時にシステムの輝度設定に復帰する -- アタッチメント装着時に光センサーで画面が暗くなることへの対策 - -### パフォーマンス - -- 撮影操作から次の撮影が可能になるまでを,研究運用上ストレスのない時間に収める(旧 PNG 経路は約 2 秒.DNG 定量モードは設定反映のための 3 枚連続キャプチャ + 約 16 MB の DNG 書き込みのぶん長くなる) -- 一覧画面のサムネイル読み込みは非同期で行い,画面表示をブロックしない - -### データ保全 - -- 撮影データは端末ローカルにのみ保存する(クラウド同期なし) -- 定量モードは RAW(DNG,10-bit BGGR Bayer)で保存し,ISP 処理による情報欠落を防ぐ(旧通常モードは非圧縮 PNG) - -### 対応端末 - -- 試験端末: AQUOS sense3(SH-02M,Android 9) -- minSdkVersion: `flutter.minSdkVersion`(`android/app/build.gradle.kts` が参照する Flutter 規定値) -- targetSdkVersion: Flutter デフォルト(最新安定版に追従) diff --git "a/docs/04_SPEC/SPEC_02_\343\202\253\343\203\241\343\203\251\346\222\256\345\275\261\344\273\225\346\247\230\346\233\270.md" "b/docs/04_SPEC/SPEC_02_\343\202\253\343\203\241\343\203\251\346\222\256\345\275\261\344\273\225\346\247\230\346\233\270.md" new file mode 100644 index 0000000..888e9e8 --- /dev/null +++ "b/docs/04_SPEC/SPEC_02_\343\202\253\343\203\241\343\203\251\346\222\256\345\275\261\344\273\225\346\247\230\346\233\270.md" @@ -0,0 +1,164 @@ +# カメラ・撮影仕様書 (Camera & Capture Specification) + +本ドキュメントでは,MiniTIAS のカメラ制御・撮影フロー・保存ファイルの仕様を定義する. + +- 画面・UI・パーミッションは [SPEC_01 画面機能仕様書](SPEC_01_画面機能仕様書.md) を参照 +- アーキテクチャ・非機能要件は [SPEC_03 アーキテクチャと非機能](SPEC_03_アーキテクチャと非機能.md) を参照 +- 定量モードの設計経緯・合意(SHADING_MODE 等の根拠)は [05_TECH](../05_TECH/TECH_01_定量モード設計の経緯と合意.md) を参照 + +## カメラ制御仕様 (Camera Control) + +### プレビュー設定(フレーミング用) + +| 項目 | 値 | +| --- | --- | +| 使用カメラ | フロントカメラ(インカメラ) | +| 解像度 | `ResolutionPreset.max` | +| 制御方式 | `camera` パッケージ(オート任せ) | +| フラッシュ | OFF(LED ライトはアタッチメント側で制御) | + +プレビューは見やすさ重視.撮影時は別の Camera2 セッションが走る. + +### 撮影設定(定量モード.Camera2 API マニュアル制御) + +| 項目 | 値 | +| --- | --- | +| 使用カメラ | フロントカメラ(インカメラ) | +| 解像度 | 3264×2448(センサ最大) | +| 画像フォーマット | `RAW_SENSOR`(10-bit BGGR Bayer).`DngCreator` で DNG ファイル出力 | +| `CONTROL_MODE` | OFF(マニュアル全制御) | +| `CONTROL_AE_MODE` | OFF | +| `SENSOR_EXPOSURE_TIME` | 8,333,333 ns(1/120 s)固定(アタッチメント + LED 環境で線形領域中央狙い.定量モード合意の目標 mean G = 400〜600 達成済み.05_TECH 参照) | +| `SENSOR_SENSITIVITY` | 40(センサ最低 ISO)固定 | +| `SENSOR_FRAME_DURATION` | 33,333,333 ns(30fps 相当) | +| `BLACK_LEVEL_LOCK` | true | +| `CONTROL_AWB_MODE` | OFF | +| `COLOR_CORRECTION_GAINS` | (1.0, 1.0, 1.0, 1.0) | +| `COLOR_CORRECTION_TRANSFORM` | 単位行列 | +| `NOISE_REDUCTION_MODE` | OFF | +| `EDGE_MODE` | OFF | +| `TONEMAP_MODE` | `CONTRAST_CURVE` 線形 `[(0,0),(1,1)]` | +| `SHADING_MODE` | HIGH_QUALITY(LSC マップ実値取得のため.画像は LSC 補正済み.PC 側で逆適用可) | +| `STATISTICS_LENS_SHADING_MAP_MODE` | ON | +| `CONTROL_AF_MODE` | OFF(AQUOS sense3 は固定焦点) | +| `FLASH_MODE` | OFF | + +### 既存 PNG 撮影パス(無効化済み・コードは温存) + +旧 YUV→BT.601→PNG 経路は `RawCapturePlugin.captureFullResolutionPng` として残存.現状の `takePicture` フローからは呼ばれない.差分比較やフォールバック用に保持. + +### プレビュー表示 + +- `camera` パッケージの `CameraPreview` ウィジェットを使用する +- UI 全体を `Transform.rotate(angle: pi)` で 180° 回転する +- カメラ映像に `Matrix4.diagonal3Values(-scale, -scale, 1.0)` を適用する + - X 軸反転: 鏡像表示(操作者が自然に見える向き) + - Y 軸反転: 端末が逆さのため上下を補正 +- プレビューは画面いっぱいに拡大し,はみ出す部分はクロップする(`ClipRect` + スケール計算) + +### 撮影フロー(定量モード) + +```text +シャッターボタンタップ + │ + ▼ +プレビュー停止(CameraController.dispose()) + │ + ▼ +baseName 生成(MiniTIAS_QM_YYYYMMDD_HHmmss.重複時 _1, _2 ...) + │ + ▼ +Camera2 API でフロントカメラを開く(RAW_SENSOR,3264×2448) + │ + ▼ +上記マニュアル制御パラメータを CaptureRequest に適用 + │ + ▼ +連続 3 枚キャプチャ(設定反映のため最初の 2 枚は破棄) + │ + ▼ +3 枚目の Image + TotalCaptureResult を取得 + │ + ▼ +DngCreator で DNG ファイルを直接書き込み(FileOutputStream) + → Pictures/MiniTIAS/{baseName}.dng(約 16 MB) + │ + ▼ +LSC マップ取得(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP) + │ + ▼ +メタデータ Map 構築(settings / actual / sensorCharacteristics / lscMap / image_statistics / hot_pixel_map) + │ + ▼ +Dart 側で JSON 保存 + → Pictures/MiniTIAS/{baseName}.meta.json + │ + ▼ +[並行] シャッター押下時に取得済みのライブプレビュースナップショット JPEG を保存 + → Pictures/MiniTIAS/{baseName}.preview.jpg + ※ スナップショット取得失敗時は preview.jpg を保存しない(撮影全体は失敗扱いにしない) + │ + ▼ +MediaStore 通知(DNG / meta.json / preview.jpg すべて scanFile) + │ + ▼ +Camera2 を閉じ,プレビューを再開 + │ + ▼ +スナックバーで保存完了を通知 +``` + +### ライフサイクル管理 + +- `WidgetsBindingObserver` を使用し,アプリのライフサイクルを監視する +- **バックグラウンド遷移時**(`paused`): カメラリソースを解放する(`CameraController.dispose()`) +- **フォアグラウンド復帰時**(`resumed`): カメラを再初期化する +- 画面遷移でカメラを使用しない画面に移動した場合もカメラリソースを解放する + +## ファイル管理仕様 (File Management) + +### 保存先 + +- パス: `Pictures/MiniTIAS/`(Android 共有ストレージ) +- ネイティブ側で共有ストレージに直接書き込み,保存後に `MediaScannerConnection.scanFile`(MethodChannel `scanFile`)で MediaStore に登録する +- ディレクトリが存在しない場合は自動作成する + +### ファイル命名規則 + +撮影 1 回で 3 ファイルを生成する(baseName は共通.重複時 `_1`, `_2` ...): + +| 種別 | ファイル名 | 概要 | +| --- | --- | --- | +| RAW 画像 | `MiniTIAS_QM_YYYYMMDD_HHmmss.dng` | 解析の本体(10-bit BGGR Bayer,約 16 MB) | +| メタデータ | `MiniTIAS_QM_YYYYMMDD_HHmmss.meta.json` | 撮影設定 + 実適用値 + LSC マップ + センサ特性(約 24 KB) | +| プレビュー | `MiniTIAS_QM_YYYYMMDD_HHmmss.preview.jpg` | アプリ内表示用の縮小プレビュー.シャッター瞬間のライブプレビュー(自動 ISP 処理済み YUV→JPEG)をそのまま保存.解像度はプレビューストリーム依存(おそらく 1280×720 程度) | + +- タイムスタンプはデバイスのローカル時刻を使用する +- 旧通常モードの `MiniTIAS_YYYYMMDD_HHmmss.png` は引き続き一覧に表示可能(後方互換).新規撮影は QM 形式で行われる + +### 重複回避ロジック + +```text +baseName = "MiniTIAS_QM_{timestamp}" + +IF "{baseName}.dng" が存在しない: + RETURN baseName + +suffix = 1 +WHILE "{baseName}_{suffix}.dng" が存在する: + suffix += 1 + +RETURN "{baseName}_{suffix}" +``` + +※ baseName をもとに `.dng` / `.meta.json` / `.preview.jpg` の 3 ファイルを共通名で生成する. + +### 削除 + +- 一覧画面の詳細表示オーバーレイから削除を実行する +- 削除前に確認ダイアログを表示する + - `.preview.jpg` の場合:「この画像を削除しますか?(DNG ファイルとメタデータも一緒に削除されます)」 + - `.png`(旧通常モード)の場合:「この画像を削除しますか?」 +- `.preview.jpg` 削除時は同 baseName の `.dng` と `.meta.json` も**連動削除**する +- ファイルシステムから物理削除する(ゴミ箱機能なし) +- 削除後,MediaStore からも当該エントリを除去する diff --git "a/docs/04_SPEC/SPEC_03_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\343\201\250\351\235\236\346\251\237\350\203\275.md" "b/docs/04_SPEC/SPEC_03_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\343\201\250\351\235\236\346\251\237\350\203\275.md" new file mode 100644 index 0000000..724b121 --- /dev/null +++ "b/docs/04_SPEC/SPEC_03_\343\202\242\343\203\274\343\202\255\343\203\206\343\202\257\343\203\201\343\203\243\343\201\250\351\235\236\346\251\237\350\203\275.md" @@ -0,0 +1,93 @@ +# アーキテクチャ・非機能仕様書 (Architecture & Non-functional Specification) + +本ドキュメントでは,MiniTIAS のアーキテクチャ(コード構成)と非機能要件を定義する. + +- 画面・UI・パーミッションは [SPEC_01 画面機能仕様書](SPEC_01_画面機能仕様書.md) を参照 +- カメラ制御・撮影フロー・ファイル管理は [SPEC_02 カメラ撮影仕様書](SPEC_02_カメラ撮影仕様書.md) を参照 + +## アーキテクチャ (Architecture) + +### 状態管理 + +- `provider` パッケージを使用する +- 画面ごとに ChangeNotifier を作成し,ビジネスロジックを UI から分離する + +### Provider 構成 + +| Provider | 責務 | +| --- | --- | +| `CameraProvider` | カメラの初期化・プレビュー制御・撮影実行・ライフサイクル管理 | +| `GalleryProvider` | 画像一覧の取得・キャッシュ・削除操作 | +| `DiagnosticsProvider` | カメラ診断の実行・結果保存(インフラのみ・UI 非表示) | + +### ディレクトリ構成 + +```text +lib/ +├── main.dart # アプリエントリポイント,Provider 登録 +├── app.dart # MaterialApp 定義,テーマ設定,180° 回転 +├── providers/ +│ ├── camera_provider.dart # カメラ制御の状態管理 +│ ├── gallery_provider.dart # 画像一覧の状態管理 +│ └── diagnostics_provider.dart # カメラ診断(インフラのみ) +├── screens/ +│ ├── home_screen.dart # BottomNavigationBar による画面切替 +│ ├── capture_screen.dart # 撮影画面 (S-01) +│ └── gallery_screen.dart # 一覧画面 (S-02) +├── widgets/ +│ ├── camera_preview.dart # カメラプレビューウィジェット +│ ├── shutter_button.dart # シャッターボタン +│ └── image_grid.dart # サムネイルグリッド +└── services/ + ├── file_service.dart # ファイル保存・命名・削除 + ├── permission_service.dart # パーミッション管理 + ├── raw_capture_service.dart # ネイティブ撮影(DNG/PNG)・診断の MethodChannel + └── sound_service.dart # シャッター音・保存完了音の制御 +``` + +※ 撮影・画像変換・診断の実処理は Android ネイティブ +`android/.../RawCapturePlugin.kt`(Camera2 / `DngCreator`)が担い, +`raw_capture_service.dart` が MethodChannel で橋渡しする。 + +### レイヤー構成 + +```text +┌─────────────────────────────────┐ +│ Screens(画面) │ UI 層: ウィジェット構築のみ +├─────────────────────────────────┤ +│ Widgets(部品) │ 再利用可能な UI コンポーネント +├─────────────────────────────────┤ +│ Providers(状態管理) │ ビジネスロジック・状態保持 +├─────────────────────────────────┤ +│ Services(サービス) │ 外部リソースとのインタフェース +└─────────────────────────────────┘ +``` + +- **Screens** は Provider を `context.watch` / `context.read` で参照し,直接 Service を呼ばない +- **Providers** は Service を呼び出し,結果を状態として保持する +- **Services** はプラットフォーム API やファイルシステムとの橋渡しを行う + +## 非機能要件 (Non-functional Requirements) + +### 画面輝度 + +- アプリ起動時に画面の輝度をデフォルト値(0.8)に設定する +- ユーザーが明るさスライダーで任意に調整できる +- アプリ終了時にシステムの輝度設定に復帰する +- アタッチメント装着時に光センサーで画面が暗くなることへの対策 + +### パフォーマンス + +- 撮影操作から次の撮影が可能になるまでを,研究運用上ストレスのない時間に収める(旧 PNG 経路は約 2 秒.DNG 定量モードは設定反映のための 3 枚連続キャプチャ + 約 16 MB の DNG 書き込みのぶん長くなる) +- 一覧画面のサムネイル読み込みは非同期で行い,画面表示をブロックしない + +### データ保全 + +- 撮影データは端末ローカルにのみ保存する(クラウド同期なし) +- 定量モードは RAW(DNG,10-bit BGGR Bayer)で保存し,ISP 処理による情報欠落を防ぐ(旧通常モードは非圧縮 PNG) + +### 対応端末 + +- 試験端末: AQUOS sense3(SH-02M,Android 9) +- minSdkVersion: `flutter.minSdkVersion`(`android/app/build.gradle.kts` が参照する Flutter 規定値) +- targetSdkVersion: Flutter デフォルト(最新安定版に追従) diff --git "a/docs/05_TECH/TECH_01_\345\256\232\351\207\217\343\203\242\343\203\274\343\203\211\350\250\255\350\250\210\343\201\256\347\265\214\347\267\257\343\201\250\345\220\210\346\204\217.md" "b/docs/05_TECH/TECH_01_\345\256\232\351\207\217\343\203\242\343\203\274\343\203\211\350\250\255\350\250\210\343\201\256\347\265\214\347\267\257\343\201\250\345\220\210\346\204\217.md" index deec804..035d393 100644 --- "a/docs/05_TECH/TECH_01_\345\256\232\351\207\217\343\203\242\343\203\274\343\203\211\350\250\255\350\250\210\343\201\256\347\265\214\347\267\257\343\201\250\345\220\210\346\204\217.md" +++ "b/docs/05_TECH/TECH_01_\345\256\232\351\207\217\343\203\242\343\203\274\343\203\211\350\250\255\350\250\210\343\201\256\347\265\214\347\267\257\343\201\250\345\220\210\346\204\217.md" @@ -2,11 +2,11 @@ 本書は SmTIAS-LightSim 側と MiniTIAS 側の**往復書簡(旧 TECH_01 要求仕様 / TECH_03 実装報告・相談 / TECH_04 回答)を統合・要約したアーカイブ**である.定量撮影モードがなぜ・どう決まったかの記録に絞る. -- **現行の確定仕様** → [SPEC_01 画面機能仕様書](../04_SPEC/SPEC_01_画面機能仕様書.md) +- **現行の確定仕様** → [SPEC_02 カメラ撮影仕様書](../04_SPEC/SPEC_02_カメラ撮影仕様書.md)(画面は [SPEC_01](../04_SPEC/SPEC_01_画面機能仕様書.md)、設計・非機能は [SPEC_03](../04_SPEC/SPEC_03_アーキテクチャと非機能.md)) - **実装の経緯・設計判断・失敗パターン** → [PROGRESS.md](../PROGRESS.md) - **実機能力の生データ** → [camera_diagnostics_20260531_161734.json](../06_TEST/camera_diagnostics_20260531_161734.json) -> 原文の往復書簡(詳細な検証履歴 TEST_01/03、評価仕様 SPEC_03/04 等への参照を含む)は SmTIAS-LightSim リポジトリ側に残る.本リポジトリには当該ファイルは存在しない. +> 原文の往復書簡には SmTIAS-LightSim リポジトリ側のファイル(検証履歴 TEST_01/03、評価仕様 SPEC_03/04 など。**本リポジトリの SPEC_03 とは別物**)への参照が含まれるが、それらは SmTIAS-LightSim 側に残り本リポジトリには存在しない. ## 背景:なぜ定量モードが必要だったか @@ -32,7 +32,7 @@ ## 確定した撮影設定(要点) -全パラメータは [SPEC_01 §撮影設定](../04_SPEC/SPEC_01_画面機能仕様書.md) に記載。要点のみ: +全パラメータは [SPEC_02 §撮影設定](../04_SPEC/SPEC_02_カメラ撮影仕様書.md) に記載。要点のみ: - `CONTROL_MODE=OFF` で AE/AWB/AF/NR/EDGE をすべて OFF、TONEMAP は線形 - 露光 **1/120s**・ISO **40** 固定、`BLACK_LEVEL_LOCK=true`