本ドキュメントでは,SmTIAS-Capture のプレビュー表示における 向き変換(反転) と クロップ拡大 の仕組み,座標系の定義,および変更時の確認手順を定義する.
重要:撮影画像(DNG)には影響しない. 本書が扱う向き変換・クロップは すべてプレビュー表示のみの処理である.保存される DNG は別経路(Camera2)で取得する.
スマホを画面が下向きになるようにアタッチメントへ載せて口元を撮影する運用のため, カメラの生プレビューをそのまま表示すると,撮影者から見て上下・左右が反転している. そこで「撮影者が自然に見える向き」に整える変換を表示時に適用する.
正しい表示状態(撮影者が見る向き)の特徴:
基準スクリーンショットについて: 実機の基準画像は被写体(顔・口元)の 個人情報を含むため Git にはコミットせず,
.gitignore除外のlocal_reference/にローカル保管する.確認時はその画像と見比べる(§5).
本プロジェクトでは以下の2つの座標系を区別する.コード内コメントもこの用語に統一する.
| 用語 | 意味 |
|---|---|
| 生フレーム座標 | カメラから来たままの向き(向き変換適用前).OrientedPreview の入力. |
| 表示後座標 | 向き変換を適用し,撮影者が見る向きにした後の座標(正規化 0.0–1.0).PreviewRegion / RegionCrop が扱う座標系. |
PreviewRegion のクロップ座標は必ず「表示後座標」で定義する.
(向き変換の後に切り出すため.生フレーム座標ではない.)
プレビューには ライブ系(描画) と スナップショット系(保存中表示) の2系統があり,
いずれも最終的に同じ RegionCrop(同じ region)でクロップして表示を一致させる.
┌─ ライブプレビュー系 (Flutter 描画) ───────────┐
│ AspectRatio(h/w で横→縦補正) │
生カメラフレーム │ OrientedPreview(向き変換 = 180°回転) │
(生フレーム座標) ────┤ oriented_preview.dart │── RegionCrop(region)
│ ※ camera_preview.dart │ 表示後座標で
└──────────────────────────────────────────────┘ クロップ&拡大
┌─ スナップショット系 (native 変換) ────────────┐ ▲
最新フレーム │ convertYuvToJpeg(rotation:90, mirror:true) │ │ 同じ region・
(_latestFrame) ────┤ camera_provider.dart │── CroppedSnapshot
│ = 向き処理済みの JPEG(表示後座標) │ → RegionCrop(region)
└──────────────────────────────────────────────┘──────┘
同じ「撮影者が見る向き」を作るのに,2箇所で別々の手段を使っている.
| 系統 | 向き処理 | 実装箇所 |
|---|---|---|
| ライブ(Flutter 描画) | Matrix4.diagonal3Values(-1, -1, 1)(X 反転+Y 反転=180°回転) | lib/widgets/oriented_preview.dart(kDisplayFlip) |
| スナップショット(native) | rotation:90 + mirror:true(Kotlin 側で回転・ミラー) | lib/providers/camera_provider.dart(convertYuvToJpeg 呼び出し) |
向きを変更したいときは,この2箇所を整合させる必要がある. 片方だけ変えるとライブ表示と保存スナップショットがズレる. 変更後は必ず §5 の確認手順を実施すること.
lib/widgets/region_crop.dart が表示後座標 0.0–1.0 の矩形 PreviewRegion を受け取り,
以下を行う(ライブ・スナップショット共通).
contain ズーム(縦横同一スケールで歪み防止)ClipRect ではみ出しを切り取るクロップ領域は assets/config/preview_region.json から読み込み,
読み込み失敗・不正値の場合は PreviewRegion.full(全画面・クロップなし)にフォールバックする
(lib/services/preview_region_service.dart).
領域の調整には scripts/preview_region_picker.html を用いる.
向き変換・クロップに関わる変更を行ったら,2段階で確認する.
flutter test test/widgets/preview_orientation_golden_test.dart
OrientedPreview・RegionCrop に流し,正解ゴールデン画像と照合する意図的に向きを変更した場合は,ゴールデンが古い正解のままなので失敗する. その場合のみ
flutter test --update-goldensで正解を更新し, 更新後のゴールデン画像が意図通りかを目視確認してからコミットする.
local_reference/ 配下,個人情報のため非コミット)
と並べて目視で一致を確認する自動チェック(段階1)は「変わっていないこと」を守り, 実機確認(段階2)は「そもそも正しいこと」を守る.役割が異なるため両方行う.
| ファイル | 役割 |
|---|---|
lib/models/preview_region.dart | クロップ領域(表示後座標)の不変モデル |
lib/services/preview_region_service.dart | preview_region.json の読み込み・フォールバック |
lib/widgets/oriented_preview.dart | 向き変換(ライブ系,kDisplayFlip) |
lib/widgets/region_crop.dart | クロップ拡大(ライブ・スナップショット共通) |
lib/widgets/camera_preview.dart | ライブプレビュー(向き変換+クロップ) |
lib/widgets/cropped_snapshot.dart | 保存中スナップショット表示(クロップのみ) |
lib/providers/camera_provider.dart | スナップショット系の向き処理(rotation:90, mirror:true) |
assets/config/preview_region.json | クロップ領域の設定値 |
scripts/preview_region_picker.html | クロップ領域の調整ツール |
test/widgets/preview_orientation_golden_test.dart | 向き・クロップの回帰テスト(段階1) |
local_reference/(非コミット) | 実機確認の基準スクリーンショット保管場所(段階2,個人情報のため Git 除外) |