【カメラ2】Android Camera2まとめ

1. まとめ

この記事は Camera2 のレビューで、主に次の部分が含まれます。

  • その1:Camera2参考資料
  • その2:Camera2の開発経緯
  • Part3: Camera2のフレームワークと利用の流れ

この記事はCamera1 のレビュー記事と合わせて読むことができます。説明を完全にするために、この記事は Camera1 のレビュー記事と重複しています。

Part2ではCamera1と の違いを詳しく紹介しCamera2、Camera2の開発経緯も紹介します。

Part3では、Camera2にまつわる使用プロセスをフレーム次元、コード次元から詳しく紹介します。ただし、使用手順、詳細で完全なコード開発または説明のみが含まれますCamera2源码分析および を参照してくださいCamera2开源项目分析

関連する Camera2 シリーズの記事を参照できます: Android Camera シリーズの記事の進捗表

2.参考資料

3.開発経緯

まず、次の 2 つの概念を明確にする必要があります。

  • HAL
  • API

異なるHAL合計の中で、特定の合計の違いをAPI見つけることができます。Camera1Camera2

3.1 HAL:ハードウェア抽象化レイヤー。

Camera1 のレビュー記事の説明は次のとおりです。

HAL は、カメラ ドライバーと上位レベルの Android フレームワークの間に位置し、アプリがカメラ ハードウェアを適切に操作できるように実装する必要があるインターフェイスを定義します。

HAL は、ハードウェア ベンダーが実装する標準インターフェースを定義し、Android が下位レベルのドライバーの実装を無視できるようにします。HAL の実装は通常、共有ライブラリ モジュール (.so) に組み込まれています。

HAL は主にカメラの; 関連するバージョン履歴のダイナミクスを理解するだけでよく、それを掘り下げる必要はありません. ここに簡単な要約があります. 詳細については、以下を参照してください。

HAL バージョン 例証する
1.0 1. Android カメラ HAL (Android 4.0) [camera.h]; 2. サポートandroid.hardware.CameraAPI
2.0 1. 拡張機能HALの初期バージョン(Android 4.2) [camera2.h]; 2. 既存のandroid.hardware.CameraAPIを実装するのに十分。
3.0 1. 拡張機能 HAL の最初のリビジョン 2. 重要なバージョン変更、入力要求およびストリーム キュー インターフェイスの再設計など
3.1 1. 拡張機能 HAL のマイナー リビジョン; 2. configure_streams はコンシューマー使用フラグを HAL に渡し、フラッシュ呼び出しを実行して、処理中のすべてのリクエスト/バッファーをできるだけ早く破棄します。
3.2 1. 拡張機能 HAL のマイナー リビジョン; 2. 非推奨get_metadata_vendor_tag_opsおよびregister_stream_buffers; 双方向および入力ストリーム仕様の再制定; 入力バッファーのリターン パスの変更。
3.3 1. 拡張機能 HAL のマイナー リビジョン; 2. OPAQUE および YUV 再処理 API の更新; 深度出力バッファーの基本サポート; いくつかのフィールドを追加します。
3.4 1. サポートされているメタデータへのマイナーな追加と、data_space サポートへの変更。
3.5 1.Android10.0;ICameraDevice,ICameraDeviceSession,ICameraDeviceCallbackを更新。
アンドロイド 8.0 1. Treble が導入されました; 2. ベンダー カメラ HAL 実装がバインドされている必要があります。
アンドロイド 9.0 1.HAL 3.3 ではいくつかのキーとメタデータ タグが追加され、2.HAL 3.4 では ICameraDeviceSession と ICameraDeviceCallback が更新されます。
アンドロイド 10.0 1. HAL 3.4 は、画像形式、メタデータ フラグ、新しい機能、データ フロー構成などを追加します。

3.2 Camera1 と Camera2 の違い

Camera1Camera2はそれぞれ 、つまり相机API1とに対応します相机API2

3.2.1 カメラ API1

