# 画面設計書 5-カメラベース画面（抽象）

## 概要

本ドキュメントは、TF DemoアプリにおけるCameraActivity（カメラベース画面・抽象クラス）の画面設計書である。カメラプレビュー・権限管理・フレーム処理の共通基盤となる抽象Activityであり、ClassifierActivity、DetectorActivity、StylizeActivityの親クラスとして機能する。

### 本画面の処理概要

**業務上の目的・背景**：TF Demoアプリの3つのカメラベースActivity（画像分類、物体検出、スタイル変換）に共通するカメラプレビュー表示、権限管理、フレームデータ取得・変換処理を共通基盤として提供するために本クラスが必要である。Camera2 APIと旧Camera APIの両方に対応し、デバイスの対応状況に応じて自動的に適切なAPIを選択する。

**画面へのアクセス方法**：本クラスは抽象クラスであり、直接起動されることはない。子クラス（ClassifierActivity, DetectorActivity, StylizeActivity）が継承して使用する。

**主要な操作・処理内容**：
1. カメラ権限（CAMERA）とストレージ権限（WRITE_EXTERNAL_STORAGE）のチェック・リクエスト
2. Camera2 APIまたは旧Camera APIの選択（isHardwareLevelSupported()によるハードウェアレベル判定）
3. CameraConnectionFragment（Camera2対応）またはLegacyCameraConnectionFragment（非対応時）の生成・配置
4. Camera2 APIからのフレーム取得（onImageAvailable: YUV_420_888→YUVバイト配列）
5. 旧Camera APIからのフレーム取得（onPreviewFrame: YUV420SPバイト配列）
6. YUV→RGB変換の遅延実行（imageConverterランナブル）
7. バックグラウンドスレッド（HandlerThread: "inference"）での推論処理実行基盤
8. デバッグモードの切り替え（ボリュームキー等）
9. OverlayViewへのコールバック登録

**画面遷移**：子クラスのsetFragment()呼び出しにより、R.id.containerにCameraConnectionFragmentまたはLegacyCameraConnectionFragmentを配置する。

**権限による表示制御**：カメラ権限とストレージ権限が未付与の場合は権限リクエストダイアログを表示する。権限付与後にsetFragment()が呼び出される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 52 | 画像変換操作（Image Operations） | 主機能 | YUV420→ARGB8888への色空間変換・フレームデータの前処理を共通基盤として提供 |
| 6 | カメラ接続フラグメント | 遷移先機能 | Camera2 API対応デバイスでCameraConnectionFragmentを生成・配置 |
| 7 | レガシーカメラ接続フラグメント | 遷移先機能 | Camera2非対応デバイスでLegacyCameraConnectionFragmentを生成・配置 |

## 画面種別

抽象基底画面（直接表示されない共通基盤）

## URL/ルーティング

直接起動されないため、ルーティングは存在しない。パッケージ: `org.tensorflow.demo`、クラス: `CameraActivity`（abstract）。

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|---------|------|
| カメラフレーム（Camera2） | 入力 | Image (YUV_420_888) | Camera2 APIのImageReaderから取得される画像フレーム |
| カメラフレーム（Legacy） | 入力 | byte[] (YUV420SP) | 旧Camera APIのonPreviewFrameから取得されるバイト配列 |
| yuvBytes | 中間 | byte[3][] | YUVプレーン別のバイト配列 |
| rgbBytes | 出力 | int[] | ARGB8888形式のRGBピクセル配列（子クラスがgetRgbBytes()で取得） |
| previewWidth/Height | 出力 | int | プレビューフレームの幅・高さ |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| メインコンテナ | FrameLayout (container) | カメラFragmentを配置するコンテナ |
| デバッグオーバーレイ | OverlayView (debug_overlay) | デバッグ情報を描画するオーバーレイ |

## イベント仕様

### 1-Camera2フレーム取得イベント（onImageAvailable）

Camera2 APIのImageReaderからフレームが利用可能になった際に呼び出される。

処理フロー:
1. previewWidth/Heightが0の場合はリターン
2. rgbBytesが未初期化の場合は初期化
3. reader.acquireLatestImage()で最新画像を取得
4. isProcessingFrame=trueなら画像をクローズしてリターン
5. fillBytes()でYUVプレーンデータをyuvBytesに格納
6. imageConverterランナブルを設定（ImageUtils.convertYUV420ToARGB8888）
7. postInferenceCallbackランナブルを設定（image.close()とisProcessingFrame=false）
8. processImage()（抽象メソッド）を呼び出し

### 2-旧Camera APIフレーム取得イベント（onPreviewFrame）

旧Camera APIのプレビューコールバックとして呼び出される。

処理フロー:
1. isProcessingFrame=trueならドロップ
2. 初回はrgbBytesを初期化し、onPreviewSizeChosen()を呼び出し
3. yuvBytes[0]にバイトデータを格納
4. imageConverterランナブルを設定（ImageUtils.convertYUV420SPToARGB8888）
5. postInferenceCallbackランナブルを設定（camera.addCallbackBufferとisProcessingFrame=false）
6. processImage()を呼び出し

### 3-デバッグモード切り替えイベント

ボリュームキー（上下）、L1ボタン、D-padセンターキーを押下するとdebugフラグが反転し、requestRender()とonSetDebug()が呼び出される。

## データベース更新仕様

### 操作別データベース影響一覧

本画面はデータベースを使用しない。

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| なし | なし | なし | データベース操作なし |

## メッセージ仕様

