画像解析ユースケースは、CPU からアクセスできる画像をアプリに提供し、それらの画像に対して画像処理、コンピュータ ビジョン、機械学習推論を実行します。アプリには、各フレームで実行する analyze()
メソッドを実装します。
Google の ML Kit を CameraX アプリと統合する方法については、ML Kit Analyzer をご覧ください。
動作モード
アプリの分析パイプラインが CameraX のフレームレート要件に対応できない場合、次のいずれかの方法でフレームをドロップするように CameraX を設定できます。
非ブロッキング(デフォルト): このモードでは、エグゼキュータは常に最新の画像を画像バッファにキャッシュし(深度 1 のキューと同様)、その間にアプリは前の画像を分析します。アプリが処理を完了する前に CameraX が新しい画像を受信すると、新しい画像が同じバッファに保存され、前の画像が上書きされます。このシナリオでは
ImageAnalysis.Builder.setImageQueueDepth()
は効果がなく、バッファの内容は常に上書きされます。この非ブロッキング モードを有効にするには、STRATEGY_KEEP_ONLY_LATEST
でsetBackpressureStrategy()
を呼び出します。 エグゼキュータの影響について詳しくは、STRATEGY_KEEP_ONLY_LATEST
のリファレンス ドキュメントをご覧ください。ブロッキング: このモードでは、内部エグゼキュータは内部画像キューに複数の画像を追加でき、キューがいっぱいになったときにのみフレームのドロップを開始します。ブロッキングはカメラデバイスのスコープ全体で発生します。カメラデバイスにバインドされたユースケースが複数ある場合、CameraX がこれらの画像を処理している間、ユースケースはすべてブロックされます。たとえば、プレビューと画像分析の両方がカメラデバイスにバインドされている場合、CameraX による画像の処理中もプレビューがブロックされます。ブロッキング モードを有効にするには、
STRATEGY_BLOCK_PRODUCER
をsetBackpressureStrategy()
に渡します。 ImageAnalysis.Builder.setImageQueueDepth() を使用して、画像のキュー深度を設定することもできます。
低レイテンシで高パフォーマンスのアナライザの場合、画像分析にかかる合計時間が CameraX のフレームの長さ(たとえば、60 fps では 16 ミリ秒)未満であれば、どちらの動作モードでも全体的にスムーズな動作が維持されます。ただし、非常に短いシステム ジッターを処理する場合などは、ブロッキング モードが役立つことがあります。
高レイテンシで高パフォーマンスのアナライザでは、ブロッキング モードと長いキューでレイテンシを補正する必要があります。ただし、この場合でもアプリはすべてのフレームを処理できます。
高レイテンシで時間のかかるアナライザ(アナライザはすべてのフレームを処理できません)では、分析パスのフレームをドロップする必要があるため、非ブロッキング モードの方が適している場合があります。ただし、同時にバインドされている他のユースケースでもすべてのフレームが表示されます。
実装
アプリで画像解析を使用する手順は次のとおりです。
ImageAnalysis
ユースケースを作成する。ImageAnalysis.Analyzer
を作成する。ImageAnalysis
にアナライザを設定する。- ライフサイクルのオーナー、カメラセレクタ、
ImageAnalysis
ユースケースをライフサイクルにバインドする。
バインドの直後、CameraX が登録済みのアナライザに画像を送信します。
分析が完了したら、ImageAnalysis.clearAnalyzer()
を呼び出すか、ImageAnalysis
ユースケースをアンバインドして分析を停止します。
ImageAnalysis ユースケースを作成する
ImageAnalysis
は、アナライザ(画像コンシューマ)を、画像プロデューサーである CameraX に接続します。アプリは、ImageAnalysis.Builder
を使用して ImageAnalysis
オブジェクトを作成します。ImageAnalysis.Builder
を使用すると、アプリで以下を設定できます。
- 画像出力パラメータ:
- 形式: CameraX がサポートする
YUV_420_888
とRGBA_8888
~setOutputImageFormat(int)
。デフォルトの形式はYUV_420_888
です。 - 解像度とアスペクト比: どちらのパラメータも設定できますが、両方の値を同時に設定することはできません。
- 回転角度。
- ターゲット名: このパラメータはデバッグに使用されます。
- 形式: CameraX がサポートする
- 画像フロー コントロール:
アプリは、解像度またはアスペクト比のいずれかを設定できますが、両方を設定することはできません。正確な出力解像度は、アプリで要求されるサイズ(またはアスペクト比)とハードウェア機能に依存し、要求されるサイズや比率とは異なる場合があります。解像度マッチング アルゴリズムについては、setTargetResolution()
のドキュメントをご覧ください。
アプリは、出力画像ピクセルを YUV 色空間(デフォルト)または RGBA 色空間の形式に設定します。RGBA 出力形式を設定すると、CameraX は内部で画像を YUV 色空間から RGBA 色空間に変換し、ImageProxy の最初のプレーン(他の 2 つのプレーンは不使用)の ByteBuffer
に、次の順序で画像ビットを格納します。
ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...
デバイスがフレームレート要件に対応できない複雑な画像解析を行う場合は、このトピックの動作モードセクションに記載されている戦略を使用してフレームをドロップするように CameraX を設定できます。
アナライザを作成する
アプリケーションでアナライザを作成するには、
ImageAnalysis.Analyzer
インターフェースとオーバーライドを
analyze(ImageProxy image)
。
各アナライザで、アプリは ImageProxy
(Media.Image のラッパー)を受け取ります。
画像形式は ImageProxy.getFormat()
で確認できます。この形式は、アプリが ImageAnalysis.Builder
で提供する次の値のいずれかです。
ImageFormat.RGBA_8888
: アプリがOUTPUT_IMAGE_FORMAT_RGBA_8888
をリクエストした場合。ImageFormat.YUV_420_888
: アプリがOUTPUT_IMAGE_FORMAT_YUV_420_888
をリクエストした場合。
色空間の設定とピクセルバイトを取得できる場所については、BuildImageAnalysis ユースケースをご覧ください。
アナライザ内では、アプリケーションは次の処理を行う必要があります。
- 指定されたフレームをできるだけ早く、指定されたフレームレートの制限内(たとえば、30 fps の場合は 32 ミリ秒未満)で分析する。アプリがフレームを迅速に分析できない場合は、サポートされているフレーム ドロップの仕組みのいずれかを使用することをおすすめします。
ImageProxy.close()
を呼び出して、ImageProxy
を CameraX にリリースします。ラップされた Media.Image のクローズ関数(Media.Image.close()
)は呼び出さないでください。
アプリは ImageProxy 内のラップされた Media.Image
を直接使用します。ラップされた画像に対して Media.Image.close()
を呼び出さないでください。CameraX 内の画像共有メカニズムが機能しなくなります。代わりに、ImageProxy.close()
を使用して基盤となる Media.Image
を CameraX にリリースします。
ImageAnalysis 向けにアナライザを設定する
アナライザを作成したら、ImageAnalysis.setAnalyzer()
を使用してアナライザを登録し、分析を開始します。分析が完了したら、ImageAnalysis.clearAnalyzer()
を使用して、登録済みのアナライザを削除します。
画像を分析するように設定できるアクティブなアナライザは 1 つのみです。登録済みのアナライザがすでに存在する場合は、ImageAnalysis.setAnalyzer()
を呼び出すと、そのアナライザが置き換えられます。アプリは、ユースケースのバインドの前でも後でも、随時新しいアナライザを設定できます。
ImageAnalysis をライフサイクルにバインドする
ProcessCameraProvider.bindToLifecycle()
関数を使用して、ImageAnalysis
を既存の AndroidX ライフサイクルにバインドすることを強くおすすめします。bindToLifecycle()
関数は、選択した Camera
デバイスを返します。このデバイスを使用して、露出などの詳細設定を微調整できます。カメラ出力の制御について詳しくは、こちらのガイドをご覧ください。
次の例では、上記の手順をすべて組み合わせて、CameraX の ImageAnalysis
ユースケースと Preview
ユースケースを lifeCycle
オーナーにバインドします。
Kotlin
val imageAnalysis = ImageAnalysis.Builder() // enable the following line if RGBA output is needed. // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy -> val rotationDegrees = imageProxy.imageInfo.rotationDegrees // insert your code here. ... // after done, release the ImageProxy object imageProxy.close() }) cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() // enable the following line if RGBA output is needed. //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(new Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() { @Override public void analyze(@NonNull ImageProxy imageProxy) { int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees(); // insert your code here. ... // after done, release the ImageProxy object imageProxy.close(); } }); cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);
参考情報
CameraX について詳しくは、以下の参考情報をご確認ください。
Codelab
コードサンプル