Androidカメラのプレビューが横伸びする原因と直し方|CameraX・PreviewView・4:3・TransformをAIで正しく修正する方法

Android開発

Android StudioでCameraXやCamera2を使ったカメラアプリを開発していると、「撮影エリアの枠は4:3なのに、プレビュー映像だけ横方向に引き伸ばされる」という問題が起こることがあります。特にAIへ「横伸びを直して」と繰り返し依頼しているのに改善しない場合、解像度だけを変更しても根本原因に届いていない可能性があります。

カメラの横伸びを調べる際に重要なのは、カメラが出力する画像のアスペクト比、プレビューを受け取るViewのサイズ、ScaleType、回転情報、Crop、Transformの責任範囲を分けて確認することです。4:3のViewを作っただけでは、内部の映像まで自動的に正しい4:3表示になるとは限りません。

CameraXを使って単純にカメラ映像を表示するアプリなら、まずPreviewViewを利用してCameraX自身に変換処理を任せる構成を確認するのが近道です。Android公式も、単純なプレビュー表示ではPreviewViewが変換を自動処理すると案内しています。[参照] Android Developers「Transform output」

最初に理解したい「4:3の枠」と「4:3のカメラ映像」は別物

よくある誤解が、XMLやCompose側で表示領域を4:3にすればカメラ映像も4:3になるというものです。例えば画面上に横1200px・縦900pxのPreviewViewを配置すれば、Viewそのものは確かに4:3です。しかしカメラから渡されている映像が1920×1080の16:9で、その映像をViewへ不適切に拡大していれば、人物や円形の物体が横長に見えることがあります。

逆に、カメラ出力が4:3でもTextureViewなどへ独自Matrixを適用するときにX方向とY方向へ異なる倍率を設定してしまえば、やはり横伸びします。したがって「Viewが4:3か」だけではなく、入力映像→回転→Crop→Scale→Viewという表示経路全体を見る必要があります。

具体的には、正円を撮影してプレビューだけ楕円になるなら「歪み」が疑われます。一方、正円の形は正しいまま上下または左右が切れているだけなら、それは横伸びではなくアスペクト比を維持したCropである可能性があります。この2つは修正方法がまったく異なります。

CameraXならPreviewViewを最優先で確認する

CameraXを利用している場合、一般的なカメラプレビューではTextureViewやSurfaceViewを直接操作するより、androidx.camera.view.PreviewViewを使う構成が基本です。Android公式は、Previewの変換情報を自分で適用する処理は複雑であり、単純にプレビューを表示する目的ならPreviewViewを使うよう案内しています。PreviewViewはCameraXの変換処理を自動的に扱います。[参照] CameraXのプレビュー変換

そのためCameraXを採用しているのに、AIが生成したコードの中でTextureViewへ独自のMatrixを設定したり、preview.widthとbuffer.widthから独自scaleX・scaleYを計算したりしている場合は、一度その設計自体を疑う価値があります。

Android公式も、大画面・可変ウィンドウへの対応について、ViewベースのアプリではPreviewViewなどを利用し、SurfaceViewやTextureViewを直接利用する場合はアスペクト比と適切な変換行列をアプリ側で計算する必要があると説明しています。[参照] Android Developers「Support camera on multiple form factors」

PreviewViewのScaleTypeは「伸ばす」のではなくCropか余白を決める

PreviewViewにはScaleTypeがあり、代表的なものとしてFILL_CENTERとFIT_CENTERがあります。PreviewViewの既定値はFILL_CENTERです。[参照] PreviewView API

FILL_CENTERは元映像のアスペクト比を維持したままPreviewView全体を埋めます。そのため入力とViewの比率が異なる場合は映像の一部がCropされます。FIT_CENTERもアスペクト比を維持しますが、映像全体が収まるように表示するため、比率が異なれば余白が生じます。正常なPreviewViewでFILL_CENTERを使っただけなら、映像を横だけに引き伸ばして枠へ無理やり合わせる動作にはなりません。