Android 5.0 は廃止されCamera API1、新しいプラットフォームは開発に重点を置いておりCamera API2Camera API1徐々に廃止されます。ただし、フェーズアウト期間は長くなり、新しい Android バージョンはしばらくの間Camera API1アプリを。これが、Camera1 フレームワークが後続の Android SDK でアプリケーション サポート フレームワークとして使用できる理由です。

3.2.2 カメラ API2

  • Camera API2このフレームワークは、効率的なゼロコピー バースト撮影/ストリーミングや、露出、ゲイン、ホワイト バランス ゲイン、色変換、ノイズ除去、シャープ化などのフレームごとのコントロールなど、低レベルに近いカメラ コントロールをアプリに提供します
  • Android 5.0 以降のバージョンはカメラ API2 を提供します。つまり、Android 5.0 以降のバージョンはをサポートしていますが、Android 5.0 以降のバージョンはすべてのカメラ API2 機能をサポートしていない場合があります。Camera2
  • Camera2以下のサポートレベルに分かれています
Camera2 対応レベル 例証する
遺産 使用する関数は API1 とほぼ同じです。つまり、最下層はカメラ API2 呼び出しをカメラ API1 呼び出しに変換します。
限定 一部のカメラ API2 関数、およびカメラ HAL 3.2 以降。
満杯 API2 のすべての主要機能、およびカメラ HAL 3.2 以降をサポートします。
レベル3 YUV 再処理と RAW 画像キャプチャ、およびその他の出力ストリーム構成をサポートします。
外部の LIMITED デバイスと同様に、このレベルは外部カメラに使用されます。

互換性のコストを考えると、実際に Camera2 の機能を使用する場合、Android5.0 で Camera2 を直接サポートすることはありません。代わりに、より高いバージョンの HAL3.2 に対応する Android SDK で Camera2 がサポートされますAndroid SDK が Camera2 をサポートし始めたのはいつですか?【3.3】で明らかになります。

3.2.3 カメラ API のバージョン履歴

以下は簡単な要約です。詳細については、
カメラ API のバージョン履歴を参照してください。

API バージョン 例証する
1.0 HAL1.0、つまり Camera1 を実装する
2.0 HAL2.0 以上、つまり Camera2 を実装する
2.1 カメラ HAL モジュールからフレームワークへの非同期コールバックのサポートを追加しました
2.2 モジュールベンダータグのサポートを追加
2.3 従来のカメラ HAL デバイスを開くためのサポートが追加されました。
2.4 API の変更; 1. 懐中電灯モードのサポート; 2. 外部カメラ (USB ホットスワップ可能なカメラなど) のサポート; 3. カメラ調停プロンプト; 4. モジュールの初期化メソッド。

3.3 HAL1 と HAL3

3.3.1 HAL1

HAL1 は、現在廃止されている Camera1 に対応します。HAL1 のアーキテクチャは次のとおりです。
ここに画像の説明を挿入
ここでは、公式ドキュメントが明確に提唱しています。

注意:由于 Camera HAL1 已弃用,建议在搭载 Android 9 或更高版本的设备上使用 Camera HAL3。

したがって、一般的には、9.0上記をサポートすることをCamera2お勧めします。またCamera2、 のサポートレベルには、それ以上の使用を推奨するレベルもFULL記載されていますLevel3HAL3.2

3.3.2 HAL3

【3.2】Camera1と のCamera2機能的な違いについてはこちらで説明しています.HAL3アーキテクチャ図はここに貼り付けています. 詳しい理論知識については
[Android Camera] Android Camera Architecture Design Detailed Explanation の記事を参照してください.
ここに画像の説明を挿入

4. フレームワークと使用プロセス

主に、フレームワーク次元コードレベル次元の2つの次元に分けられます

4.1 フレーム寸法

以下の部品を含む

  • Camera2 カメラモデル
  • カメラ HAL3 コア オペレーション モデル
  • HAL 操作の概要
  • カメラセンサー
  • Android カメラ API の手順

