Le cas d'utilisation de l'analyse des images fournit à votre application une image accessible par processeur permettant d'effectuer le traitement d'images, la vision par ordinateur ou l'inférence de machine learning. L'application implémente une méthode analyze()
qui est exécutée sur chaque frame.
Pour savoir comment intégrer le ML Kit de Google à votre application CameraX, consultez la page Analyseur ML Kit.
Modes de fonctionnement
Lorsque le pipeline d'analyse de l'application ne peut pas répondre aux exigences de fréquence de frames de CameraX, il est possible de configurer CameraX pour abandonner des frames de l'une des manières suivantes :
Mode non bloquant (par défaut) : dans ce mode, l'exécuteur met toujours en cache la dernière image reçue dans un tampon d'image (semblable à une file d'attente d'une profondeur égale à 1) pendant que l'application analyse l'image précédente. Si CameraX reçoit une nouvelle image alors que le traitement en cours dans l'application n'est pas terminé, cette nouvelle image est enregistrée dans le même tampon et remplace l'image précédente. Notez que
ImageAnalysis.Builder.setImageQueueDepth()
n'a aucun effet dans ce scénario et que le contenu du tampon est toujours remplacé. Vous pouvez activer ce mode non bloquant en appelantsetBackpressureStrategy()
avecSTRATEGY_KEEP_ONLY_LATEST
. Pour en savoir plus sur les conséquences liées à l'utilisation de l'exécuteur, consultez la documentation de référence surSTRATEGY_KEEP_ONLY_LATEST
.Mode bloquant : dans ce mode, l'exécuteur interne peut ajouter plusieurs images à la file d'attente d'images interne, et ne commence à abandonner des frames que lorsque la file d'attente est pleine. Le blocage s'applique à l'ensemble du champ d'application de l'appareil photo : si l'appareil photo est associé à plusieurs cas d'utilisation, ceux-ci seront tous bloqués pendant que CameraX traite ces images. Par exemple, si l'aperçu et l'analyse d'image sont tous deux associés à un appareil photo, l'aperçu est également bloqué pendant que CameraX traite les images. Vous pouvez activer le mode bloquant en transmettant
STRATEGY_BLOCK_PRODUCER
àsetBackpressureStrategy()
. Vous pouvez également configurer la profondeur de la file d'attente d'images à l'aide de ImageAnalysis.Builder.setImageQueueDepth().
Si vous utilisez un analyseur hautes performances à faible latence avec lequel la durée totale d'analyse d'une image est inférieure à celle d'un frame CameraX (16 ms pour 60 fps, par exemple), les deux modes de fonctionnement offrent une expérience globale fluide. Le mode bloquant peut s'avérer utile dans certains cas, par exemple si le système fait l'objet de gigues très brèves.
Avec un analyseur hautes performances à latence élevée, un mode bloquant avec une file d'attente plus longue est nécessaire pour compenser la latence. Notez toutefois que l'application pourra quand même traiter tous les frames.
Avec un analyseur chronophage à latence élevée (qui ne peut pas traiter tous les frames), il peut s'avérer plus judicieux d'opter pour un mode non bloquant. En effet, les frames doivent être abandonnés dans le chemin d'analyse, mais les autres cas d'utilisation simultanés associés pourront quand même voir tous les frames.
Implémentation
Pour utiliser l'analyse d'image dans votre application :
- Créez un cas d'utilisation
ImageAnalysis
. - Créez un
ImageAnalysis.Analyzer
. - Définissez votre analyseur sur votre
ImageAnalysis
. - Associez le propriétaire du cycle de vie, le sélecteur d'appareil photo et le cas d'utilisation
ImageAnalysis
au cycle de vie.
Dès que la liaison est établie, CameraX envoie des images à votre analyseur enregistré.
Une fois l'analyse terminée, appelez ImageAnalysis.clearAnalyzer()
ou dissociez le cas d'utilisation ImageAnalysis
pour arrêter l'analyse.
Créer un cas d'utilisation ImageAnalysis
ImageAnalysis
connecte votre analyseur (un consommateur d'images) à CameraX, qui génère des images.
Les applications peuvent utiliser ImageAnalysis.Builder
pour créer un objet ImageAnalysis
. ImageAnalysis.Builder
permet à l'application de configurer les éléments suivants :
- Paramètres de sortie des images :
- Format : CameraX prend en charge
YUV_420_888
etRGBA_8888
viasetOutputImageFormat(int)
. Le format par défaut estYUV_420_888
. - Résolution et AspectRatio : vous pouvez définir un des deux paramètres, mais notez que vous ne pouvez pas définir les deux valeurs en même temps.
- Rotation.
- Nom cible : utilisez ce paramètre à des fins de débogage.
- Format : CameraX prend en charge
- Contrôles de flux d'images :
Les applications peuvent définir la résolution ou le format, mais pas les deux. La résolution de sortie exacte dépend de la taille (ou du format) demandée par l'application et des capacités matérielles. Elle peut différer de la taille ou du ratio demandé. Pour en savoir plus sur l'algorithme de mise en correspondance des résolutions, consultez la documentation sur setTargetResolution()
.
Une application peut configurer les pixels de l'image de sortie dans des espaces de couleurs YUV (par défaut) ou RVBA. Si le format de sortie choisi est RVBA, CameraX convertit les images en interne pour les faire passer de l'espace de couleurs YUV à l'espace de couleurs RVBA. Il regroupe aussi les bits des images dans le ByteBuffer
du premier plan d'ImageProxy (les deux autres plans ne sont pas utilisés) avec la séquence suivante :
ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...
Si vous effectuez une analyse complexe des images et que l'appareil ne peut pas suivre la fréquence de frames, vous pouvez configurer CameraX pour qu'il abandonne les frames en utilisant les stratégies décrites dans la section Modes de fonctionnement de cet article.
Créer votre analyseur
Les applications peuvent créer des analyseurs en implémentant le
ImageAnalysis.Analyzer
et le remplacement
analyze(ImageProxy image)
Dans chaque analyseur, les applications reçoivent un ImageProxy
, qui est un wrapper pour Media.Image.
Le format d'image peut être interrogé avec ImageProxy.getFormat()
.
Le format est l'une des valeurs suivantes fournies par l'application avec l'ImageAnalysis.Builder
:
ImageFormat.RGBA_8888
si l'application a demandéOUTPUT_IMAGE_FORMAT_RGBA_8888
.ImageFormat.YUV_420_888
si l'application a demandéOUTPUT_IMAGE_FORMAT_YUV_420_888
.
Consultez la section Créer un cas d'utilisation ImageAnalysis pour en savoir plus sur les configurations des espaces de couleurs et où récupérer les octets de pixels.
Dans un analyseur, l'application doit :
- Analyser un frame donné le plus rapidement possible, de préférence dans la limite de temps définie pour la fréquence de frames (par exemple, moins de 32 ms pour une fréquence de 30 fps). Si l'application ne peut pas analyser un frame assez rapidement, pensez à utiliser l'un des mécanismes d'abandon de frames compatibles.
- Appeler
ImageProxy.close()
afin de libérer l'ImageProxy
pour CameraX. Notez que vous ne devez pas appeler la fonction de fermeture de "Media.Image" (Media.Image.close()
) encapsulée.
Les applications peuvent utiliser la Media.Image
encapsulée directement dans ImageProxy.
N'appelez pas Media.Image.close()
sur l'image encapsulée, car cela endommagerait le mécanisme de partage d'image dans CameraX. Utilisez plutôt ImageProxy.close()
afin de libérer la Media.Image
sous-jacente pour CameraX.
Configurer votre analyseur pour ImageAnalysis
Une fois l'analyseur créé, utilisez ImageAnalysis.setAnalyzer()
pour l'enregistrer et commencer l'analyse. Une fois l'analyse terminée, supprimez l'analyseur enregistré à l'aide de ImageAnalysis.clearAnalyzer()
.
Vous ne pouvez configurer qu'un seul analyseur actif pour l'analyse d'images. L'appel de ImageAnalysis.setAnalyzer()
remplace l'analyseur enregistré s'il existe déjà. Les applications peuvent définir un nouvel analyseur à tout moment, avant ou après la liaison du cas d'utilisation.
Associer ImageAnalysis à un cycle de vie
Il est vivement recommandé d'associer votre ImageAnalysis
à un cycle de vie AndroidX existant à l'aide de la fonction ProcessCameraProvider.bindToLifecycle()
. Notez que la fonction bindToLifecycle()
renvoie l'appareil Camera
sélectionné, qui peut être utilisé pour affiner des paramètres avancés, notamment l'exposition. Pour en savoir plus sur le contrôle de la sortie de l'appareil photo, consultez ce guide.
L'exemple suivant combine tous les éléments des étapes précédentes, en associant les cas d'utilisation ImageAnalysis
et Preview
de CameraX à un propriétaire de lifeCycle
:
Kotlin
val imageAnalysis = ImageAnalysis.Builder() // enable the following line if RGBA output is needed. // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy -> val rotationDegrees = imageProxy.imageInfo.rotationDegrees // insert your code here. ... // after done, release the ImageProxy object imageProxy.close() }) cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() // enable the following line if RGBA output is needed. //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(new Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() { @Override public void analyze(@NonNull ImageProxy imageProxy) { int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees(); // insert your code here. ... // after done, release the ImageProxy object imageProxy.close(); } }); cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);
Ressources supplémentaires
Pour en savoir plus sur CameraX, consultez les ressources supplémentaires suivantes.
Atelier de programmation
Exemple de code