つまりPreviewViewを正しく利用しているのに明確な横伸びが発生するなら、「4:3だからFILL_CENTERが悪い」と即断するより、PreviewViewの外側でscaleXを変更していないか、親Viewの変換、ComposeのModifier、独自Matrix、TextureViewへの変換が重複していないかを調べるべきです。

解像度は「1200×900に固定すれば解決」と考えない

CameraXでは、アプリが希望した解像度をカメラが必ずそのまま出力するわけではありません。Android公式によると、CameraXは端末の対応能力や同時に利用するUseCaseなどを考慮して適切な解像度を選択し、要求した比率や解像度と完全一致しない場合があります。[参照] CameraX「Configuration options」

例えば4:3が必要だからとAIへ「Previewを1440×1080に固定してください」と依頼しても、そのサイズを端末が希望通り採用する保証はありません。またPreview、ImageCapture、ImageAnalysisへ別々の固定サイズを指定すると、かえって構成が複雑になる場合があります。

現在のCameraXではResolutionSelectorを使って希望する解像度・アスペクト比戦略を指定できます。従来のsetTargetResolutionやsetTargetAspectRatioはPreview.Builderでは非推奨となっており、ResolutionSelectorと同時使用もできません。[参照] Preview.Builder API

4:3にしたいならResolutionSelectorと実際の出力サイズを確認する

4:3の工事写真を前提にする場合、「UIが4:3だから終わり」ではなく、PreviewとImageCaptureでどの比率を要求し、CameraXが実際に何を選択したのかを確認します。CameraXではアスペクト比を優先したい場合、対応するAspectRatioStrategyを持つResolutionSelectorを利用する方法があります。

重要なのは、4:3を絶対的なピクセルサイズではなくアスペクト比として要求する設計をまず検討することです。CameraX側に端末が対応する適切な解像度を選ばせた方が、多数のAndroid端末で動作させるアプリには適しています。

またAndroid公式は、CameraXが端末事情によって要求を調整する可能性があるため、実際に返された画像サイズを確認するよう案内しています。「コードでは4:3を指定したから実際も必ず4:3」と思い込まず、ログへ実値を出すことがデバッグでは重要です。

ImageCaptureとPreviewのアスペクト比を混同しない

工事検査用アプリでは「保存される写真を4:3にしたい」という要件と「画面上のプレビューを4:3で見せたい」という要件が同時にあります。この2つは似ていますが同じ処理ではありません。

ImageCaptureでは保存画像のCropや回転情報が関係します。一方Previewではカメラから受け取ったバッファを画面上のViewへどのように変換して表示するかが問題になります。保存したJPEGが正常な4:3なのに画面だけ横伸びしているなら、ImageCaptureよりPreview側を重点的に調査するべきです。

反対にプレビューは正常なのに保存後の写真だけ比率がおかしいなら、ImageCapture側の設定、Crop、Exifの回転などを調べます。「プレビューと撮影結果のどちらが壊れているか」を最初に分けるだけで、AIによる無関係なコード変更を大幅に減らせます。

端末回転とセンサー回転を単純にwidth・heightの入れ替えだけで処理しない

Androidカメラでは端末が縦向きでも、カメラセンサーから得られるバッファの座標系がそのまま縦向きとは限りません。90度または270度回転が必要になる場合、表示上の幅と高さの関係も変化します。

ここでAIが生成した独自コードによくあるのが、「portraitならwidthとheightをswapする」といった単純な処理と、別の場所でCameraX自身のrotation処理を同時に行うケースです。変換を二重に行えば、正しい比率だった映像を誤って拡大・Cropする原因になります。

CameraXのPreviewViewを使うなら、まずCameraXに任せられる回転・変換を独自実装していないか確認します。逆にTextureViewやOpenGLへ直接Surfaceを渡す高度な構成なら、SurfaceRequestから提供されるTransformationInfoなどを正しく利用する必要があります。

Transformは「なんとなくMatrixを掛ける」のが最も危険