4.1.1 Camera2 カメラモデル

ここに画像の説明を挿入
上の図には、次の 3 つのレイヤーが含まれています。

4.1.1.1 カメラ利用アプリ

モジュール 例証する
CameraManager.AvailabilityCallback アプリケーション レイヤーは、このコールバックを通じてカメラ デバイスのステータスを監視します。
カメラ特性 カメラ デバイス情報と関連する設定クラス
出力ストリームの宛先 出力ストリーム構成: 構成可能なプレビュー、写真撮影、記録、およびその他のシナリオ
折り返し電話 次のようないくつかのコールバック メソッド: CameraCaptureSession.CaptureCallBack

4.1.1.2 カメラ 2 API

モジュール 例証する
カメラマネージャー アプリケーション層はデバイスに接続し、このタイプの呼び出しを通じてカメラ情報を取得します
カメラデバイス 絵画の作成を提供し、フレーム キャプチャを作成するためのアプリケーション レイヤーを提供します。
カメラキャプチャセッション 特定のフレーム キャプチャが出力ストリーム情報に変換される
構成された出力 出力ストリーム キュー

4.1.1.3 カメラ デバイス ハードウェア

ハードウェア レベル: レンズ制御、Camera Sensorフラッシュ設定、後処理、および制御操作を含みます。

4.1.2 カメラ HAL3 コア操作モデル

カメラ モデルのコア操作は、フレーム キャプチャ要求を対応する出力ストリームに出力することです。次のように:
ここに画像の説明を挿入
API は、カメラ サブシステムを、CaptureRequest受信フレーム キャプチャ リクエスト ( ) をフレームに変換するパイプラインとしてモデル化します。リクエストには、フレームのキャプチャと処理に関するすべての構成情報が含まれます。これには、解像度とピクセル フォーマット手動センサーレンズ、およびフラッシュ コントロール3A モードの動作RAW から YUV への処理コントロール統計生成などが含まれます。

  1. 構造CaptureRequest
  2. 独身Capture
  3. request
  4. Output1: 構成された出力面 [プレビュー、写真など]
  5. 出力 2:onCaptureComplete-> CaptureResult

コアの処理フローは、上図と上記の 5 つのステップで示されます。これは、単一の要求操作です。Camera2また、繰り返しの Request 操作プロセスも用意されています。読む。

4.1.3 HAL 操作の概要

ここに画像の説明を挿入
如上图,包含如下几个流程:

  1. 输入和构造CaptureRequest
  2. Camera HAL经过配置、帧捕获和后处理
  3. 输出到输出流配置里。

在图片里可以看到CaptureRequest分为2种:

  • 单次Capture,直接输出
  • 重复Capture,重新由4转移到6

几点说明

  1. 捕获的异步请求来自于框架。
  2. HAL 设备必须按顺序处理请求。对于每个请求,均生成输出结果元数据以及一个或多个输出图像缓冲区。
  3. 请求和结果以及后续请求引用的信息流遵守先进先出规则。
  4. 指定请求的所有输出的时间戳必须完全相同,以便框架可以根据需要将它们匹配在一起。
  5. 所有捕获配置和状态(不包括 3A 例程)都包含在请求和结果中。

4.1.4 Camera传感器

本小节具体介绍Camera硬件层面即Camera Sensor
ここに画像の説明を挿入
如上图为:图像信号处理器(ISP,也称为相机传感器)包含了如下几个模块:

  1. 3A算法;
  2. 输出流分辨率和大小以及格式的处理
  3. 图像处理:Hot Pixel Correction -> Demosaic -> Noise Reduction -> ShadingCorrection -> Geometric Correction -> Color Correction -> Tone Curve Adjustment -> Edge Enhancement。

整个ISP处理流程的简介看参看如下2篇文章:

【Android Camera】1.Camera理论知识和基本原理

这里总结为如下2张图:
ここに画像の説明を挿入

ここに画像の説明を挿入

