Utilisez ML Kit pour ajouter facilement des fonctionnalités de segmentation des sujets à votre application.
Fonctionnalité | Détails |
---|---|
Nom du SDK | play-services-mlkit-subject-segmentation |
Implémentation | Non groupé: le modèle est téléchargé de manière dynamique à l'aide des services Google Play. |
Impact sur la taille de l'application | Augmentation de la taille d'environ 200 Ko. |
Délai d'initialisation | Les utilisateurs devront peut-être attendre que le modèle soit téléchargé avant de l'utiliser pour la première fois. |
Essayer
- Testez l'application exemple pour voir un exemple d'utilisation de cette API.
Avant de commencer
- Dans le fichier
build.gradle
de niveau projet, veillez à inclure le dépôt Maven de Google à la fois dans les sectionsbuildscript
etallprojects
. - Ajoutez la dépendance de la bibliothèque de segmentation des sujets ML Kit au fichier Gradle au niveau de l'application de votre module, qui est généralement
app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1'
}
Comme indiqué ci-dessus, le modèle est fourni par les services Google Play.
Vous pouvez configurer votre application pour qu'elle télécharge automatiquement le modèle sur l'appareil une fois qu'elle est installée depuis le Play Store. Pour ce faire, ajoutez la déclaration suivante au fichier AndroidManifest.xml
de votre application:
<application ...>
...
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="subject_segment" >
<!-- To use multiple models: android:value="subject_segment,model2,model3" -->
</application>
Vous pouvez également vérifier explicitement la disponibilité du modèle et demander le téléchargement via les services Google Play avec l'API ModuleInstallClient.
Si vous n'activez pas les téléchargements de modèles au moment de l'installation ou si vous demandez un téléchargement explicite, le modèle est téléchargé la première fois que vous exécutez le segmenteur. Les requêtes que vous effectuez avant la fin du téléchargement ne produisent aucun résultat.
1. Préparer l'image d'entrée
Pour effectuer une segmentation sur une image, créez un objet InputImage
à partir d'un Bitmap
, media.Image
, ByteBuffer
, d'un tableau d'octets ou d'un fichier sur l'appareil.
Vous pouvez créer un objet InputImage
à partir de différentes sources, chacune étant expliquée ci-dessous.
Utiliser un media.Image
Pour créer un objet InputImage
à partir d'un objet media.Image
, par exemple lorsque vous capturez une image à partir de l'appareil photo d'un appareil, transmettez l'objet media.Image
et la rotation de l'image à InputImage.fromMediaImage()
.
Si vous utilisez la bibliothèque
CameraX, les classes OnImageCapturedListener
et ImageAnalysis.Analyzer
calculent la valeur de rotation à votre place.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Si vous n'utilisez pas de bibliothèque d'appareil photo qui vous indique le degré de rotation de l'image, vous pouvez le calculer à partir du degré de rotation de l'appareil et de l'orientation du capteur de l'appareil photo dans l'appareil:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Transmettez ensuite l'objet media.Image
et la valeur de degré de rotation à InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Utiliser un URI de fichier
Pour créer un objet InputImage
à partir d'un URI de fichier, transmettez le contexte de l'application et l'URI de fichier à InputImage.fromFilePath()
. Cela est utile lorsque vous utilisez un intent ACTION_GET_CONTENT
pour inviter l'utilisateur à sélectionner une image dans son application Galerie.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Utiliser un ByteBuffer
ou un ByteArray
Pour créer un objet InputImage
à partir d'un ByteBuffer
ou d'un ByteArray
, commencez par calculer le degré de rotation de l'image comme décrit précédemment pour l'entrée media.Image
.
Créez ensuite l'objet InputImage
avec le tampon ou le tableau, ainsi que la hauteur, la largeur, le format d'encodage des couleurs et le degré de rotation de l'image:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Utiliser un Bitmap
Pour créer un objet InputImage
à partir d'un objet Bitmap
, effectuez la déclaration suivante:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
L'image est représentée par un objet Bitmap
avec les degrés de rotation.
2. Créer une instance de SubjectSegmenter
Définir les options de segmentation
Pour segmenter votre image, commencez par créer une instance de SubjectSegmenterOptions
comme suit:
Kotlin
val options = SubjectSegmenterOptions.Builder() // enable options .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() // enable options .build();
Voici le détail de chaque option:
Masque de confiance de premier plan
Le masque de confiance au premier plan vous permet de distinguer le sujet au premier plan de l'arrière-plan.
Appeler enableForegroundConfidenceMask()
dans les options vous permet de récupérer ultérieurement le masque au premier plan en appelant getForegroundMask()
sur l'objet SubjectSegmentationResult
renvoyé après le traitement de l'image.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build();
Bitmap de premier plan
De même, vous pouvez également obtenir un bitmap de l'objet au premier plan.
Appeler enableForegroundBitmap()
dans les options vous permet de récupérer ultérieurement le bitmap de premier plan en appelant getForegroundBitmap()
sur l'objet SubjectSegmentationResult
renvoyé après le traitement de l'image.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build();
Masque de confiance multi-sujet
Comme pour les options de premier plan, vous pouvez utiliser SubjectResultOptions
pour activer le masque de confiance pour chaque sujet de premier plan comme suit:
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Bitmap multi-sujet
De même, vous pouvez activer le bitmap pour chaque sujet:
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Créer le segmenteur de sujet
Une fois que vous avez spécifié les options SubjectSegmenterOptions
, créez une instance SubjectSegmenter
appelant getClient()
et transmettant les options en tant que paramètre:
Kotlin
val segmenter = SubjectSegmentation.getClient(options)
Java
SubjectSegmenter segmenter = SubjectSegmentation.getClient(options);
3. Traiter une image
Transmettez l'objet InputImage
préparé à la méthode process
de SubjectSegmenter
:
Kotlin
segmenter.process(inputImage) .addOnSuccessListener { result -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
segmenter.process(inputImage) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(SubjectSegmentationResult result) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Obtenir le résultat de la segmentation par sujet
Récupérer des masques et des bitmaps de premier plan
Une fois le traitement effectué, vous pouvez récupérer le masque de premier plan pour votre image appelant getForegroundConfidenceMask()
comme suit:
Kotlin
val colors = IntArray(image.width * image.height) val foregroundMask = result.foregroundConfidenceMask for (i in 0 until image.width * image.height) { if (foregroundMask[i] > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255) } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
int[] colors = new int[image.getWidth() * image.getHeight()]; FloatBuffer foregroundMask = result.getForegroundConfidenceMask(); for (int i = 0; i < image.getWidth() * image.getHeight(); i++) { if (foregroundMask.get() > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255); } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888 );
Vous pouvez également récupérer un bitmap du premier plan de l'image en appelant getForegroundBitmap()
:
Kotlin
val foregroundBitmap = result.foregroundBitmap
Java
Bitmap foregroundBitmap = result.getForegroundBitmap();
Récupérer des masques et des bitmaps pour chaque sujet
De même, vous pouvez récupérer le masque pour les sujets segmentés en appelant getConfidenceMask()
sur chaque sujet comme suit:
Kotlin
val subjects = result.subjects val colors = IntArray(image.width * image.height) for (subject in subjects) { val mask = subject.confidenceMask for (i in 0 until subject.width * subject.height) { val confidence = mask[i] if (confidence > 0.5f) { colors[image.width * (subject.startY - 1) + subject.startX] = Color.argb(128, 255, 0, 255) } } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
Listsubjects = result.getSubjects(); int[] colors = new int[image.getWidth() * image.getHeight()]; for (Subject subject : subjects) { FloatBuffer mask = subject.getConfidenceMask(); for (int i = 0; i < subject.getWidth() * subject.getHeight(); i++) { float confidence = mask.get(); if (confidence > 0.5f) { colors[width * (subject.getStartY() - 1) + subject.getStartX()] = Color.argb(128, 255, 0, 255); } } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 );
Vous pouvez également accéder au bitmap de chaque sujet segmenté comme suit:
Kotlin
val bitmaps = mutableListOf() for (subject in subjects) { bitmaps.add(subject.bitmap) }
Java
Listbitmaps = new ArrayList<>(); for (Subject subject : subjects) { bitmaps.add(subject.getBitmap()); }
Conseils pour améliorer les performances
Pour chaque session d'application, la première inférence est souvent plus lente que les inférences suivantes en raison de l'initialisation du modèle. Si une faible latence est essentielle, envisagez d'appeler une inférence "factice" à l'avance.
La qualité de vos résultats dépend de la qualité de l'image d'entrée:
- Pour que ML Kit obtienne un résultat de segmentation précis, l'image doit être d'au moins 512 x 512 pixels.
- Un mauvais cadrage peut également avoir un impact sur la précision. Si vous n'obtenez pas de résultats acceptables, demandez à l'utilisateur de reprendre la photo.