CameraXの出力には画像バッファだけでなく、そのバッファをどのようにCrop・回転してユーザーへ表示するかという変換情報があります。Android公式ではPreviewの場合、SurfaceRequest.setTransformationInfoListener()からTransformationInfoを受け取れると説明しています。[参照] CameraX Transform output

TextureViewへ独自Matrixを設定している場合は、特にscaleXとscaleYが同じ倍率になっているかを確認します。例えば入力が4:3、出力枠も4:3なのにscaleX=1.33、scaleY=1.0のような非等方スケーリングを行えば、当然横へ伸びます。

アスペクト比を維持する表示では、基本的に一つの倍率で拡大縮小し、余った部分をCropするか余白として残します。「Viewの幅にぴったり」「Viewの高さにもぴったり」を別々の倍率で実現しようとすると、映像の縦横比を壊します。

オーバーレイがある場合はViewPortと座標変換も確認する

工事検査用アプリでは、プレビューの上に撮影枠、工事名、黒板、日付、矩形、検査ポイントなどを重ねることがあります。この場合、カメラ映像とオーバーレイの座標系を一致させる必要があります。

CameraXにはOutputTransformやCoordinateTransformがあり、同じViewPortに関連付けられたUseCaseとPreviewView間で座標を変換できます。例えばImageAnalysisで検出した矩形をPreviewView上へ正しく描画する用途です。[参照] CoordinateTransform API

ここで注意したいのは、オーバーレイの位置合わせ用Matrixと、カメラ映像そのものを表示するMatrixを混同しないことです。AIに修正させる場合も「このMatrixは映像を変換するためか、座標を変換するためか」を明確にさせるとバグを減らせます。

CameraXとCamera2を同時に疑うより、まず実装方式を一つに特定する

AIへ「CameraX/Camera2/PreviewView/TextureViewのどこが原因ですか」とだけ質問すると、AIは可能性を大量に列挙しがちです。しかし実際のプロジェクトがCameraX+PreviewViewなら、Camera2+TextureView用の修正を考える必要はありません。

CameraXは内部でCamera2実装を利用する場合がありますが、アプリ側がCameraX APIを使っているのであれば、まずCameraXの抽象化レイヤー内で正しい構成にするのが基本です。問題が起きたからといってCamera2へ書き換えると、Surface、センサー回転、対応解像度、Transformなどを自前管理する範囲が増え、初心者にはむしろ難易度が上がります。

単純な写真撮影アプリなら、まずCameraX+PreviewView+ImageCaptureという最小構成で正常な比率を実現し、その後に工事写真用のUIやオーバーレイを追加する方が原因を切り分けやすくなります。

横伸び問題は「最小構成」を作ると一気に原因を絞れる

AIを使った開発で特に効果的なのが、現在の巨大な撮影画面をそのまま修正し続けるのではなく、一度最小構成で再現テストをする方法です。

例えば「ActivityまたはFragment+4:3のPreviewView+CameraX Preview+ImageCapture」だけを残し、フィルター、独自TextureView、画像解析、黒板表示、ズームUI、独自Matrixなどをすべて外します。この状態で正円を映し、円が正円として表示されるか確認します。

最小構成では正常ならCameraXや端末カメラそのものが原因である可能性は下がり、元アプリで後から追加したTransformやレイアウト処理を一つずつ戻せば原因箇所を特定できます。最小構成でも同じ端末で歪むなら、CameraX設定・端末依存・ライブラリバージョンなどを重点的に調べます。

AIへ渡す前にログとして取得したい情報

「横伸びしています」という文章だけでは、人間の開発者でも原因を特定するのは困難です。AIへ修正を依頼するときは、推測させるのではなく診断に必要な実測値を渡します。

最低限、端末名、Androidバージョン、縦画面・横画面、CameraXのバージョン、PreviewViewの実測width/height、Previewが実際に選択した解像度、ImageCaptureの出力サイズ、rotationDegrees、PreviewView.ScaleType、ImplementationMode、親Viewのサイズなどを確認します。

