Configura cada caso de uso de CameraX para controlar diferentes aspectos de sus operaciones.
Por ejemplo, con el caso de uso de captura de imágenes, puedes establecer una relación de aspecto objetivo y un modo de flash. El siguiente código muestra un ejemplo:
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
Además de las opciones de configuración, algunos casos de uso exponen APIs para alterar, de forma dinámica, la configuración después de que se crean. Para obtener información sobre la configuración específica de cada caso de uso, consulta Cómo implementar una vista previa, Cómo analizar imágenes y Captura de imágenes.
CameraXConfig
Para simplificar, CameraX tiene configuraciones predeterminadas, como ejecutores internos y controladores que son adecuados para la mayoría de los casos de uso. Sin embargo, si tu aplicación tiene requisitos especiales o prefiere personalizar esas configuraciones, CameraXConfig
es la interfaz para ese propósito.
Con CameraXConfig
, una aplicación puede hacer lo siguiente:
- Optimizar la latencia de inicio con
setAvailableCameraLimiter()
- Proporcionar el ejecutor de la aplicación a CameraX con
setCameraExecutor()
- Reemplazar el controlador de programador predeterminado por
setSchedulerHandler()
- Cambiar el nivel de registro con
setMinimumLoggingLevel()
Modelo de uso
En el siguiente procedimiento, se describe la manera de usar CameraXConfig
:
- Crea un objeto
CameraXConfig
con tus configuraciones personalizadas. - Implementa la interfaz
CameraXConfig.Provider
en tuApplication
y devuelve tu objetoCameraXConfig
engetCameraXConfig()
. - Agrega la clase
Application
al archivoAndroidManifest.xml
, como se describe aquí.
En la siguiente muestra de código, se restringe el registro de CameraX a los mensajes de error únicamente:
Kotlin
class CameraApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setMinimumLoggingLevel(Log.ERROR).build() } }
Mantén una copia local del objeto CameraXConfig
si tu aplicación necesita saber la configuración de CameraX después de establecerlo.
Limitador de cámara
Durante la primera invocación de ProcessCameraProvider.getInstance()
, CameraX enumera y consulta las características de las cámaras disponibles en el dispositivo. Debido a que CameraX necesita comunicarse con los componentes de hardware, este proceso puede demorar bastante tiempo en cada cámara, especialmente en dispositivos de gama baja. Si tu aplicación solo usa cámaras específicas en el dispositivo, como la cámara frontal predeterminada, puedes configurar CameraX para ignorar otras cámaras, lo que puede reducir la latencia de inicio de las cámaras que usa la aplicación.
Si el CameraSelector
que se pasa a CameraXConfig.Builder.setAvailableCamerasLimiter()
filtra una cámara, CameraX se comporta como si esa cámara no existiera. Por ejemplo, el siguiente código limita la aplicación para que use solo la cámara posterior predeterminada del dispositivo:
Kotlin
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }
Subprocesos
Muchas de las APIs de la plataforma en las que se compiló CameraX requieren el bloqueo de la comunicación entre procesos (IPC) con hardware que a veces puede tardar cientos de milisegundos en responder. Por este motivo, CameraX solo llama a estas APIs desde subprocesos en segundo plano para que el subproceso principal no esté bloqueado y la IU permanezca fluida. CameraX administra internamente estos subprocesos en segundo plano para que este comportamiento se vea transparente. Sin embargo, algunas aplicaciones requieren un control estricto de los subprocesos. CameraXConfig
permite que una aplicación establezca los subprocesos en segundo plano que se usan a través de CameraXConfig.Builder.setCameraExecutor()
y CameraXConfig.Builder.setSchedulerHandler()
.
Ejecutor de cámara
El ejecutor de cámara se usa para todas las llamadas internas a la API de la plataforma de cámara y también para las devoluciones de llamada de estas APIs. CameraX asigna y administra un Executor
interno para realizar estas tareas.
Sin embargo, si tu aplicación requiere un control más estricto de los subprocesos, usa CameraXConfig.Builder.setCameraExecutor()
.
Controlador del programador
Se usa para programar tareas internas a intervalos fijos, como volver a abrir la cámara cuando no está disponible. Este controlador no ejecuta trabajos y solo los envía al ejecutor de la cámara. En ocasiones, también se usa en las plataformas de APIs heredadas que requieren un Handler
para las devoluciones de llamada. En esos casos, las devoluciones de llamada solo se envían directamente al ejecutor de la cámara. CameraX asigna y administra un elemento HandlerThread
interno para realizar esas tareas, pero puedes anularlo con CameraXConfig.Builder.setSchedulerHandler()
.
Registro
El registro de CameraX permite que las aplicaciones filtren mensajes de Logcat, ya que se recomienda evitar mensajes detallados en el código de producción. CameraX admite cuatro niveles de registro, desde el más detallado hasta el más grave:
Log.DEBUG
(predeterminado)Log.INFO
Log.WARN
Log.ERROR
Consulta la documentación de registro de Android para obtener descripciones detalladas de estos niveles de registro. Usa CameraXConfig.Builder.setMinimumLoggingLevel(int)
para establecer el nivel de registro apropiado para tu aplicación.
Selección automática
Automáticamente, CameraX proporciona funciones específicas para el dispositivo en el que se ejecuta tu app. Por ejemplo, CameraX determinará automáticamente la mejor resolución si no especificas una o si la que especificas no es compatible. Todo esto lo administra la biblioteca, lo que elimina la necesidad de escribir un código específico del dispositivo.
El objetivo de CameraX es inicializar con éxito una sesión de la cámara. Por lo tanto, CameraX ajusta la resolución y las relaciones de aspecto, según la capacidad del dispositivo por los siguientes motivos:
- El dispositivo no admite el tipo de resolución solicitado.
- El dispositivo tiene problemas de compatibilidad, como los dispositivos heredados que requieren determinadas resoluciones para funcionar correctamente.
- En algunos dispositivos, ciertos formatos solo están disponibles en algunas relaciones de aspecto.
- El dispositivo tiene preferencia por un "mod16 más cercano" para JPEG o codificación de video. Para obtener más información, consulta
SCALER_STREAM_CONFIGURATION_MAP
.
Aunque CameraX crea y administra la sesión, siempre verifica en el código los tamaños de imagen que se muestran en los resultados del caso de uso y ajustarlos en consecuencia.
Rotación
De forma predeterminada, se configura la rotación de la cámara para que coincida con la rotación de la pantalla durante la creación del caso de uso. En este caso predeterminado, CameraX produce resultados que permiten que la app pueda lograr lo que esperas obtener en la vista previa. Puedes cambiar la rotación a un valor personalizado para admitir dispositivos de una amplia variedad de pantallas. Para ello, pasa la orientación de la pantalla del momento cuando se configura el caso de uso o hazlo de manera dinámica cuando ya se haya creado.
Tu app puede establecer la rotación objetivo con los parámetros de configuración. Luego, puede actualizar la configuración de rotación con los métodos de las APIs de casos de uso (como ImageAnalysis.setTargetRotation()
), incluso mientras el ciclo de vida está en estado activo. Esto es posible cuando la app está bloqueada en modo de retrato, por lo que no se produce ninguna reconfiguración de rotación, pero la foto o el caso de uso de análisis requieren que se tenga en cuenta la rotación del dispositivo de ese momento. Por ejemplo, el reconocimiento de rotación puede ser necesario para que los rostros estén orientados correctamente y se puedan detectar, o que las fotos estén configuradas en modo horizontal o vertical.
Los datos de las imágenes capturadas se pueden almacenar sin información de rotación. Los datos Exif contienen información de rotación para que las aplicaciones de galería puedan mostrar la imagen en la orientación correcta después de guardarla.
Para mostrar datos de vista previa con la orientación correcta, puedes usar el resultado de metadatos de Preview.PreviewOutput()
para crear transformaciones.
En la siguiente muestra de código, se indica cómo configurar la rotación en un evento de orientación:
Kotlin
override fun onCreate() { val imageCapture = ImageCapture.Builder().build() val orientationEventListener = object : OrientationEventListener(this as Context) { override fun onOrientationChanged(orientation : Int) { // Monitors orientation values to determine the target rotation value val rotation : Int = when (orientation) { in 45..134 -> Surface.ROTATION_270 in 135..224 -> Surface.ROTATION_180 in 225..314 -> Surface.ROTATION_90 else -> Surface.ROTATION_0 } imageCapture.targetRotation = rotation } } orientationEventListener.enable() }
Java
@Override public void onCreate() { ImageCapture imageCapture = new ImageCapture.Builder().build(); OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) { @Override public void onOrientationChanged(int orientation) { int rotation; // Monitors orientation values to determine the target rotation value if (orientation >= 45 && orientation < 135) { rotation = Surface.ROTATION_270; } else if (orientation >= 135 && orientation < 225) { rotation = Surface.ROTATION_180; } else if (orientation >= 225 && orientation < 315) { rotation = Surface.ROTATION_90; } else { rotation = Surface.ROTATION_0; } imageCapture.setTargetRotation(rotation); } }; orientationEventListener.enable(); }
En función de la rotación, cada caso de uso rotará los datos de la imagen directamente o proporcionará los metadatos de rotación a los consumidores de los datos de la imagen no rotada.
- Preview: Se proporciona un resultado de metadatos para que se conozca la rotación de la resolución objetivo con el elemento
Preview.getTargetRotation()
. - ImageAnalysis: El resultado de los metadatos se proporciona para que se conozcan las coordenadas de búfer de la imagen en relación con las coordenadas de la pantalla.
- ImageCapture: Se modifican los metadatos de Exif de la imagen, el búfer o ambos para tener en cuenta la configuración de rotación. El valor modificado depende de la implementación de HAL.
Rectángulo de recorte
De forma predeterminada, el rectángulo de recorte es el rectángulo del búfer completo. Puedes personalizarlo con ViewPort
y UseCaseGroup
. Agrupando casos de uso y configurando el viewport, CameraX garantiza que los rectángulos de recorte de todos los casos de uso del grupo apunten a la misma área del sensor de la cámara.
En el siguiente fragmento de código, se muestra la manera de usar estas dos clases:
Kotlin
val viewPort = ViewPort.Builder(Rational(width, height), display.rotation).build() val useCaseGroup = UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build() cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)
Java
ViewPort viewPort = new ViewPort.Builder( new Rational(width, height), getDisplay().getRotation()).build(); UseCaseGroup useCaseGroup = new UseCaseGroup.Builder() .addUseCase(preview) .addUseCase(imageAnalysis) .addUseCase(imageCapture) .setViewPort(viewPort) .build(); cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);
ViewPort
define el rectángulo del búfer visible para los usuarios finales. Luego, CameraX calcula el rectángulo de recorte más grande posible, según las propiedades de viewport y los casos de uso adjuntos. Por lo general, para lograr un efecto WYSIWYG, puedes configurar el viewport, según el caso de uso de vista previa. Una forma sencilla de obtener el viewport es usar PreviewView
.
En los siguientes fragmentos de código, se muestra la manera de obtener el objeto ViewPort
:
Kotlin
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
Java
ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();
En el ejemplo anterior, lo que obtiene la app de ImageAnalysis
y ImageCapture
coincide con lo que el usuario final ve en PreviewView
, si se supone que el tipo de escala de PreviewView
se establece en el valor predeterminado, FILL_CENTER
. Después de aplicar el rectángulo de recorte y la rotación al búfer de salida, la imagen de todos los casos de uso es la misma, aunque posiblemente con resoluciones diferentes. Si deseas obtener más información para aplicar la información de transformación, consulta Resultado de la transformación.
Selección de cámara
CameraX selecciona automáticamente el mejor dispositivo de cámara, según los requisitos y casos de uso de tu aplicación. Si quieres usar un dispositivo diferente al que seleccionaste para ti, hay algunas opciones:
- Solicita la cámara frontal predeterminada con
CameraSelector.DEFAULT_FRONT_CAMERA
. - Solicita la cámara posterior predeterminada con
CameraSelector.DEFAULT_BACK_CAMERA
. - Filtra la lista de dispositivos disponibles por su
CameraCharacteristics
conCameraSelector.Builder.addCameraFilter()
.
En la siguiente muestra de código, se ilustra cómo crear un CameraSelector
para influenciar en la selección de dispositivos:
Kotlin
fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? { val cam2Infos = provider.availableCameraInfos.map { Camera2CameraInfo.from(it) }.sortedByDescending { // HARDWARE_LEVEL is Int type, with the order of: // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) } return when { cam2Infos.isNotEmpty() -> { CameraSelector.Builder() .addCameraFilter { it.filter { camInfo -> // cam2Infos[0] is either EXTERNAL or best built-in camera val thisCamId = Camera2CameraInfo.from(camInfo).cameraId thisCamId == cam2Infos[0].cameraId } }.build() } else -> null } } // create a CameraSelector for the USB camera (or highest level internal camera) val selector = selectExternalOrBestCamera(processCameraProvider) processCameraProvider.bindToLifecycle(this, selector, preview, analysis)
Cómo seleccionar varias cámaras a la vez
A partir de CameraX 1.3, también puedes seleccionar varias cámaras de forma simultánea. Por ejemplo, puedes utilizar una cámara frontal y una posterior para tomar fotos o grabar videos desde ambas perspectivas al mismo tiempo.
Cuando se usa la función Cámara simultánea, el dispositivo puede operar al mismo tiempo dos cámaras con lentes diferentes, o bien dos cámaras traseras al mismo tiempo. En el siguiente bloque de código, se muestra la manera de configurar dos cámaras cuando se llama a bindToLifecycle
y la manera de obtener ambos objetos Camera del elemento ConcurrentCamera
que se devuelve.
Kotlin
// Build ConcurrentCameraConfig val primary = ConcurrentCamera.SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ) val secondary = ConcurrentCamera.SingleCameraConfig( secondaryCameraSelector, useCaseGroup, lifecycleOwner ) val concurrentCamera = cameraProvider.bindToLifecycle( listOf(primary, secondary) ) val primaryCamera = concurrentCamera.cameras[0] val secondaryCamera = concurrentCamera.cameras[1]
Java
// Build ConcurrentCameraConfig SingleCameraConfig primary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); SingleCameraConfig secondary = new SingleCameraConfig( primaryCameraSelector, useCaseGroup, lifecycleOwner ); ConcurrentCamera concurrentCamera = mCameraProvider.bindToLifecycle(Arrays.asList(primary, secondary)); Camera primaryCamera = concurrentCamera.getCameras().get(0); Camera secondaryCamera = concurrentCamera.getCameras().get(1);
Resolución de la cámara
Puedes permitir que CameraX configure la resolución de imagen, según una combinación de funciones del dispositivo, el nivel de hardware compatible con el dispositivo, el caso de uso y la relación de aspecto proporcionada. Como alternativa, puedes establecer una resolución objetivo específica o una relación de aspecto específica en casos de uso que admitan esa configuración.
Resolución automática
CameraX puede determinar automáticamente la mejor configuración de resolución, según los casos de uso especificados en el objeto cameraProcessProvider.bindToLifecycle()
. Siempre que sea posible, especifica todos los casos de uso necesarios para ejecutar simultáneamente en una sola sesión, en una sola llamada al elemento bindToLifecycle()
. CameraX determina las resoluciones, según el conjunto de casos de uso vinculados, teniendo cuenta el nivel de hardware compatible con el dispositivo y considerando la variación específica del dispositivo (en la que un dispositivo supera o no alcanza las configuraciones de transmisión disponibles).
El intent permite que la aplicación se ejecute en una amplia variedad de dispositivos, al mismo tiempo que se minimizan las instrucciones de código específicas para cada dispositivo.
La relación de aspecto predeterminada para los casos de uso de análisis de imagen y captura de imagen es 4:3.
Los casos de uso tienen una relación de aspecto configurable para permitir que la aplicación especifique la relación de aspecto deseada según el diseño de la IU. El resultado de CameraX se produce para que la relación de aspecto coincida lo más posible con lo solicitado, según lo que permita el dispositivo. Si no hubiera una concordancia exacta en la resolución admitida, se seleccionará la que cumpla con la mayor cantidad de condiciones posible. Entonces, la aplicación indicará cómo aparece la cámara en la app, y CameraX determinará la mejor configuración de resolución de cámara para diferentes dispositivos.
Por ejemplo, una app puede realizar cualquiera de las siguientes acciones:
- Especificar una resolución objetivo de 4:3 o 16:9 para un caso de uso
- Especificar una resolución personalizada, en la que CameraX intenta encontrar la coincidencia más cercana
- Especificar una proporción de aspecto de recorte para
ImageCapture
CameraX elige las resoluciones internas de superficie de Camera2 automáticamente. En la siguiente tabla, se muestran las resoluciones:
Caso de uso | Resolución de superficie interna | Resultado de resolución de datos |
---|---|---|
Vista previa | Relación de aspecto: Es la resolución que mejor se ajusta al objetivo de la configuración. | Resolución de superficie interna. Se proporcionan los metadatos para permitir un recorte, una escala y una rotación de la vista para la relación de aspecto objetivo. |
Resolución predeterminada: Es la resolución de vista previa más alta o la resolución preferida por el dispositivo más alta que coincide con la relación de aspecto de la vista previa. | ||
Resolución máxima: Es el tamaño de la vista previa, que hace referencia a la mejor coincidencia de tamaño en relación con la resolución de pantalla del dispositivo o a 1080p (1920 x 1080), el que sea menor. | ||
Análisis de imágenes | Relación de aspecto: Es la resolución que mejor se ajusta al objetivo de la configuración. | Resolución de superficie interna. |
Resolución predeterminada: La configuración de resolución objetivo predeterminada es 640 x 480. Ajustar la resolución objetivo y la relación de aspecto correspondiente genera una mejor resolución compatible. | ||
Resolución máxima: Es la resolución máxima de salida del dispositivo de la cámara en formato YUV_420_888 que se recupera de StreamConfigurationMap.getOutputSizes() .
La resolución objetivo se establece en 640 x 480 de forma predeterminada, por lo que si deseas una resolución superior a esa, debes usar setTargetResolution() y setTargetAspectRatio() para obtener la resolución más cercana a las que son compatibles.
|
||
Captura de imágenes | Relación de aspecto: Es la relación de aspecto que mejor se ajusta a la configuración. | Resolución de superficie interna. |
Resolución predeterminada: Es la resolución más alta disponible o preferida por el dispositivo que coincide con la relación de aspecto de ImageCapture. | ||
Resolución máxima: La resolución máxima de salida del dispositivo de la cámara en formato JPEG. Usa StreamConfigurationMap.getOutputSizes() para recuperarla.
|
Cómo especificar una resolución
Puedes establecer resoluciones específicas cuando compilas casos de uso con el método setTargetResolution(Size resolution)
, como se demuestra en la siguiente muestra de código:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
No puedes establecer la relación de aspecto objetivo y la resolución objetivo en el mismo caso de uso, ya que se genera un elemento IllegalArgumentException
cuando se compila el objeto de configuración.
Expresa la resolución Size
en el fotograma de coordenadas después de rotar los tamaños compatibles, según la rotación objetivo. Por ejemplo, un dispositivo con orientación natural vertical en rotación objetivo natural que solicita una imagen vertical puede especificar 480 x 640 y el mismo dispositivo con rotación a 90 grados y orientación horizontal puede especificar 640 x 480.
La resolución objetivo intenta establecer un límite mínimo para la resolución de la imagen. La resolución de la imagen real es la resolución disponible más próxima al tamaño que no sea menor que la resolución objetivo, según lo determine la implementación de Camera.
Sin embargo, si no existe una resolución que sea igual o mayor que la resolución objetivo, se elige la resolución disponible más próxima a la resolución objetivo. Las resoluciones con la misma relación de aspecto del elemento Size
proporcionado tienen una prioridad más alta que las resoluciones de diferentes relaciones de aspecto.
CameraX aplica la mejor resolución que se adecue a las solicitudes. Si la necesidad principal es satisfacer la relación de aspecto, especifica solo el objeto setTargetAspectRatio
, y CameraX determinará una resolución específica adecuada, según el dispositivo.
Si la necesidad principal de la app es especificar una resolución para que el procesamiento de imágenes sea más eficiente (por ejemplo, una imagen pequeña o mediana, según la capacidad de procesamiento del dispositivo), usa el objeto setTargetResolution(Size resolution)
.
Si tu app requiere una resolución exacta, consulta la tabla en createCaptureSession()
para determinar qué resoluciones máximas admite cada nivel de hardware. Si necesitas verificar las resoluciones específicas compatibles con el dispositivo actual, consulta StreamConfigurationMap.getOutputSizes(int)
.
Si tu app se ejecuta en Android 10 o versiones posteriores, puedes usar el objeto isSessionConfigurationSupported()
para verificar un elemento SessionConfiguration
específico.
Cómo controlar la salida de la cámara
Además de permitirte configurar la salida de la cámara, según sea necesario, para cada caso de uso individual, CameraX también implementa las siguientes interfaces para admitir operaciones de cámara comunes a todos los casos de uso vinculados:
CameraControl
te permite configurar funciones comunes de la cámara.CameraInfo
te permite consultar los estados de esas funciones de cámara comunes.
Estas son las funciones de cámara compatibles con CameraControl:
- Zoom
- Linterna
- Enfoque y medición (presionar para enfocar)
- Compensación de exposición
Cómo obtener instancias de CameraControl y CameraInfo
Recupera instancias de CameraControl
y CameraInfo
con el objeto Camera
que devuelve ProcessCameraProvider.bindToLifecycle()
.
En el siguiente código, se muestra un ejemplo:
Kotlin
val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // For performing operations that affect all outputs. val cameraControl = camera.cameraControl // For querying information and states. val cameraInfo = camera.cameraInfo
Java
Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview) // For performing operations that affect all outputs. CameraControl cameraControl = camera.getCameraControl() // For querying information and states. CameraInfo cameraInfo = camera.getCameraInfo()
Por ejemplo, puedes enviar zoom y otras operaciones de CameraControl
después de llamar a bindToLifecycle()
. Después de detener o destruir la actividad utilizada para vincular la instancia de la cámara, CameraControl
ya no puede ejecutar operaciones y muestra un ListenableFuture
con errores.
Zoom
CameraControl ofrece dos métodos para cambiar el nivel de zoom:
setZoomRatio()
establece el zoom según la proporción.La proporción debe estar dentro del rango de
CameraInfo.getZoomState().getValue().getMinZoomRatio()
yCameraInfo.getZoomState().getValue().getMaxZoomRatio()
. De lo contrario, la función devuelve unListenableFuture
con errores.setLinearZoom()
establece el zoom actual con un valor de zoom lineal de 0 a 1.0.La ventaja del zoom lineal es que logra que el campo visual (FOV) se ajuste con los cambios de zoom. Por lo tanto, resulta ideal para usarlo con una vista de
Slider
.
CameraInfo.getZoomState()
muestra un LiveData del estado de zoom actual. El valor cambia cuando se inicializa la cámara o si se establece el nivel de zoom con setZoomRatio()
o setLinearZoom()
. Cuando se llama a cualquiera de los dos métodos, se establecen los valores que respaldan ZoomState.getZoomRatio()
y ZoomState.getLinearZoom()
.
Esto resulta útil si deseas mostrar el texto de la proporción del zoom junto con un control deslizante.
Solo observa el LiveData
de ZoomState
para actualizar ambos sin necesidad de hacer una conversión.
El ListenableFuture
que devuelven ambas APIs ofrece la opción de que las aplicaciones reciban notificaciones cuando se completa una solicitud recurrente con el valor de zoom especificado. Además, si estableces un nuevo valor de zoom mientras la operación anterior aún se está ejecutando, el ListenableFuture
de la operación de zoom anterior fallará de inmediato.
Linterna
CameraControl.enableTorch(boolean)
habilita o inhabilita la linterna.
CameraInfo.getTorchState()
se puede usar para consultar el estado actual de la linterna. Puedes verificar el valor que muestra CameraInfo.hasFlashUnit()
para determinar si hay una linterna disponible. Si no lo está, llamar a CameraControl.enableTorch(boolean)
hace que el objeto ListenableFuture
que se devuelve se complete de inmediato con un resultado con errores y establece el estado de la linterna en TorchState.OFF
.
Cuando la linterna está habilitada, permanece encendida durante la captura de fotos y videos, independientemente de la configuración de flashMode. El flashMode
en ImageCapture
solo funciona cuando la linterna está inhabilitada.
Enfoque y medición
CameraControl.startFocusAndMetering()
activa la medición de la exposición y el enfoque automático a través de la configuración de regiones de medición AF/AE/AWB, según la FocusMeteringAction especificada. Esto se suele usar para implementar la función "presionar para enfocar" en muchas aplicaciones de cámara.
MeteringPoint
Para comenzar, crea un MeteringPoint
con MeteringPointFactory.createPoint(float x, float y, float
size)
.
Un MeteringPoint
representa un punto único en la Surface
de la cámara. Se almacena en un formato normalizado de modo que se pueda convertir con facilidad en coordenadas de sensores para especificar regiones AF/AE/AWB.
El tamaño de MeteringPoint
varía de 0 a 1 y tiene un valor predeterminado de 0.15f. Cuando llamas a MeteringPointFactory.createPoint(float x, float y, float
size)
, CameraX crea una región rectangular centrada en (x, y)
para el size
proporcionado.
En el siguiente código, se muestra cómo crear un MeteringPoint
:
Kotlin
// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview. previewView.setOnTouchListener((view, motionEvent) -> { val meteringPoint = previewView.meteringPointFactory .createPoint(motionEvent.x, motionEvent.y) … } // Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for // preview. Please note that if the preview is scaled or cropped in the View, // it’s the application's responsibility to transform the coordinates properly // so that the width and height of this factory represents the full Preview FOV. // And the (x,y) passed to create MeteringPoint might need to be adjusted with // the offsets. val meteringPointFactory = DisplayOrientedMeteringPointFactory( surfaceView.display, camera.cameraInfo, surfaceView.width, surfaceView.height ) // Use SurfaceOrientedMeteringPointFactory if the point is specified in // ImageAnalysis ImageProxy. val meteringPointFactory = SurfaceOrientedMeteringPointFactory( imageWidth, imageHeight, imageAnalysis)
startFocusAndMetering y FocusMeteringAction
Para invocar a startFocusAndMetering()
, las aplicaciones deben compilar una FocusMeteringAction
, que consiste en uno o más MeteringPoints
con combinaciones opcionales del modo de medición de FLAG_AF
, FLAG_AE
, FLAG_AWB
. En el siguiente código, se demuestra este uso:
Kotlin
val meteringPoint1 = meteringPointFactory.createPoint(x1, x1) val meteringPoint2 = meteringPointFactory.createPoint(x2, y2) val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB // Optionally add meteringPoint2 for AF/AE. .addPoint(meteringPoint2, FLAG_AF | FLAG_AE) // The action is canceled in 3 seconds (if not set, default is 5s). .setAutoCancelDuration(3, TimeUnit.SECONDS) .build() val result = cameraControl.startFocusAndMetering(action) // Adds listener to the ListenableFuture if you need to know the focusMetering result. result.addListener({ // result.get().isFocusSuccessful returns if the auto focus is successful or not. }, ContextCompat.getMainExecutor(this)
Como se muestra en el código anterior, startFocusAndMetering()
toma un objeto FocusMeteringAction
que consta de un elemento MeteringPoint
para las regiones de medición AF/AE/AWB, y otro MeteringPoint solo está disponible para AF y AE.
Internamente, CameraX lo convierte en MeteringRectangles
de Camera2 y establece los parámetros CONTROL_AF_REGIONS
/CONTROL_AE_REGIONS
/CONTROL_AWB_REGIONS
en la solicitud de captura.
Como no todos los dispositivos son compatibles con AF/AE/AWB y varias regiones, CameraX ejecuta FocusMeteringAction
de la mejor manera posible. CameraX usa la cantidad máxima compatible de MeteringPoints, en el orden en que se agregaron los puntos. Se ignorarán todos los MeteringPoints agregados después de alcanzada la cantidad máxima. Por ejemplo, si se proporciona un objeto FocusMeteringAction
con 3 MeteringPoints en una plataforma que admite solo 2, solo se usarán los primeros 2 MeteringPoints. CameraX ignorará el último MeteringPoint
.
Compensación de exposición
La compensación de exposición resulta útil cuando las aplicaciones necesitan ajustar los valores de exposición (VE) más allá del resultado de la exposición automática (EA). Los valores de compensación de exposición se combinan de la siguiente manera para determinar la exposición necesaria en las condiciones actuales de la imagen:
Exposure = ExposureCompensationIndex * ExposureCompensationStep
CameraX proporciona la función Camera.CameraControl.setExposureCompensationIndex()
para establecer la compensación de exposición como un valor de índice.
Los valores positivos de índice permiten que la imagen se vea más brillante, mientras que los valores negativos la atenúan. Las aplicaciones pueden consultar el rango admitido por CameraInfo.ExposureState.exposureCompensationRange()
descrito en la siguiente sección. Si se admite el valor, el ListenableFuture
que se devuelve se completa cuando el valor se habilita correctamente en la solicitud de captura. Si el índice especificado está fuera del rango admitido, setExposureCompensationIndex()
hace que el ListenableFuture
que se devuelve se complete de inmediato con un resultado con errores.
CameraX solo conserva la solicitud pendiente de setExposureCompensationIndex()
más reciente, y llamar a la función varias veces antes de que se ejecute la solicitud anterior provoca su cancelación.
En el siguiente fragmento, se establece un índice de compensación de exposición y se registra una devolución de llamada para cuando se haya ejecutado la solicitud de cambio de exposición:
Kotlin
camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex) .addListener({ // Get the current exposure compensation index, it might be // different from the asked value in case this request was // canceled by a newer setting request. val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex … }, mainExecutor)
Camera.CameraInfo.getExposureState()
recupera elExposureState
actual, incluida la siguiente información:- La compatibilidad con el control de compensación de exposición
- El índice actual de compensación de exposición
- El rango del índice de compensación de exposición
- El paso de compensación de exposición que se usó para calcular el valor de esa compensación
Por ejemplo, el siguiente código inicializa los parámetros de configuración de una SeekBar
de exposición con los valores actuales del ExposureState
:
Kotlin
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
Recursos adicionales
Para obtener más información acerca de CameraX, consulta los siguientes recursos adicionales.
Codelab
Muestra de código
Comunidad de desarrolladores
Grupo de discusión sobre Android CameraX