| メッセージID | メッセージ内容 | 種別 | 表示条件 |
|-------------|--------------|------|---------|
| MSG-01 | "Camera AND storage permission are required for this demo" | 警告 | 権限リクエストの理由を表示する場合 |
| MSG-02 | "No Camera Detected" | エラー | chooseCamera()で利用可能なカメラが見つからなかった場合 |

## 例外処理

| 例外状態 | 処理内容 |
|---------|---------|
| カメラ権限未付与 | requestPermission()で権限リクエストダイアログを表示 |
| カメラ未検出 | Toastメッセージを表示しActivityをfinish() |
| CameraAccessException | Logger.e()でエラーログ出力 |
| onImageAvailableでのException | Logger.e()でエラーログ出力、Trace.endSection()で計測終了 |
| HandlerThread.join()でのInterruptedException | Logger.e()でエラーログ出力 |

## 備考

- 抽象メソッド: processImage(), onPreviewSizeChosen(), getLayoutId(), getDesiredPreviewFrameSize()
- レイアウト: `activity_camera.xml`
- Camera2 API使用条件: 外部カメラまたはFULLハードウェアレベル以上
- onPauseでfinish()が呼ばれるため、バックグラウンドに移動するとActivityが終了する
- HandlerThread名: "inference"

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | CameraActivity.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/CameraActivity.java` | フィールド変数（57-71行目）: debug, handler, handlerThread, useCamera2API, isProcessingFrame, yuvBytes, rgbBytes, previewWidth/Height |

**読解のコツ**: isProcessingFrameが排他制御の中心。imageConverterとpostInferenceCallbackの2つのRunnableが遅延実行パターンの鍵。

#### Step 2: ライフサイクルとFragment配置を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | CameraActivity.java | 同上 | onCreate()（74-86行目）: レイアウト設定と権限チェック |
| 2-2 | CameraActivity.java | 同上 | setFragment()（352-386行目）: chooseCamera()→Camera2/Legacy分岐→Fragment配置 |
| 2-3 | CameraActivity.java | 同上 | chooseCamera()（317-350行目）: Camera2 API対応判定ロジック |

**主要処理フロー**:
1. **74-86行目**: FLAG_KEEP_SCREEN_ON設定、activity_camera.xml設定、権限チェック
2. **317-350行目**: バックカメラ選択、useCamera2APIフラグ設定
3. **339-341行目**: Camera2 API判定（LENS_FACING_EXTERNALまたはFULLレベル以上）
4. **352-386行目**: useCamera2APIに基づくFragment生成・R.id.containerへの配置

#### Step 3: フレーム処理パイプラインを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | CameraActivity.java | 同上 | onImageAvailable()（155-215行目）: Camera2フレーム処理 |
| 3-2 | CameraActivity.java | 同上 | onPreviewFrame()（107-149行目）: 旧Cameraフレーム処理 |
| 3-3 | CameraActivity.java | 同上 | getRgbBytes()（90-93行目）: imageConverter実行によるYUV→RGB変換 |

### プログラム呼び出し階層図

```
CameraActivity (abstract, extends Activity)
    |
    +-- onCreate()
    |       +-- setContentView(activity_camera)
    |       +-- hasPermission() → setFragment() / requestPermission()
    |
    +-- setFragment()
    |       +-- chooseCamera() → cameraId, useCamera2API
    |       +-- [Camera2] CameraConnectionFragment.newInstance()
    |       +-- [Legacy] LegacyCameraConnectionFragment()
    |       +-- fragmentManager.replace(R.id.container)
    |
    +-- onImageAvailable() [Camera2]
    |       +-- reader.acquireLatestImage()
    |       +-- fillBytes(planes, yuvBytes)
    |       +-- imageConverter = ImageUtils.convertYUV420ToARGB8888
    |       +-- processImage() [abstract]
    |
    +-- onPreviewFrame() [Legacy]
    |       +-- onPreviewSizeChosen() [初回]
    |       +-- imageConverter = ImageUtils.convertYUV420SPToARGB8888
    |       +-- processImage() [abstract]
    |
    +-- onResume() → HandlerThread("inference").start()
    +-- onPause() → finish(), handlerThread.quitSafely()
```

### データフロー図

```
[入力]                        [処理]                              [出力]

Camera2 API ------> onImageAvailable -----> fillBytes --------> yuvBytes[3][]
(ImageReader)      (YUV_420_888)          (Plane→byte[])           |
                                                                    v
旧Camera API -----> onPreviewFrame ------> yuvBytes[0] = bytes     |
(PreviewCallback)  (YUV420SP)                                      |
                                                                    v
                                          imageConverter ---------> rgbBytes[]
                                          (YUV→ARGB8888)           (ARGB8888)
                                                                    |
                                          processImage() [abstract] |
                                          (子クラスで実装)            v
                                                            子クラスの推論処理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| CameraActivity.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/CameraActivity.java` | ソース | カメラ処理の抽象基底Activity |
| CameraConnectionFragment.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/CameraConnectionFragment.java` | ソース | Camera2 APIプレビューFragment |
| LegacyCameraConnectionFragment.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/LegacyCameraConnectionFragment.java` | ソース | 旧Camera APIプレビューFragment |
| ImageUtils.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/env/ImageUtils.java` | ソース | YUV→RGB変換ユーティリティ |
| OverlayView.java | `tensorflow/tools/android/test/src/org/tensorflow/demo/OverlayView.java` | ソース | デバッグ描画用オーバーレイView |
| activity_camera.xml | `tensorflow/tools/android/test/res/layout/activity_camera.xml` | レイアウト | メインActivityレイアウト |