4.1.5 Android Camera API步骤

  1. 监听和枚举相机设备。
  2. 打开设备并连接监听器。
  3. 配置目标使用情形的输出(如静态捕获、录制等)。
  4. 为目标使用情形创建请求。
  5. 捕获/重复请求和连拍。
  6. 接收结果元数据和图片数据。
  7. 切换使用情形时,返回到第 3 步。

4.2 代码维度

这里以【4.1.5里的流程进行阐述】

4.2.1 监听和枚举相机设备。

4.2.1.1监听相机设备

可通过CameraManager的如下方法:

   public void registerAvailabilityCallback(@NonNull AvailabilityCallback callback,
            @Nullable Handler handler) {
    
    
        CameraManagerGlobal.get().registerAvailabilityCallback(callback,
                CameraDeviceImpl.checkAndWrapHandler(handler));
    }

AvailabilityCallback次の 3 つのメソッドが含まれています。

 public void onCameraAvailable(@NonNull String cameraId) {
    
    
        }

 public void onCameraUnavailable(@NonNull String cameraId) {
    
    
        }

   public void onCameraAccessPrioritiesChanged() {
    
    
        }

4.2.1.2 カメラデバイスの列挙

カメラ デバイスに 2 つ以上のカメラ デバイスが含まれるようになりました。カメラ デバイスは、次の方法で列挙できます。

val cameraIdList = cameraManager.cameraIdList // may be empty
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
val cameraCapabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
val cameraCompatible = cameraCapabilities?.contains(
        CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false

次のメソッドは、前面または背面の cameraId へのアクセスを提供します

fun getFirstCameraIdFacing(cameraManager: CameraManager,
                           facing: Int = CameraMetadata.LENS_FACING_BACK): String? {
    
    
    // Get list of all compatible cameras
    val cameraIds = cameraManager.cameraIdList.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)
        capabilities?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }

    // Iterate over the list of cameras and return the first one matching desired
    // lens-facing configuration
    cameraIds.forEach {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        if (characteristics.get(CameraCharacteristics.LENS_FACING) == facing) {
    
    
            return it
        }
    }
    // If no camera matched desired orientation, return the first one from the list
    return cameraIds.firstOrNull()
}

その他の関連する方法:

fun filterCompatibleCameras(cameraIds: Array<String>,
                            cameraManager: CameraManager): List<String> {
    
    
    return cameraIds.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
                CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ?: false
    }
}

fun filterCameraIdsFacing(cameraIds: List<String>, cameraManager: CameraManager,
                          facing: Int): List<String> {
    
    
    return cameraIds.filter {
    
    
        val characteristics = cameraManager.getCameraCharacteristics(it)
        characteristics.get(CameraCharacteristics.LENS_FACING) == facing
    }
}

fun getNextCameraId(cameraManager: CameraManager, currCameraId: String? = null): String? {
    
    
    // Get all front, back and external cameras in 3 separate lists
    val cameraIds = filterCompatibleCameras(cameraManager.cameraIdList, cameraManager)
    val backCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_BACK)
    val frontCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_FRONT)
    val externalCameras = filterCameraIdsFacing(
            cameraIds, cameraManager, CameraMetadata.LENS_FACING_EXTERNAL)

    // The recommended order of iteration is: all external, first back, first front
    val allCameras = (externalCameras + listOf(
            backCameras.firstOrNull(), frontCameras.firstOrNull())).filterNotNull()

    // Get the index of the currently selected camera in the list
    val cameraIndex = allCameras.indexOf(currCameraId)

    // The selected camera may not be on the list, for example it could be an
    // external camera that has been removed by the user
    return if (cameraIndex == -1) {
    
    
        // Return the first camera from the list
        allCameras.getOrNull(0)
    } else {
    
    
        // Return the next camera from the list, wrap around if necessary
        allCameras.getOrNull((cameraIndex + 1) % allCameras.size)
    }
}

4.2.2 デバイスの電源を入れ、リスナーを接続します。