さらに「保存JPEGは正常/異常」「スクリーンショットでは横伸び」「縦画面だけ発生」「Pixelでは正常だがGalaxyでは異常」のような再現条件も重要です。AIにとって、これらは単なる追加情報ではなく原因候補を消していくための証拠になります。

AIには関連コードを断片ではなく一式で渡す

AIへPreview.Builderの数行だけを渡しても、実際の原因がXMLのPreviewView、ConstraintLayout、ComposeのModifier、後段のMatrix処理にあれば発見できません。カメラ表示に関係するコードを一まとまりで提示する必要があります。

具体的には、PreviewViewを定義しているXMLまたはComposeコード、CameraXを初期化する処理、Preview.Builder、ImageCapture.Builder、bindToLifecycle、setSurfaceProvider、ResolutionSelector、回転処理、ViewPort、MatrixやscaleX・scaleYを変更するコードを渡します。

プロジェクト全体を参照できるCodexのようなコーディングエージェントを使う場合は、「修正前にカメラ関連ファイルを検索し、PreviewView・TextureView・SurfaceView・Matrix・scaleX・scaleY・setTransform・ResolutionSelector・setTargetResolution・setTargetAspectRatioの使用箇所を列挙せよ」と指示すると、いきなりコードを書き換えさせるより安全です。

AIには「修正」より先に「原因調査」を命令する

AIコーディングでありがちな失敗が、「横伸びを直してください」とだけ指示することです。この命令ではAIの目的が「何かコードを変更すること」になり、ResolutionSelectorを変更し、それでも直らなければScaleTypeを変更し、さらにMatrixを追加するという場当たり的な修正になりやすくなります。

代わりに、最初の依頼ではコードを変更せず原因を調査するよう明示します。「現状コードを変更するな。カメラ入力からPreviewView表示までのデータフローを追跡し、アスペクト比を変更している全箇所を列挙せよ。横伸びを起こし得る箇所を根拠付きで順位付けせよ」といった指示です。

その診断結果を読んでから「最有力原因だけを最小変更で修正し、変更ファイルと理由を説明せよ」と次の指示を出します。この2段階方式にすると、AIが一度に多数の設定を変更して原因が分からなくなる問題を防げます。

Codexなどへ渡しやすい具体的な診断プロンプト

実際には、次のような内容をプロンプトへ含めると診断精度を上げやすくなります。文章をそのまま使う必要はありませんが、「症状・期待結果・実測値・調査対象・禁止事項」を分けるのがポイントです。

症状:撮影領域のViewは4:3だが、カメラプレビューだけ横方向に引き伸ばされる。保存JPEGが正常かどうかも確認する。目的:人物や円形物体の縦横比を維持したまま4:3領域へ表示したい。FILLが必要なら歪ませず中央Cropする。調査:CameraX初期化、PreviewView、ResolutionSelector、ViewPort、rotation、Matrix、scaleX/scaleY、setTransform、TextureView/SurfaceViewの使用箇所をプロジェクト全体から検索する。まずコードを変更せず、入力バッファから画面表示までの変換経路を説明し、非等方スケーリングまたは二重Transformがないか確認する。原因候補を根拠付きで順位付けする。次に最有力原因のみを最小変更で修正する。不要なCamera2への書き換えは禁止する。

さらに端末名やログをプロンプト末尾へ追加します。「PreviewView=1080×810、実際のPreview解像度=○×○、rotation=90、ScaleType=FILL_CENTER、保存JPEG=4032×3024で正常」のような具体値があれば、AIは「解像度の問題なのか表示Transformの問題なのか」をかなり絞れます。

AIに必ず「変更理由」と「検証方法」を出させる

AIがコードを修正したら、動いたかどうかだけで終わらせず、「なぜその変更で横伸びが解消するのか」を説明させます。説明できない変更を積み重ねると、別端末で再発したときに修正不能になりやすいためです。

例えばAIがFILL_CENTERへ変更したなら、「入力比率を維持して拡大し、Viewからはみ出す部分だけCropするため、X/Y別倍率による歪みが発生しない」と説明できる必要があります。ResolutionSelectorを変更したなら、端末が希望比率を採用できない場合のfallbackまで説明させます。

