注: このページでは、Camera2 パッケージについて説明します。アプリで Camera2 の特定の低レベルの機能を必要とする場合を除き、CameraX を使用することをおすすめします。CameraX と Camera2 は、どちらも Android 5.0(API レベル 21)以降に対応しています。
マルチカメラは Android 9(API レベル 28)で導入されました。リリース以来、 API に対応したデバイスが市場に 投入されていますマルチカメラのユースケースが多い 特定のハードウェア構成と密接に関連しています。つまり、 あらゆるユースケースがすべてのデバイスと互換性があるため、マルチカメラが可能になります。 Google Play の機能を利用することをおすすめします 提供。
一般的なユースケースは次のとおりです。
- ズーム: 切り抜き範囲や目的のフォーカスに応じてカメラを切り替えます あります。
- Depth: 複数のカメラを使用して奥行きマップを作成します。
- ボケ: 推定される奥行き情報を使用して、デジタル一眼レフのような狭い範囲をシミュレートします。 焦点を当てます
論理カメラと物理カメラの違い
multi-camera API を理解するには、 1 台の論理カメラと物理カメラです参考として、3 つのデバイスを持つ 背面カメラ。この例では、3 台の背面カメラのそれぞれが、 物理カメラと見なされます論理カメラは 2 つ以上のカメラを 利用できます論理関数の出力は、 カメラは、基盤となる物理カメラのいずれかからのストリームで、 基盤となる複数の物理カメラからの 融合ストリームです できます。いずれの場合も、ストリーミングはカメラ ハードウェアによって処理されます。 抽象化レイヤ(HAL)。
多くのスマートフォン メーカーがファースト パーティのカメラアプリを開発しており、 デバイスにプリインストールされています。ハードウェアの機能をすべて使用するには プライベートまたは非表示の API を使用したり、Google Cloud からの特別な扱いを ドライバ実装のみをサポートしています。一部 デバイスは論理カメラの概念を実装するために、論理カメラの融合されたストリームを提供します。 割り当てられていますが、特定の特権レベルにのみ関連付けられます。 説明します。多くの場合、外部に公開される物理カメラは 1 つだけです。 説明します。Android 9 より前のサードパーティ デベロッパーは、 これを次の図に示します。
Android 9 以降、Android アプリでは非公開 API を使用できなくなりました。 フレームワークにマルチカメラ サポートが含まれているため、 おすすめの方法です スマートフォン メーカーは すべての物理カメラが同じ方向を向いている場合です次は サードパーティ デベロッパーは、Android 9 以降のデバイスで 高い:
論理カメラの機能は OEM の実装によって異なります。 使用できます。たとえば、Pixel 3 のようなデバイスは、その論理的実装が 基づいて物理カメラを 1 つ選択し、 リクエストされた焦点距離と切り抜き領域です。
multi-camera API
新しい API では、次の定数、クラス、メソッドを新たに追加しています。
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
CameraCharacteristics.getPhysicalCameraIds()
CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()
CameraDevice.createCaptureSession(SessionConfiguration config)
CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
OutputConfiguration
、SessionConfiguration
Android 互換性定義ドキュメント(CDD)の変更により、 マルチカメラ API には、デベロッパーから一定の期待が寄せられています。デバイス Android 9 より前ではデュアルカメラを搭載していたが、複数のカメラを起動していた 試行錯誤を重ねましたAndroid 9 以降の場合: マルチカメラ は、物理デバイス ペアを開くことができるタイミングを指定する一連のルールを提供します。 複数台構成されています。
ほとんどの場合、Android 9 以降を搭載したデバイスは、 カメラ(赤外線など、あまり一般的でないタイプのセンサーを除く) 使い勝手の良い論理カメラです受信されたストリームの組み合わせごとに 動作が保証されています。論理カメラに属する 1 つのストリームを、 2 つのストリームを 1 つにまとめます。
同時に複数のストリーム
複数のカメラ ストリームを同時に使用する
1 台のカメラで複数のストリームを同時に使用するためのルールを説明しています。
重要な変更点が 1 つありますが、複数のカメラに同じルールが適用されます。
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
論理ストリーム YUV_420_888 または raw ストリームを
物理ストリームですつまり、YUV 型または RAW 型の各ストリームは、
同じタイプとサイズのストリームを 2 つ作成します。カメラ ストリームから開始できます。
シングルカメラ デバイスの場合、保証される構成は次のとおりです。
- ストリーム 1: 論理カメラ
id = 0
からの YUV 型、MAXIMUM
サイズ
その後、マルチカメラをサポートするデバイスでセッションを作成できます。 論理 YUV ストリームを 2 つの物理ストリームに置き換えます
- ストリーム 1: YUV タイプ、物理カメラからの
MAXIMUM
サイズid = 1
- ストリーム 2: YUV タイプ、物理カメラからの
MAXIMUM
サイズid = 2
次の場合にのみ、YUV ストリームまたは RAW ストリームを同等の 2 つのストリームに置き換えることができます。
この 2 台のカメラは、論理カメラ グループに属しており、
CameraCharacteristics.getPhysicalCameraIds()
。
このフレームワークによって提供される保証は、 複数の物理カメラのフレームを同時に取得できる追加のストリーム サポートされ、場合によっては複数の物理コンピュータを カメラデバイスを個別に選択できます。外部 IP アドレスの使用を そのためには、Terraform を使用してデバイスごとのテストと調整を行う必要があります。 試行錯誤を繰り返しています
複数の物理カメラを使用するセッションの作成
マルチカメラ対応デバイスで物理カメラを使用する場合は、
CameraDevice
(論理カメラ)であり、1 つのカメラ内で
あります。API を使用して単一のセッションを作成する
CameraDevice.createCaptureSession(SessionConfiguration config)
:
API レベル 28 で追加されました。セッション構成には多数の出力が
各構成には、一連の出力ターゲットと、必要に応じて
物理カメラ ID を指定します。
キャプチャ リクエストには出力ターゲットが関連付けられています。フレームワーク リクエストの送信先となる物理(または論理)カメラを 関連付けられる出力ターゲットを定義します。出力ターゲットが 物理的な証明書とともに出力構成として送信された その物理カメラがリクエストを受信して処理します。
物理カメラのペアの使用
マルチカメラ用の Camera API には、 背面にある物理カメラを見つけます。「新規顧客の獲得」目標を 使用できる物理カメラのペアを特定できます。 論理カメラストリームの 1 つを置き換えます。
Kotlin
/** * Helper class used to encapsulate a logical camera and two underlying * physical cameras */ data class DualCamera(val logicalId: String, val physicalId1: String, val physicalId2: String) fun findDualCameras(manager: CameraManager, facing: Int? = null): List{ val dualCameras = MutableList () // Iterate over all the available camera characteristics manager.cameraIdList.map { Pair(manager.getCameraCharacteristics(it), it) }.filter { // Filter by cameras facing the requested direction facing == null || it.first.get(CameraCharacteristics.LENS_FACING) == facing }.filter { // Filter by logical cameras // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28 it.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!!.contains( CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) }.forEach { // All possible pairs from the list of physical cameras are valid results // NOTE: There could be N physical cameras as part of a logical camera grouping // getPhysicalCameraIds() requires API >= 28 val physicalCameras = it.first.physicalCameraIds.toTypedArray() for (idx1 in 0 until physicalCameras.size) { for (idx2 in (idx1 + 1) until physicalCameras.size) { dualCameras.add(DualCamera( it.second, physicalCameras[idx1], physicalCameras[idx2])) } } } return dualCameras }
Java
/** * Helper class used to encapsulate a logical camera and two underlying * physical cameras */ final class DualCamera { final String logicalId; final String physicalId1; final String physicalId2; DualCamera(String logicalId, String physicalId1, String physicalId2) { this.logicalId = logicalId; this.physicalId1 = physicalId1; this.physicalId2 = physicalId2; } } ListfindDualCameras(CameraManager manager, Integer facing) { List dualCameras = new ArrayList<>(); List cameraIdList; try { cameraIdList = Arrays.asList(manager.getCameraIdList()); } catch (CameraAccessException e) { e.printStackTrace(); cameraIdList = new ArrayList<>(); } // Iterate over all the available camera characteristics cameraIdList.stream() .map(id -> { try { CameraCharacteristics characteristics = manager.getCameraCharacteristics(id); return new Pair<>(characteristics, id); } catch (CameraAccessException e) { e.printStackTrace(); return null; } }) .filter(pair -> { // Filter by cameras facing the requested direction return (pair != null) && (facing == null || pair.first.get(CameraCharacteristics.LENS_FACING).equals(facing)); }) .filter(pair -> { // Filter by logical cameras // CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA requires API >= 28 IntPredicate logicalMultiCameraPred = arg -> arg == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA; return Arrays.stream(pair.first.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)) .anyMatch(logicalMultiCameraPred); }) .forEach(pair -> { // All possible pairs from the list of physical cameras are valid results // NOTE: There could be N physical cameras as part of a logical camera grouping // getPhysicalCameraIds() requires API >= 28 String[] physicalCameras = pair.first.getPhysicalCameraIds().toArray(new String[0]); for (int idx1 = 0; idx1 < physicalCameras.length; idx1++) { for (int idx2 = idx1 + 1; idx2 < physicalCameras.length; idx2++) { dualCameras.add( new DualCamera(pair.second, physicalCameras[idx1], physicalCameras[idx2])); } } }); return dualCameras; }
物理カメラの状態の処理は論理カメラによって制御されます。宛先 「デュアルカメラ」を開くか、該当する物理カメラに対応する論理カメラ カメラ:
Kotlin
fun openDualCamera(cameraManager: CameraManager, dualCamera: DualCamera, // AsyncTask is deprecated beginning API 30 executor: Executor = AsyncTask.SERIAL_EXECUTOR, callback: (CameraDevice) -> Unit) { // openCamera() requires API >= 28 cameraManager.openCamera( dualCamera.logicalId, executor, object : CameraDevice.StateCallback() { override fun onOpened(device: CameraDevice) = callback(device) // Omitting for brevity... override fun onError(device: CameraDevice, error: Int) = onDisconnected(device) override fun onDisconnected(device: CameraDevice) = device.close() }) }
Java
void openDualCamera(CameraManager cameraManager, DualCamera dualCamera, Executor executor, CameraDeviceCallback cameraDeviceCallback ) { // openCamera() requires API >= 28 cameraManager.openCamera(dualCamera.logicalId, executor, new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice cameraDevice) { cameraDeviceCallback.callback(cameraDevice); } @Override public void onDisconnected(@NonNull CameraDevice cameraDevice) { cameraDevice.close(); } @Override public void onError(@NonNull CameraDevice cameraDevice, int i) { onDisconnected(cameraDevice); } }); }
どのカメラを開くかを選択するだけでなく、 以前の Android バージョンではカメラに接続できません。新しい セッション構成 API がフレームワークに指示し、特定のターゲットを 特定の物理カメラ ID:
Kotlin
/** * Helper type definition that encapsulates 3 sets of output targets: * * 1. Logical camera * 2. First physical camera * 3. Second physical camera */ typealias DualCameraOutputs = Triple?, MutableList ?, MutableList ?> fun createDualCameraSession(cameraManager: CameraManager, dualCamera: DualCamera, targets: DualCameraOutputs, // AsyncTask is deprecated beginning API 30 executor: Executor = AsyncTask.SERIAL_EXECUTOR, callback: (CameraCaptureSession) -> Unit) { // Create 3 sets of output configurations: one for the logical camera, and // one for each of the physical cameras. val outputConfigsLogical = targets.first?.map { OutputConfiguration(it) } val outputConfigsPhysical1 = targets.second?.map { OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId1) } } val outputConfigsPhysical2 = targets.third?.map { OutputConfiguration(it).apply { setPhysicalCameraId(dualCamera.physicalId2) } } // Put all the output configurations into a single flat array val outputConfigsAll = arrayOf( outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2) .filterNotNull().flatMap { it } // Instantiate a session configuration that can be used to create a session val sessionConfiguration = SessionConfiguration( SessionConfiguration.SESSION_REGULAR, outputConfigsAll, executor, object : CameraCaptureSession.StateCallback() { override fun onConfigured(session: CameraCaptureSession) = callback(session) // Omitting for brevity... override fun onConfigureFailed(session: CameraCaptureSession) = session.device.close() }) // Open the logical camera using the previously defined function openDualCamera(cameraManager, dualCamera, executor = executor) { // Finally create the session and return via callback it.createCaptureSession(sessionConfiguration) } }
Java
/** * Helper class definition that encapsulates 3 sets of output targets: ** 1. Logical camera * 2. First physical camera * 3. Second physical camera */ final class DualCameraOutputs { private final List
logicalCamera; private final List firstPhysicalCamera; private final List secondPhysicalCamera; public DualCameraOutputs(List logicalCamera, List firstPhysicalCamera, List third) { this.logicalCamera = logicalCamera; this.firstPhysicalCamera = firstPhysicalCamera; this.secondPhysicalCamera = third; } public List getLogicalCamera() { return logicalCamera; } public List getFirstPhysicalCamera() { return firstPhysicalCamera; } public List getSecondPhysicalCamera() { return secondPhysicalCamera; } } interface CameraCaptureSessionCallback { void callback(CameraCaptureSession cameraCaptureSession); } void createDualCameraSession(CameraManager cameraManager, DualCamera dualCamera, DualCameraOutputs targets, Executor executor, CameraCaptureSessionCallback cameraCaptureSessionCallback) { // Create 3 sets of output configurations: one for the logical camera, and // one for each of the physical cameras. List outputConfigsLogical = targets.getLogicalCamera().stream() .map(OutputConfiguration::new) .collect(Collectors.toList()); List outputConfigsPhysical1 = targets.getFirstPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId1); return outputConfiguration; }) .collect(Collectors.toList()); List outputConfigsPhysical2 = targets.getSecondPhysicalCamera().stream() .map(s -> { OutputConfiguration outputConfiguration = new OutputConfiguration(s); outputConfiguration.setPhysicalCameraId(dualCamera.physicalId2); return outputConfiguration; }) .collect(Collectors.toList()); // Put all the output configurations into a single flat array List outputConfigsAll = Stream.of( outputConfigsLogical, outputConfigsPhysical1, outputConfigsPhysical2 ) .filter(Objects::nonNull) .flatMap(Collection::stream) .collect(Collectors.toList()); // Instantiate a session configuration that can be used to create a session SessionConfiguration sessionConfiguration = new SessionConfiguration( SessionConfiguration.SESSION_REGULAR, outputConfigsAll, executor, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSessionCallback.callback(cameraCaptureSession); } // Omitting for brevity... @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { cameraCaptureSession.getDevice().close(); } }); // Open the logical camera using the previously defined function openDualCamera(cameraManager, dualCamera, executor, (CameraDevice c) -> // Finally create the session and return via callback c.createCaptureSession(sessionConfiguration)); }
詳しくは、
createCaptureSession
サポートされているストリームの組み合わせについては、こちらをご覧ください。ストリームの組み合わせ
単一の論理カメラ上の複数のストリームに使用します。互換性は
同じ構成を使用し、一方のストリームを 2 つのストリームに置き換える
同じ論理カメラの一部である 2 台の物理カメラから
接続されます
カメラ セッション 必要なメッセージをディスパッチする キャプチャ リクエスト。各 関連する物理サーバーからデータを受信し、 使用したり、論理カメラにフォールバックしたりできます。
Zoom のユースケースの例
複数の物理カメラを単一のストリームに統合して、 物理カメラを切り替えて 異なる「ズームレベル」を効果的に捉えています。
<ph type="x-smartling-placeholder">最初に物理カメラのペアを選択して、ユーザーが切り替えられるようにします。 あります。最大限の効果を得るには、 最短 / 最大焦点距離。
Kotlin
fun findShortLongCameraPair(manager: CameraManager, facing: Int? = null): DualCamera? { return findDualCameras(manager, facing).map { val characteristics1 = manager.getCameraCharacteristics(it.physicalId1) val characteristics2 = manager.getCameraCharacteristics(it.physicalId2) // Query the focal lengths advertised by each physical camera val focalLengths1 = characteristics1.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F) val focalLengths2 = characteristics2.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F) // Compute the largest difference between min and max focal lengths between cameras val focalLengthsDiff1 = focalLengths2.maxOrNull()!! - focalLengths1.minOrNull()!! val focalLengthsDiff2 = focalLengths1.maxOrNull()!! - focalLengths2.minOrNull()!! // Return the pair of camera IDs and the difference between min and max focal lengths if (focalLengthsDiff1 < focalLengthsDiff2) { Pair(DualCamera(it.logicalId, it.physicalId1, it.physicalId2), focalLengthsDiff1) } else { Pair(DualCamera(it.logicalId, it.physicalId2, it.physicalId1), focalLengthsDiff2) } // Return only the pair with the largest difference, or null if no pairs are found }.maxByOrNull { it.second }?.first }
Java
// Utility functions to find min/max value in float[] float findMax(float[] array) { float max = Float.NEGATIVE_INFINITY; for(float cur: array) max = Math.max(max, cur); return max; } float findMin(float[] array) { float min = Float.NEGATIVE_INFINITY; for(float cur: array) min = Math.min(min, cur); return min; } DualCamera findShortLongCameraPair(CameraManager manager, Integer facing) { return findDualCameras(manager, facing).stream() .map(c -> { CameraCharacteristics characteristics1; CameraCharacteristics characteristics2; try { characteristics1 = manager.getCameraCharacteristics(c.physicalId1); characteristics2 = manager.getCameraCharacteristics(c.physicalId2); } catch (CameraAccessException e) { e.printStackTrace(); return null; } // Query the focal lengths advertised by each physical camera float[] focalLengths1 = characteristics1.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); float[] focalLengths2 = characteristics2.get( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); // Compute the largest difference between min and max focal lengths between cameras Float focalLengthsDiff1 = findMax(focalLengths2) - findMin(focalLengths1); Float focalLengthsDiff2 = findMax(focalLengths1) - findMin(focalLengths2); // Return the pair of camera IDs and the difference between min and max focal lengths if (focalLengthsDiff1 < focalLengthsDiff2) { return new Pair<>(new DualCamera(c.logicalId, c.physicalId1, c.physicalId2), focalLengthsDiff1); } else { return new Pair<>(new DualCamera(c.logicalId, c.physicalId2, c.physicalId1), focalLengthsDiff2); } }) // Return only the pair with the largest difference, or null if no pairs are found .max(Comparator.comparing(pair -> pair.second)).get().first; }
そのための理にかなったアーキテクチャは、
SurfaceViews
- ストリームごとに 1 つ。
これらの SurfaceViews
は、ユーザー操作に基づいてスワップされ、一方のみが
表示できます。
次のコードは、論理カメラを開いてカメラを設定する方法を示しています。 カメラ セッションの作成、2 つのプレビュー ストリームの開始:
Kotlin
val cameraManager: CameraManager = ... // Get the two output targets from the activity / fragment val surface1 = ... // from SurfaceView val surface2 = ... // from SurfaceView val dualCamera = findShortLongCameraPair(manager)!! val outputTargets = DualCameraOutputs( null, mutableListOf(surface1), mutableListOf(surface2)) // Here you open the logical camera, configure the outputs and create a session createDualCameraSession(manager, dualCamera, targets = outputTargets) { session -> // Create a single request which has one target for each physical camera // NOTE: Each target receive frames from only its associated physical camera val requestTemplate = CameraDevice.TEMPLATE_PREVIEW val captureRequest = session.device.createCaptureRequest(requestTemplate).apply { arrayOf(surface1, surface2).forEach { addTarget(it) } }.build() // Set the sticky request for the session and you are done session.setRepeatingRequest(captureRequest, null, null) }
Java
CameraManager manager = ...; // Get the two output targets from the activity / fragment Surface surface1 = ...; // from SurfaceView Surface surface2 = ...; // from SurfaceView DualCamera dualCamera = findShortLongCameraPair(manager, null); DualCameraOutputs outputTargets = new DualCameraOutputs( null, Collections.singletonList(surface1), Collections.singletonList(surface2)); // Here you open the logical camera, configure the outputs and create a session createDualCameraSession(manager, dualCamera, outputTargets, null, (session) -> { // Create a single request which has one target for each physical camera // NOTE: Each target receive frames from only its associated physical camera CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); Arrays.asList(surface1, surface2).forEach(captureRequestBuilder::addTarget); // Set the sticky request for the session and you are done session.setRepeatingRequest(captureRequestBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } });
あとはユーザーが 2 つのモードを切り替える UI を用意するだけです。
ボタンなどのサーフェスや、SurfaceView
のダブルタップ。さらに、
なんらかのシーン分析を実行し、2 つのストリームを切り替える
自動的に適用されます。
レンズの歪み
どのレンズにもある程度の歪みが生じます。Android では、
レンズによって生じる歪み
CameraCharacteristics.LENS_DISTORTION
これは、サポートが終了した
CameraCharacteristics.LENS_RADIAL_DISTORTION
。
論理カメラの場合、歪みは最小限で、アプリケーションで
カメラからのフレームが増減します。物理カメラの場合は
特に広角レンズでは、レンズ構成が大きく異なる可能性があります。
。
デバイスによっては、以下を使用して自動歪み補正を実装している場合があります。
CaptureRequest.DISTORTION_CORRECTION_MODE
。
ほとんどのデバイスで、歪み補正はデフォルトでオンになっています。
Kotlin
val cameraSession: CameraCaptureSession = ... // Use still capture template to build the capture request val captureRequest = cameraSession.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE ) // Determine if this device supports distortion correction val characteristics: CameraCharacteristics = ... val supportsDistortionCorrection = characteristics.get( CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES )?.contains( CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ) ?: false if (supportsDistortionCorrection) { captureRequest.set( CaptureRequest.DISTORTION_CORRECTION_MODE, CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ) } // Add output target, set other capture request parameters... // Dispatch the capture request cameraSession.capture(captureRequest.build(), ...)
Java
CameraCaptureSession cameraSession = ...; // Use still capture template to build the capture request CaptureRequest.Builder captureRequestBuilder = null; try { captureRequestBuilder = cameraSession.getDevice().createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE ); } catch (CameraAccessException e) { e.printStackTrace(); } // Determine if this device supports distortion correction CameraCharacteristics characteristics = ...; boolean supportsDistortionCorrection = Arrays.stream( characteristics.get( CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES )) .anyMatch(i -> i == CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY); if (supportsDistortionCorrection) { captureRequestBuilder.set( CaptureRequest.DISTORTION_CORRECTION_MODE, CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY ); } // Add output target, set other capture request parameters... // Dispatch the capture request cameraSession.capture(captureRequestBuilder.build(), ...);
このモードでキャプチャ リクエストを設定すると、フレームレートに影響する可能性があります。 画像が生成されます。歪み補正機能は、 使用できます。