Vous configurez chaque cas d'utilisation de CameraX pour vérifier différents aspects des opérations de ces cas d'utilisation.
Par exemple, avec le cas d'utilisation "Capture d'image", vous pouvez définir un format cible et un mode Flash. Par exemple, le code suivant :
Kotlin
val imageCapture = ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build()
Java
ImageCapture imageCapture = new ImageCapture.Builder() .setFlashMode(...) .setTargetAspectRatio(...) .build();
En plus des options de configuration, certains cas d'utilisation exposent des API à des paramètres dynamiques après la création du cas d'utilisation. Pour en savoir plus sur la configuration spécifique à chaque cas d'utilisation, consultez Intégrer un aperçu, Analyser des images et Capture d'image.
CameraXConfig
Pour plus de facilité, CameraX dispose de configurations par défaut, telles que des exécuteurs et des gestionnaires internes, adaptés à la plupart des cas d'utilisation. Toutefois, si votre application présente des exigences particulières ou si vous préférez personnaliser ces configurations, CameraXConfig
est l'interface prévue à cet effet.
Avec CameraXConfig
, une application peut effectuer les opérations suivantes :
- Optimiser la latence de démarrage avec
setAvailableCameraLimiter()
- Fournir l'exécuteur d'application à CameraX avec
setCameraExecutor()
- Remplacer le gestionnaire de programmeur par défaut avec
setSchedulerHandler()
- Modifier le niveau de journalisation avec
setMinimumLoggingLevel()
Modèle d'utilisation
La procédure suivante décrit comment utiliser CameraXConfig
:
- Créez un objet
CameraXConfig
avec vos configurations personnalisées. - Intégrez l'interface
CameraXConfig.Provider
dans votreApplication
et renvoyez votre objetCameraXConfig
dansgetCameraXConfig()
. - Ajoutez la classe
Application
à votre fichierAndroidManifest.xml
, comme indiqué ici.
L'exemple de code suivant limite la journalisation de CameraX aux messages d'erreur uniquement :
Kotlin
class CameraApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setMinimumLoggingLevel(Log.ERROR).build() } }
Conservez une copie locale de l'objet CameraXConfig
si votre application a besoin de connaître la configuration de CameraX après l'avoir définie.
Limiteur de caméra
Lors du premier appel de ProcessCameraProvider.getInstance()
, CameraX énumère et interroge les caractéristiques des caméras disponibles sur l'appareil. CameraX doit communiquer avec les composants matériels. Ce processus peut donc prendre un certain temps pour chaque caméra, en particulier sur les appareils bas de gamme. Si votre application n'utilise que des caméras spécifiques sur l'appareil, comme la caméra avant par défaut, vous pouvez configurer CameraX de sorte qu'il ignore les autres caméras et réduire ainsi le temps de latence au démarrage des caméras utilisées par votre application.
Si le CameraSelector
soumis à CameraXConfig.Builder.setAvailableCamerasLimiter()
filtre une caméra, CameraX se comporte comme si cette caméra n'existait pas. Par exemple, le code suivant limite l'utilisation de la caméra arrière par défaut de l'appareil :
Kotlin
class MainApplication : Application(), CameraXConfig.Provider { override fun getCameraXConfig(): CameraXConfig { return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig()) .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA) .build() } }
Threads
De nombreuses API de la plate-forme sur lesquelles CameraX repose requièrent un dispositif de communication inter-processus (IPC) avec du matériel dont la réponse peut parfois prendre des centaines de millisecondes. Pour cette raison, CameraX n'appelle ces API qu'à partir de threads en arrière-plan, ce qui garantit que le thread principal n'est pas bloqué et que l'UI reste fluide. CameraX gère ces threads en interne, de sorte que ce comportement semble transparent. Cependant, certaines applications nécessitent un contrôle strict des threads. CameraXConfig
permet à une application de définir les threads d'arrière-plan utilisés via CameraXConfig.Builder.setCameraExecutor()
et CameraXConfig.Builder.setSchedulerHandler()
.
Exécuteur de la caméra
L'exécuteur de la caméra est utilisé pour tous les appels d'API internes de la plate-forme de caméra, ainsi que pour les rappels de ces API. CameraX alloue et gère un élément Executor
interne pour effectuer ces tâches.
Toutefois, si votre application nécessite un contrôle plus strict des threads, utilisez CameraXConfig.Builder.setCameraExecutor()
.
Gestionnaire de programmeur
Le gestionnaire de programmeur permet de planifier des tâches internes à intervalles fixes, par exemple de réessayer d'ouvrir la caméra lorsqu'elle n'est pas disponible. Ce gestionnaire n'exécute pas les tâches et les envoie uniquement à l'exécuteur de la caméra. Il est également utilisé sur les anciennes plates-formes d'API qui requièrent Handler
pour les rappels. Dans ces cas, les rappels sont toujours envoyés directement à l'exécuteur de la caméra. CameraX alloue et gère un élément HandlerThread
interne pour effectuer ces tâches, mais vous pouvez le remplacer par CameraXConfig.Builder.setSchedulerHandler()
.
Journalisation
La journalisation de CameraX permet aux applications de filtrer les messages Logcat, car il est recommandé d'éviter les messages détaillés dans le code de production. CameraX est compatible avec quatre niveaux de journalisation, du plus détaillé au plus succinct :
Log.DEBUG
(par défaut)Log.INFO
Log.WARN
Log.ERROR
Reportez-vous aux documents des journaux Android pour obtenir une description détaillée de ces niveaux de journalisation. Utilisez CameraXConfig.Builder.setMinimumLoggingLevel(int)
pour définir le niveau de journalisation approprié pour votre application.
Sélection automatique
CameraX fournit automatiquement une fonctionnalité spécifique à l'appareil sur lequel votre application s'exécute. Par exemple, CameraX détermine automatiquement la meilleure résolution à utiliser si vous ne précisez pas de résolution ou si la résolution spécifiée n'est pas compatible. Tout cela est géré par la bibliothèque, sans besoin d'écrire du code spécifique à l'appareil.
L'objectif de CameraX est d'initialiser correctement une session d'appareil photo. Cela signifie que CameraX fait des compromis au niveau de la résolution et du format selon la capacité de l'appareil. Ce compromis peut avoir lieu pour les raisons suivantes :
- L'appareil n'est pas compatible avec la résolution demandée.
- L'appareil présente des problèmes de compatibilité, par exemple d'anciens appareils dont le fonctionnement correct requiert certaines résolutions.
- Sur certains appareils, certains formats ne sont disponibles qu'avec certaines proportions.
- L'appareil privilégie le "mod16 le plus proche" pour l'encodage JPEG ou vidéo. Pour en savoir plus, consultez
SCALER_STREAM_CONFIGURATION_MAP
.
Bien que CameraX crée et gère la session, vérifiez toujours les tailles d'image renvoyées dans la sortie du cas d'utilisation de votre code et apportez les ajustements nécessaires.
Rotation
Par défaut, la rotation de la caméra est définie pour correspondre à la rotation de l'écran par défaut lors de la création du cas d'utilisation. Dans ce cas par défaut, CameraX génère des sorties pour permettre à l'application de correspondre à ce que vous attendez de l'aperçu. Vous pouvez remplacer la rotation par une valeur personnalisée compatible avec les appareils multi-écrans en transmettant l'orientation d'affichage actuelle lors de la configuration des objets de cas d'utilisation ou de manière dynamique après leur création.
Votre application peut définir la rotation des cibles à l'aide des paramètres de configuration. Il peut ensuite mettre à jour les paramètres de rotation en utilisant les méthodes des API de cas d'utilisation (comme ImageAnalysis.setTargetRotation()
), même lorsque le cycle de vie est en cours d'exécution. Vous pouvez utiliser cette option lorsque l'application est verrouillée en mode portrait (et donc qu'une rotation n'entraîne aucune reconfiguration), mais le cas d'utilisation photo ou analyse doit détecter la rotation actuelle de l'appareil. Par exemple, une détection de la rotation peut être nécessaire pour que les visages soient correctement orientés pour la détection de visages ou que les photos apparaissent en mode paysage ou portrait.
Les données des images capturées peuvent être stockées sans informations de rotation. Les données EXIF contiennent des informations de rotation permettant aux applications de galerie d'afficher l'image dans le bon sens après l'enregistrement.
Pour afficher les données d'aperçu avec la bonne orientation, vous pouvez utiliser la sortie de métadonnées de Preview.PreviewOutput()
pour effectuer des modifications.
L'exemple de code suivant montre comment définir la rotation sur un événement d'orientation :
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(); }
Selon la rotation définie, chaque cas d'utilisation effectue une rotation directe des données d'image ou fournit aux consommateurs des métadonnées de rotation des données d'image sans rotation.
- Aperçu : la sortie des métadonnées est fournie afin que la rotation de la résolution cible soit connue à l'aide de
Preview.getTargetRotation()
. - ImageAnalysis : la sortie de métadonnées est fournie afin que les coordonnées du tampon d'image soient connues par rapport aux coordonnées de l'écran.
- ImageCapture : les métadonnées ou la mémoire tampon de l'image EXIF ou les deux seront modifiées pour prendre en compte le paramètre de rotation. La valeur modifiée dépend de l'intégration de HAL.
Rectangle de recadrage
Par défaut, le rectangle de recadrage correspond à celui du tampon complet. Vous pouvez le personnaliser avec ViewPort
et UseCaseGroup
. En regroupant les cas d'utilisation et en définissant la fenêtre d'affichage, CameraX garantit que les rectangles de recadrage de tous les cas d'utilisation du groupe pointent vers la même zone du capteur de la caméra.
L'extrait de code suivant montre comment utiliser ces deux classes :
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
définit le rectangle de tampon visible par les utilisateurs finaux. CameraX calcule ensuite le rectangle de recadrage le plus grand possible en fonction des propriétés de la fenêtre d'affichage et des cas d'utilisation associés. En règle générale, pour obtenir un effet WYSIWYG, vous pouvez configurer la fenêtre d'affichage en fonction du cas d'utilisation de l'aperçu. Un moyen simple d'obtenir la fenêtre d'affichage consiste à utiliser PreviewView
.
Les extraits de code suivants montrent comment obtenir l'objet ViewPort
:
Kotlin
val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort
Java
ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();
Dans l'exemple précédent, ce que l'application obtient de l'élément ImageAnalysis
et de l'élément ImageCapture
correspond à ce que l'utilisateur final voit dans PreviewView
, en supposant que le type d'échelle de PreviewView
est défini sur la valeur par défaut, FILL_CENTER
. Après avoir appliqué le recadrage et la rotation au tampon de sortie, l'image de tous les cas d'utilisation sera la même, mais peut-être avec des résolutions différentes. Pour en savoir plus sur la mise en œuvre des informations de transformation, consultez Sortie de la transformation.
Choix de la caméra
CameraX sélectionne automatiquement la meilleure caméra en fonction des exigences de votre application et des cas d'utilisation. Si vous souhaitez utiliser un appareil différent de celui sélectionné, plusieurs options s'offrent à vous :
- Définir la caméra frontale par défaut avec
CameraSelector.DEFAULT_FRONT_CAMERA
- Définir la caméra arrière par défaut avec
CameraSelector.DEFAULT_BACK_CAMERA
- Filtrer la liste des appareils disponibles en fonction de leur
CameraCharacteristics
avecCameraSelector.Builder.addCameraFilter()
L'exemple de code suivant montre comment créer un CameraSelector
pour influencer la sélection des appareils :
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)
Sélectionner plusieurs caméras simultanément
À partir de la version 1.3 de CameraX, vous pouvez également sélectionner plusieurs caméras simultanément. Par exemple, vous pouvez lier une caméra frontale et arrière pour prendre des photos ou enregistrer des vidéos simultanément depuis les deux perspectives.
Avec la fonctionnalité Caméra simultanée, l'appareil peut utiliser en même temps deux caméras avec des objectifs orientés dans des directions distinctes ou deux caméras arrière. Le bloc de code suivant montre comment définir deux caméras lors de l'appel de bindToLifecycle
et comment obtenir les deux objets Camera à partir de l'objet ConcurrentCamera
renvoyé.
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);
Résolution de la caméra
Vous pouvez laisser CameraX définir la résolution de l'image selon les fonctionnalités de l'appareil, du niveau matériel compatible, du cas d'utilisation et du format fourni. Vous pouvez également définir une résolution cible ou un format spécifique dans les cas d'utilisation compatibles avec cette configuration.
Résolution automatique
CameraX peut déterminer automatiquement les meilleurs paramètres de résolution en fonction des cas d'utilisation spécifiés dans cameraProcessProvider.bindToLifecycle()
. Dans la mesure du possible, indiquez tous les cas d'utilisation nécessaires pour une exécution simultanée dans une même session dans un seul appel bindToLifecycle()
. CameraX détermine les résolutions en fonction de l'ensemble des cas d'utilisation liés en tenant compte du niveau de matériel compatible de l'appareil et de la variance spécifique (qui permet à un appareil de dépasser ou de ne pas atteindre les configurations de flux disponibles).
L'objectif est de permettre à l'application de s'exécuter sur une grande variété d'appareils tout en minimisant les chemins de code propres à ceux-ci.
Le format par défaut des cas d'utilisation "Capture d'image" et "Analyse des images" est de 4:3.
Les cas d'utilisation présentent un format configurable permettant à l'application de préciser les proportions souhaitées en fonction de la mise en page de l'interface utilisateur. La sortie de CameraX est générée pour correspondre aux proportions demandées par l'appareil. Si aucune correspondance exacte de résolution n'est acceptée, celle qui remplit le plus de conditions est sélectionnée. Ainsi, l'application détermine la façon dont la caméra apparaît dans l'application, tandis que CameraX sélectionne les meilleurs paramètres de résolution de la caméra pour répondre à ce besoin sur différents appareils.
Par exemple, une application peut effectuer les opérations suivantes :
- Définir une résolution cible de 4:3 ou 16:9 pour un cas d'utilisation
- Spécifier une résolution personnalisée, que CameraX utilisera comme référence pour tenter de trouver la correspondance la plus proche
- Indiquer un format de recadrage pour
ImageCapture
CameraX sélectionne automatiquement les résolutions de surface interne de Camera2. Le tableau suivant présente les résolutions :
Cas d'utilisation | Résolution de surface interne | Résolution des données de sortie |
---|---|---|
Aperçu | Format : résolution la mieux adaptée au paramètre de la cible. | Résolution de surface interne. Des métadonnées sont fournies pour permettre un recadrage, une mise à l'échelle et une rotation du format cible. |
Résolution par défaut : résolution d'aperçu la plus élevée ou résolution privilégiée par l'appareil correspondant au format de l'aperçu. | ||
Résolution maximale : taille d'aperçu, qui correspond à la meilleure taille correspondant à la résolution d'écran de l'appareil ou à 1 080p (1 920 x 1 080 pixels), selon la taille la plus petite. | ||
Analyse d'image | Format : résolution la mieux adaptée au paramètre de la cible. | Résolution de surface interne. |
Résolution par défaut : le paramètre de résolution cible par défaut est 640 x 480. Si vous ajustez à la fois la résolution cible et le format correspondant, vous obtiendrez la meilleure résolution possible. | ||
Résolution maximale : résolution de sortie maximale de l'appareil photo au format YUV_420_888, obtenue à partir de StreamConfigurationMap.getOutputSizes() .
La résolution cible est définie par défaut sur 640 x 480. Si vous souhaitez une résolution supérieure à 640 x 480, vous devez donc utiliser setTargetResolution() et setTargetAspectRatio() pour obtenir la résolution la plus proche.
|
||
Capture d'image | Proportions : format le mieux adapté au paramètre. | Résolution de surface interne. |
Résolution par défaut : résolution la plus élevée disponible ou résolution la plus adaptée pour l'appareil correspondant au format d'ImageCapture. | ||
Résolution maximale : résolution de sortie maximale de la caméra au format JPEG. Utilisez StreamConfigurationMap.getOutputSizes() pour obtenir cette information.
|
Indiquer une résolution
Vous pouvez indiquer des résolutions spécifiques lorsque vous créez des cas d'utilisation à l'aide de la méthode setTargetResolution(Size resolution)
, comme l'illustre l'exemple de code suivant :
Kotlin
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .build()
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .build();
Vous ne pouvez pas définir à la fois un format et une résolution cibles pour le même cas d'utilisation. Cela entraînerait une erreur IllegalArgumentException
lors de la création de l'objet de configuration.
Indiquez l'élément Size
de la résolution dans le cadre des coordonnées après avoir effectué la rotation des tailles compatibles vers la rotation cible. Par exemple, un appareil en orientation naturelle portrait dans une rotation cible naturelle demandant une image au format portrait peut être en 480 x 640 , après une rotation à 90 degrés et une orientation cible paysage, le même appareil peut être en 640 x 480.
La résolution cible tente de fixer une limite minimale pour la résolution de l'image. La résolution d'image réelle est la taille disponible la plus proche qui n'est pas inférieure à la résolution cible, comme déterminé par l'implémentation de la caméra.
Toutefois, si aucune résolution n'est supérieure ou égale à la résolution cible, la résolution disponible inférieure à la résolution cible la plus proche sera sélectionnée. Les résolutions avec le même format de Size
fournie ont une priorité plus élevée que les résolutions de différents formats.
CameraX applique la résolution qui convient le mieux en fonction des requêtes. Si le besoin principal est de correspondre au format, indiquez uniquement setTargetAspectRatio
. CameraX déterminera une résolution spécifique adaptée à l'appareil.
Si le besoin principal de l'application est d'indiquer une résolution afin d'améliorer l'efficacité du traitement d'image (par exemple, une image de petite ou moyenne taille basée sur la capacité de traitement de l'appareil), utilisez setTargetResolution(Size resolution)
.
Si votre application requiert une résolution exacte, consultez le tableau dans createCaptureSession()
pour connaître les résolutions maximales compatibles avec chaque niveau de matériel. Pour vérifier les résolutions spécifiques compatibles avec l'appareil actuel, consultez StreamConfigurationMap.getOutputSizes(int)
.
Si votre application s'exécute sous Android version 10 ou ultérieure, vous pouvez utiliser isSessionConfigurationSupported()
pour valider une SessionConfiguration
spécifique.
Contrôler la sortie de la caméra
En plus de vous permettre de configurer la sortie de la caméra selon les besoins pour chaque cas d'utilisation, CameraX intègre les interfaces suivantes pour assurer les opérations de la caméra communes à tous les cas d'utilisation liés :
CameraControl
vous permet de configurer les fonctionnalités courantes de la caméra.CameraInfo
vous permet d'interroger les états de ces fonctionnalités courantes de la caméra.
Voici les fonctionnalités de la caméra compatibles avec CameraControl :
- Zoom
- Lampe de poche
- Mise au point et mesures (appuyer pour effectuer la mise au point)
- Correction d'exposition
Obtenir des instances de CameraControl et CameraInfo
Récupérez les instances de CameraControl
et CameraInfo
à l'aide de l'objet Camera
renvoyé par ProcessCameraProvider.bindToLifecycle()
.
Le code suivant sert d'exemple :
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()
Par exemple, vous pouvez effectuer un zoom et d'autres opérations de CameraControl
après avoir appelé bindToLifecycle()
. Une fois que vous avez arrêté ou détruit l'activité utilisée pour lier l'instance de caméra, CameraControl
ne peut plus exécuter d'opérations et renvoie une erreur ListenableFuture
.
Zoom
CameraControl propose deux méthodes pour modifier le zoom :
setZoomRatio()
définit le zoom en fonction du ratio de zoom.Ce ratio doit être compris entre
CameraInfo.getZoomState().getValue().getMinZoomRatio()
etCameraInfo.getZoomState().getValue().getMaxZoomRatio()
. Sinon, la fonction renvoie une erreurListenableFuture
.setLinearZoom()
définit le zoom actuel avec une valeur de zoom linéaire comprise entre 0 et 1.L'avantage du zoom linéaire est qu'il fait évoluer le champ de vision en fonction des changements de zoom. C'est donc idéal pour les vues
Slider
.
CameraInfo.getZoomState()
renvoie un LiveData de l'état actuel du zoom. La valeur change lorsque la caméra est initialisée ou si le niveau de zoom est défini à l'aide de setZoomRatio()
ou setLinearZoom()
. L'appel de l'une ou l'autre méthode définit les valeurs de sauvegarde de ZoomState.getZoomRatio()
et de ZoomState.getLinearZoom()
.
Cela s'avère utile si vous souhaitez afficher un texte avec un ratio de zoom à côté d'un curseur.
Il vous suffit d'observer la LiveData
de ZoomState
pour mettre à jour les deux sans avoir à effectuer de conversion.
ListenableFuture
renvoyé par les deux API permet aux applications d'être notifiées lorsqu'une demande récurrente avec la valeur de zoom spécifiée est terminée. En outre, si vous définissez une nouvelle valeur de zoom pendant que l'opération précédente est toujours en cours d'exécution, le ListenableFuture
de l'opération de zoom précédente échoue immédiatement.
Lampe de poche
CameraControl.enableTorch(boolean)
active ou désactive la lampe de poche.
CameraInfo.getTorchState()
permet d'interroger l'état actuel de la lampe de poche. Vous pouvez vérifier la valeur renvoyée par CameraInfo.hasFlashUnit()
pour déterminer si une lampe de poche est disponible. Si ce n'est pas le cas, l'appel de CameraControl.enableTorch(boolean)
entraîne la fin immédiate du ListenableFuture
renvoyé avec un résultat d'échec et définit l'état de la torche sur TorchState.OFF
.
Lorsque la lampe de poche est activée, elle reste allumée pendant la capture photo et vidéo, quel que soit le paramètre flashMode. L'élément flashMode
dans ImageCapture
ne fonctionne que lorsque la lampe de poche est désactivée.
Mise au point et mesures
CameraControl.startFocusAndMetering()
déclenche la mise au point automatique et la mesure de l'exposition en définissant les régions de mesure AF/AE/AWB (Automatic Focus / Automatic Exposure / Automatic White-balance) en fonction de l'action FocusMeteringAction donnée. La fonction est souvent utilisée pour intégrer la fonctionnalité "Appuyer pour effectuer la mise au point" dans de nombreuses applications de caméra.
MeteringPoint
Commencez par créer un élément MeteringPoint
à l'aide de MeteringPointFactory.createPoint(float x, float y, float
size)
.
Un élément MeteringPoint
représente un point unique de la caméra Surface
. Il est stocké dans un format normalisé afin de pouvoir être facilement converti en coordonnées de capteurs destinées à déterminer les régions de AF/AE/AWB.
La taille de MeteringPoint
varie de 0 à 1, avec une taille par défaut de 0,15 f. Lors de l'appel de MeteringPointFactory.createPoint(float x, float y, float
size)
, CameraX crée une zone rectangulaire centrée sur (x, y)
pour l'élément size
fourni.
Le code suivant montre comment créer 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 et FocusMeteringAction
Pour appeler startFocusAndMetering()
, les applications doivent créer une FocusMeteringAction
, qui se compose d'un ou plusieurs MeteringPoints
avec des combinaisons de mode de mesure facultatives de FLAG_AF
, FLAG_AE
et FLAG_AWB
. Le code suivant illustre cette utilisation :
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)
Comme indiqué dans le code précédent, startFocusAndMetering()
utilise un FocusMeteringAction
composé d'un MeteringPoint
pour les régions de mesure AF/AE/AWB et d'un autre MeteringPoint pour AF et AE uniquement.
En interne, CameraX la convertit en MeteringRectangles
de Camera2 et définit les paramètres CONTROL_AF_REGIONS
/ CONTROL_AE_REGIONS
/ CONTROL_AWB_REGIONS
correspondant à la demande de capture.
Tous les appareils n'étant pas compatibles avec AF/AE/AWB et plusieurs régions, CameraX exécute la FocusMeteringAction
au mieux. CameraX utilise le maximum de MeteringPoints acceptés, dans l'ordre dans lequel ils ont été ajoutés. Tous les MeteringPoints ajoutés après le nombre maximal sont ignorés. Par exemple, si une FocusMeteringAction
est fournie avec trois MeterPoints sur une plate-forme compatible avec seulement deux, seuls les deux premiers sont utilisés. Le dernier MeteringPoint
est ignoré par CameraX.
Correction d'exposition
La correction d'exposition est utile lorsque les applications doivent ajuster les valeurs d'exposition au-delà du résultat de sortie d'exposition automatique. Les valeurs de correction d'exposition sont combinées de la manière suivante pour déterminer l'exposition nécessaire pour les conditions d'image actuelles :
Exposure = ExposureCompensationIndex * ExposureCompensationStep
CameraX fournit la fonction Camera.CameraControl.setExposureCompensationIndex()
permettant de définir la correction d'exposition en tant que valeur d'indice.
Les valeurs d'indice positives augmentent la luminosité de l'image, tandis que les valeurs négatives la réduisent. Les applications peuvent interroger la plage acceptée par CameraInfo.ExposureState.exposureCompensationRange()
décrite dans la section suivante. Si la valeur est acceptée, la valeur ListenableFuture
renvoyée se termine lorsque la valeur est correctement activée dans la demande de capture. Si l'indice spécifié se trouve en dehors de la plage acceptée, setExposureCompensationIndex()
entraîne la fin immédiate de l'élément ListenableFuture
renvoyé, avec un échec du résultat.
CameraX ne conserve que la dernière demande setExposureCompensationIndex()
en attente. Si vous appelez la fonction plusieurs fois avant que la demande précédente ne soit exécutée, elle est annulée.
L'extrait de code suivant définit un index de correction d'exposition et enregistre un rappel lorsque la demande de modification de l'exposition est exécutée :
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()
récupère la valeurExposureState
actuelle, y compris :- La compatibilité du contrôle de la correction d'exposition
- L'indice de correction d'exposition actuel
- La plage d'indices de correction d'exposition
- L'étape de correction d'exposition utilisée dans le calcul de la valeur de correction d'exposition
Par exemple, le code suivant initialise les paramètres d'une exposition SeekBar
avec les valeurs ExposureState
actuelles :
Kotlin
val exposureState = camera.cameraInfo.exposureState binding.seekBar.apply { isEnabled = exposureState.isExposureCompensationSupported max = exposureState.exposureCompensationRange.upper min = exposureState.exposureCompensationRange.lower progress = exposureState.exposureCompensationIndex }
Ressources supplémentaires
Pour en savoir plus sur CameraX, consultez les ressources supplémentaires suivantes.
Atelier de programmation
Exemple de code
Communauté de développeurs
Groupe de discussion Android CameraX