次の方法で:

    public void openCamera(@NonNull String cameraId,
            @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
            throws CameraAccessException {
    
    

        openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler),
                USE_CALLING_UID);
    }

パラメータの説明:

  • cameraId->【4.2.1】
  • CameraDevice.StateCallback次のように:
    public static abstract class StateCallback {
    
    
        public abstract void onOpened(@NonNull CameraDevice camera); // Must implement

        public void onClosed(@NonNull CameraDevice camera) {
    
    
            // Default empty implementation
        }

        public abstract void onDisconnected(@NonNull CameraDevice camera); // Must implement

        public abstract void onError(@NonNull CameraDevice camera,
                @ErrorCode int error); // Must implement
    }

4.2.3 ターゲット ユース ケースの出力 (静的キャプチャ、記録など) を構成し、ターゲット ユース ケースの要求を作成します。.

// Retrieve the target surfaces, which could be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis
// 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons
// 4. RenderScript.Allocation, if you want to do parallel processing
val surfaceView = findViewById<SurfaceView>(...)
val imageReader = ImageReader.newInstance(...)

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
val previewSurface = surfaceView.holder.surface
val imReaderSurface = imageReader.surface
val targets = listOf(previewSurface, imReaderSurface)

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
cameraDevice.createCaptureSession(targets, object: CameraCaptureSession.StateCallback() {
    
    
  override fun onConfigured(session: CameraCaptureSession) {
    
    
    // Do something with `session`
  }
  // Omitting for brevity...
  override fun onConfigureFailed(session: CameraCaptureSession) = Unit
}, null)  // null can be replaced with a Handler, falls back to current thread's Looper

4.2.4 キャプチャ/リピート リクエストとバーストおよび単一および複数のキャプチャの切り替え。

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
val repeatingRequest = session.device.createCaptureRequest(
        CameraDevice.TEMPLATE_PREVIEW)
repeatingRequest.addTarget(previewSurface)
session.setRepeatingRequest(repeatingRequest.build(), null, null)

// Some time later...

// Create the single request and dispatch it
// NOTE: This may disrupt the ongoing repeating request momentarily
val singleRequest = session.device.createCaptureRequest(
        CameraDevice.TEMPLATE_STILL_CAPTURE)
singleRequest.addTarget(imReaderSurface)
session.capture(singleRequest.build(), null, null)

シングルとマルチの違いは以下の通りです。
ここに画像の説明を挿入

4.2.5 結果のメタデータと画像データを受け取ります。

    public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
            @Nullable CaptureCallback listener, @Nullable Handler handler)
            throws CameraAccessException;
  • CaptureRequest->【4.2.4】
  • CaptureCallback listener次のように:
 public static abstract class CaptureCallback {
    
    
        public static final int NO_FRAMES_CAPTURED = -1;

        public void onCaptureStarted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, long timestamp, long frameNumber) {
    
    
            // default empty implementation
        }
        public void onCapturePartial(CameraCaptureSession session,
                CaptureRequest request, CaptureResult result) {
    
    
            // default empty implementation
        }
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
    
    
            // default empty implementation
        }
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
    
    
            // default empty implementation
        }
        public void onCaptureFailed(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
    
    
            // default empty implementation
        }
        public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session,
                int sequenceId, long frameNumber) {
    
    
            // default empty implementation
        }
        public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session,
                int sequenceId) {
    
    
            // default empty implementation
        }
        public void onCaptureBufferLost(@NonNull CameraCaptureSession session,
                @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
    
    
            // default empty implementation
        }
    }

終わり

関連記事を読み続ける場合は、以下を参照してください: Android カメラ シリーズの記事の進行表.また、Android カメラ シリーズの記事をブックマークして、一緒に学び、進歩させてください。

質問や正誤表がある場合は、コメントするか、次の電子メールで私に連絡してください。
[email protected]

おすすめ

転載: blog.csdn.net/Scott_S/article/details/122143560