Nota: En esta página, se hace referencia al paquete Camera2. A menos que tu app requiera funciones específicas y de bajo nivel de Camera2, te recomendamos usar CameraX. CameraX y Camera2 admiten Android 5.0 (nivel de API 21) y versiones posteriores.
La función de varias cámaras se introdujo con Android 9 (nivel de API 28). Desde su lanzamiento, salieron al mercado dispositivos compatibles con la API. Muchos casos de uso de varias cámaras tienen acoplamiento alto con una configuración de hardware específica. En otras palabras, no todos los casos de uso son compatibles con todos los dispositivos, por lo que las funciones de varias cámaras son una buena opción para la entrega de funciones en Play.
Estos son algunos casos prácticos típicos:
- Zoom: Alterna entre cámaras según la región de recorte o la longitud focal deseada.
- Profundidad: Se usan varias cámaras para crear un mapa de profundidad.
- Bokeh: Uso de información de profundidad inferida para simular un rango de enfoque estrecho similar a una cámara réflex digital
La diferencia entre cámaras lógicas y físicas
Para comprender la API de varias cámaras, es necesario entender la diferencia entre las cámaras lógicas y las físicas. A modo de referencia, considera un dispositivo con tres cámaras posteriores. En este ejemplo, cada una de las tres cámaras posteriores se considera una cámara física. Una cámara lógica es un grupo de dos o más de esas cámaras físicas. La salida de la cámara lógica puede ser una transmisión que proviene de una de las cámaras físicas subyacentes o una transmisión fusionada que proviene de más de una cámara física subyacente a la vez. De cualquier manera, la capa de abstracción de hardware (HAL) de la cámara controla la transmisión.
Muchos fabricantes de teléfonos desarrollan aplicaciones de cámara propias, que suelen venir preinstaladas en sus dispositivos. Para usar todas las capacidades del hardware, pueden usar APIs ocultas o privadas, o recibir un tratamiento especial de la implementación del controlador al que otras aplicaciones no tienen acceso. Algunos dispositivos implementan el concepto de cámaras lógicas proporcionando un flujo fusionado de marcos de las diferentes cámaras físicas, pero solo a ciertas aplicaciones con privilegios. A menudo, solo una de las cámaras físicas se expone al framework. La situación de los desarrolladores externos antes de Android 9 se ilustra en el siguiente diagrama:
A partir de Android 9, ya no se permiten las APIs privadas en las apps para Android. Dado que se agregó compatibilidad con varias cámaras en el framework, las prácticas recomendadas de Android recomiendan que los fabricantes de teléfonos expongan una cámara lógica para todas las cámaras físicas que estén orientadas en la misma dirección. A continuación, se muestra lo que los desarrolladores externos deberían ver en los dispositivos con Android 9 y versiones posteriores:
Lo que proporciona la cámara lógica depende por completo de la implementación del OEM de la HAL de la cámara. Por ejemplo, un dispositivo como Pixel 3 implementa su cámara lógica de manera que elige una de sus cámaras físicas en función de la longitud focal y la región de recorte solicitadas.
API de varias cámaras
La nueva API agrega las siguientes constantes, clases y métodos nuevos:
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
CameraCharacteristics.getPhysicalCameraIds()
CameraCharacteristics.getAvailablePhysicalCameraRequestKeys()
CameraDevice.createCaptureSession(SessionConfiguration config)
CameraCharacteritics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
OutputConfiguration
ySessionConfiguration
Debido a los cambios en el Documento de definición de compatibilidad de Android (CDD), la API de varias cámaras también incluye ciertas expectativas de los desarrolladores. Los dispositivos con dos cámaras existían antes de Android 9, pero abrir más de una cámara implicaba ensayo y error de forma simultánea. En Android 9 y versiones posteriores, la función de varias cámaras proporciona un conjunto de reglas para especificar cuándo es posible abrir un par de cámaras físicas que forman parte de la misma cámara lógica.
En la mayoría de los casos, los dispositivos con Android 9 y versiones posteriores exponen todas las cámaras físicas (excepto posiblemente para los tipos de sensores menos comunes, como los infrarrojos), junto con una cámara lógica más fácil de usar. Para cada combinación de transmisiones que tenga garantía de funcionar, se puede reemplazar una que pertenezca a una cámara lógica por dos transmisiones de las cámaras físicas subyacentes.
Varias transmisiones a la vez
El uso de varias transmisiones de cámara a la vez abarca las reglas para usar varias transmisiones de forma simultánea en una sola cámara.
Con una adición notable, se aplican las mismas reglas para varias cámaras.
CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
explica cómo reemplazar un YUV_420_888 lógico o una transmisión sin procesar por dos transmisiones físicas. Es decir, cada transmisión de tipo YUV o RAW se puede reemplazar por dos transmisiones de tipo y tamaño idénticos. Puedes comenzar con una transmisión de cámara con la siguiente configuración garantizada para dispositivos de una sola cámara:
- Transmisión 1: tipo YUV, tamaño
MAXIMUM
de la cámara lógicaid = 0
Luego, un dispositivo compatible con varias cámaras te permite crear una sesión que reemplace esa transmisión YUV lógica por dos transmisiones físicas:
- Transmisión 1: tipo YUV,
MAXIMUM
tamaño de la cámara físicaid = 1
- Transmisión 2: tipo YUV,
MAXIMUM
de tamaño de la cámara físicaid = 2
Puedes reemplazar una transmisión YUV o RAW por dos transmisiones equivalentes solo si esas dos cámaras forman parte de una agrupación lógica de cámaras, que aparece en CameraCharacteristics.getPhysicalCameraIds()
.
Las garantías que proporciona el framework son solo el mínimo requerido para obtener fotogramas de más de una cámara física a la vez. La mayoría de los dispositivos admiten transmisiones adicionales, que a veces permiten abrir varios dispositivos de cámara físicos de forma independiente. Dado que no es una garantía estricta del framework, hacerlo requiere realizar pruebas y ajustes por dispositivo con prueba y error.
Cómo crear una sesión con varias cámaras físicas
Cuando uses cámaras físicas en un dispositivo compatible con varias cámaras, abre una sola CameraDevice
(la cámara lógica) y, luego, interactúa con ella en una sola sesión. Crea la sesión única con la API CameraDevice.createCaptureSession(SessionConfiguration config)
, que se agregó en el nivel de API 28. La configuración de la sesión tiene varias configuraciones de salida, cada una de las cuales tiene un conjunto de objetivos de salida y, de forma opcional, un ID de cámara física deseado.
Las solicitudes de captura tienen un destino de salida asociado. El framework determina a qué cámara física (o lógica) se envían las solicitudes en función del destino de salida que se adjunta. Si el objetivo de salida corresponde a uno de los destinos que se envió como una configuración de salida junto con un ID de cámara física, esa cámara física recibe y procesa la solicitud.
Usar un par de cámaras físicas
Otra incorporación de las APIs de cámara para varias cámaras es la capacidad de identificar cámaras lógicas y encontrar las cámaras físicas detrás de ellas. Puedes definir una función que te ayude a identificar pares potenciales de cámaras físicas que puedes usar para reemplazar una de las transmisiones lógicas de la cámara:
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; }
La cámara lógica controla el control del estado de las cámaras físicas. Para abrir una "cámara dual", abre la cámara lógica correspondiente a las cámaras físicas:
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); } }); }
Además de seleccionar qué cámara abrir, el proceso es el mismo que el de abrir una en versiones anteriores de Android. Cuando se crea una sesión de captura con la nueva API de configuración de sesiones, se le indica al framework que asocie ciertos objetivos con IDs de cámaras físicas específicos:
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)); }
Consulta createCaptureSession
para obtener información sobre la combinación de transmisiones compatible. La combinación de transmisiones sirve para varias transmisiones en una sola cámara lógica. La compatibilidad se extiende al uso de la misma configuración y al reemplazo de una de esas transmisiones por dos transmisiones de dos cámaras físicas que forman parte de la misma cámara lógica.
Con la sesión de cámara lista, envía las solicitudes de captura deseadas. Cada objetivo de la solicitud de captura recibe sus datos de la cámara física asociada, si hay alguno en uso, o recurre a la cámara lógica.
Caso de uso de ejemplo de Zoom
Es posible usar la combinación de cámaras físicas en una sola transmisión para que los usuarios puedan cambiar entre las diferentes cámaras físicas y experimentar un campo visual diferente, y capturar de manera efectiva un "nivel de zoom" diferente.
Primero, selecciona el par de cámaras físicas para permitir que los usuarios cambien entre ellas. Para obtener el máximo efecto, puedes elegir el par de cámaras que proporcionan la longitud focal mínima y máxima disponible.
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; }
Una arquitectura razonable para esto sería tener dos SurfaceViews
, uno para cada transmisión.
Estos SurfaceViews
se intercambian según la interacción del usuario, de modo que solo uno sea visible en un momento determinado.
En el siguiente código, se muestra cómo abrir la cámara lógica, configurar los resultados de la cámara, crear una sesión de la cámara e iniciar dos transmisiones de vista previa:
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(); } });
Lo único que queda por hacer es proporcionar una IU para que el usuario pueda alternar entre las dos plataformas, como un botón o presionar dos veces la SurfaceView
. Incluso puedes realizar algún tipo de análisis de escena y alternar automáticamente entre las dos transmisiones.
Distorsión del lente
Todos los lentes producen una determinada cantidad de distorsión. En Android, puedes consultar la distorsión creada por las lentes con CameraCharacteristics.LENS_DISTORTION
, que reemplaza el elemento CameraCharacteristics.LENS_RADIAL_DISTORTION
obsoleto.
En el caso de las cámaras lógicas, la distorsión es mínima y tu aplicación puede usar los fotogramas más o menos a medida que provienen de la cámara. En el caso de las cámaras físicas, las configuraciones de los lentes pueden ser muy diferentes, en especial, con lentes gran angular.
Algunos dispositivos pueden implementar la corrección de distorsión automática mediante CaptureRequest.DISTORTION_CORRECTION_MODE
.
La corrección de distorsión estará activada de forma predeterminada en la mayoría de los dispositivos.
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(), ...);
Configurar una solicitud de captura en este modo puede afectar la velocidad de fotogramas que produce la cámara. Puedes elegir configurar la corrección de distorsión solo en capturas de imágenes estáticas.