同時にテスト条件も生成させます。縦向き・横向き、前面・背面カメラ、4:3と16:9の異なるセンサー出力、複数メーカー端末、画面回転後、アプリ再開後などを確認すれば、「自分のスマホだけ偶然直った」という状態を避けられます。

工事検査用カメラなら「見た目が正しい」だけで終わらせない

工事検査写真では一般的なSNSカメラより、撮影結果の再現性が重要です。プレビューが正しく見えても、保存画像で撮影範囲が大きく変わると「画面に入っていた対象が写真では切れている」といった問題につながります。

そのため最終的には、PreviewView上で見えている範囲とImageCaptureで保存される範囲がどの程度一致するかを確認します。必要であればUseCaseGroupとViewPortを使い、複数UseCase間でCrop領域をそろえる設計を検討します。

また「4:3で保存する」という要件についても、単なるUI上の4:3なのか、JPEGのピクセル寸法を4:3にする必要があるのか、写真にオーバーレイを焼き込むのかを仕様として分離しておくと、AIが誤った実装を選びにくくなります。

横伸びが直らないときの確認順序

原因が分からなくなった場合は、次の順番で調査すると効率的です。

  1. 保存JPEGまで横伸びしているのか、プレビューだけなのかを確認する
  2. CameraX+PreviewViewなのか、Camera2+TextureViewなどなのか実装方式を確定する
  3. PreviewView自体の実測width・heightをログへ出す
  4. カメラが実際に選択したPreview・ImageCaptureの解像度を確認する
  5. PreviewViewのScaleTypeを確認する
  6. scaleX・scaleY・Matrix・setTransformなど独自変換をプロジェクト全体から検索する
  7. 端末回転とCameraXのTransformを二重処理していないか確認する
  8. ResolutionSelectorとAspectRatioStrategyの設定を確認する
  9. PreviewとImageCaptureで要求条件が不必要に食い違っていないか確認する
  10. 最小構成のCameraX+PreviewViewで同じ端末に問題が再現するかテストする
  11. AIには修正前に原因候補と根拠を提出させる
  12. 最有力原因を一つずつ最小変更で修正し、変更ごとに実機テストする

特に「保存写真は正常なのにPreviewだけ横伸び」なら表示Transform側、「保存写真もおかしい」ならCapture・Crop・解像度側まで調査範囲を広げるという切り分けが有効です。

まとめ:CameraXの横伸びは解像度より「表示までの変換経路」を追う

Androidカメラで撮影領域は4:3なのに映像だけ横伸びする場合、4:3のViewを作り直したり解像度を何度も変更したりするだけでは解決しないことがあります。重要なのは、カメラ出力→Crop→Rotation→Scale→PreviewViewのどこでアスペクト比が変わっているかを特定することです。

CameraXで一般的なプレビューを実装するなら、まずPreviewViewへ変換を任せ、独自TextureViewやMatrixを必要以上に追加しない構成を検討します。PreviewViewのFILL_CENTERはアスペクト比を維持してCropする方式なので、それでも明確に横伸びするなら独自scale、親Viewの変換、二重Transformなどを重点的に探します。

またCameraXでは端末ごとに利用可能な解像度が異なり、指定したサイズが必ず採用されるわけではありません。現在はResolutionSelectorを利用しつつ、コード上の希望値だけでなく実機で選ばれた解像度・Viewサイズ・rotationをログで確認することが重要です。

AIに修正させる場合も、「横伸びを直して」とだけ依頼するのではなく、実装方式、関連ファイル、端末、CameraXバージョン、実測解像度、Viewサイズ、回転、保存JPEGの状態を渡し、「変更前に原因を調査する→変換経路を説明する→原因候補を順位付けする→最有力箇所だけ最小変更する→検証方法を提示する」という順序で作業させると、場当たり的なAI修正から抜け出しやすくなります。

コメント

タイトルとURLをコピーしました