Utilizza ML Kit per aggiungere facilmente funzionalità di segmentazione dei soggetti alla tua app.
| Funzionalità | Dettagli |
|---|---|
| Nome SDK | play-services-mlkit-subject-segmentation |
| Implementazione | Non in bundle: il modello viene scaricato dinamicamente utilizzando Google Play Services. |
| Impatto sulle dimensioni dell'app | Aumento delle dimensioni di circa 200 KB. |
| Tempo di inizializzazione | Gli utenti potrebbero dover attendere il download del modello prima del primo utilizzo. |
Prova
- Prova l'app di esempio per vedere un esempio di utilizzo di questa API.
Prima di iniziare
- Nel file
build.gradlea livello di progetto, assicurati di includere il repository Maven di Google nelle sezionibuildscripteallprojects. - Aggiungi la dipendenza per la libreria di segmentazione dei soggetti di ML Kit al file Gradle a livello di app del modulo, che in genere è
app/build.gradle:
dependencies {
implementation 'com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1'
}
Come indicato in precedenza, il modello è fornito da Google Play Services.
Puoi configurare l'app in modo che scarichi automaticamente il modello sul dispositivo dopo l'installazione dal Play Store. Per farlo, aggiungi la seguente dichiarazione al file AndroidManifest.xml dell'app:
<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>
Puoi anche controllare esplicitamente la disponibilità del modello e richiedere il download tramite Google Play Services con l'API ModuleInstallClient.
Se non attivi i download dei modelli in fase di installazione o non richiedi il download esplicito, il modello viene scaricato la prima volta che esegui il segmentatore. Le richieste effettuate prima del completamento del download non producono risultati.
1. Prepara l'immagine di input
Per eseguire la segmentazione di un'immagine, crea un oggetto InputImage da un Bitmap, media.Image, ByteBuffer, array di byte o un file sul dispositivo.
Puoi creare un InputImage
oggetto da diverse origini, ognuna delle quali è spiegata di seguito.
Utilizzo di un media.Image
Per creare un oggetto InputImage
da un oggetto media.Image, ad esempio quando acquisisci un'immagine dalla
fotocamera di un dispositivo, passa l'oggetto media.Image e la
rotazione dell'immagine a InputImage.fromMediaImage().
Se utilizzi la
CameraX library, le classi OnImageCapturedListener e
ImageAnalysis.Analyzer calcolano il valore di rotazione
per te.
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 // ... } } }
Se non utilizzi una libreria della fotocamera che ti fornisce il grado di rotazione dell'immagine, puoi calcolarlo dal grado di rotazione del dispositivo e dall'orientamento del sensore della fotocamera nel dispositivo:
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; }
Quindi, passa l'oggetto media.Image e il valore del grado di rotazione a InputImage.fromMediaImage():
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Utilizzo di un URI del file
Per creare un InputImage
oggetto da un URI del file, passa il contesto dell'app e l'URI del file a
InputImage.fromFilePath(). Questa operazione è utile quando utilizzi un intent ACTION_GET_CONTENT per chiedere all'utente di selezionare un'immagine dalla sua app di galleria.
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(); }
Utilizzo di un ByteBuffer o ByteArray
Per creare un InputImage
oggetto da un ByteBuffer o un ByteArray, calcola prima il grado di rotazione dell'immagine
come descritto in precedenza per l'input media.Image.
Quindi, crea l'oggetto InputImage con il buffer o l'array, insieme all'altezza, alla larghezza, al formato di codifica dei colori e al grado di rotazione dell'immagine:
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 );
Utilizzo di un Bitmap
Per creare un oggetto InputImage
da un oggetto Bitmap, effettua la seguente dichiarazione:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
L'immagine è rappresentata da un oggetto Bitmap insieme ai gradi di rotazione.
2. Crea un'istanza di SubjectSegmenter
Definisci le opzioni del segmentatore
Per segmentare l'immagine, crea prima un'istanza di SubjectSegmenterOptions come segue:
Kotlin
val options = SubjectSegmenterOptions.Builder()
// enable options
.build()Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder()
// enable options
.build();Ecco i dettagli di ogni opzione:
Maschera di affidabilità in primo piano
La maschera di affidabilità in primo piano ti consente di distinguere il soggetto in primo piano dallo sfondo.
Se chiami enableForegroundConfidenceMask() nelle opzioni, potrai recuperare la maschera in primo piano chiamando getForegroundMask() sull'oggetto SubjectSegmentationResult restituito dopo l'elaborazione dell'immagine.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build();
Bitmap in primo piano
Allo stesso modo, puoi anche ottenere una bitmap del soggetto in primo piano.
Se chiami enableForegroundBitmap() nelle opzioni, potrai recuperare la bitmap in primo piano chiamando getForegroundBitmap() sull'oggetto SubjectSegmentationResult restituito dopo l'elaborazione dell'immagine.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build();
Maschera di affidabilità per più soggetti
Come per le opzioni in primo piano, puoi utilizzare SubjectResultOptions per attivare la maschera di affidabilità per ogni soggetto in primo piano come segue:
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 per più soggetti
Allo stesso modo, puoi attivare la bitmap per ogni soggetto:
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()Crea il segmentatore dei soggetti
Dopo aver specificato le opzioni SubjectSegmenterOptions, crea un'
SubjectSegmenter istanza chiamando getClient() e passando le opzioni come un
parametro:
Kotlin
val segmenter = SubjectSegmentation.getClient(options)
Java
SubjectSegmenter segmenter = SubjectSegmentation.getClient(options);
3. Elabora un'immagine
Passa l'oggetto InputImage
preparato al metodo SubjectSegmenter's process:
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. Ottieni il risultato della segmentazione dei soggetti
Recupera maschere e bitmap in primo piano
Una volta elaborata, puoi recuperare la maschera in primo piano per l'immagine chiamando getForegroundConfidenceMask() come segue:
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 );
Puoi anche recuperare una bitmap del primo piano dell'immagine chiamando getForegroundBitmap():
Kotlin
val foregroundBitmap = result.foregroundBitmap
Java
Bitmap foregroundBitmap = result.getForegroundBitmap();
Recupera maschere e bitmap per ogni soggetto
Allo stesso modo, puoi recuperare la maschera per i soggetti segmentati chiamando getConfidenceMask() su ogni soggetto come segue:
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 );
Puoi anche accedere alla bitmap di ogni soggetto segmentato come segue:
Kotlin
val bitmaps = mutableListOf() for (subject in subjects) { bitmaps.add(subject.bitmap) }
Java
Listbitmaps = new ArrayList<>(); for (Subject subject : subjects) { bitmaps.add(subject.getBitmap()); }
Suggerimenti per migliorare il rendimento
Per ogni sessione dell'app, la prima inferenza è spesso più lenta delle inferenze successive a causa dell'inizializzazione del modello. Se la bassa latenza è fondamentale, valuta la possibilità di chiamare in anticipo un'inferenza "fittizia".
La qualità dei risultati dipende dalla qualità dell'immagine di input:
- Affinché ML Kit ottenga un risultato di segmentazione accurato, l'immagine deve avere una risoluzione di almeno 512 x 512 pixel.
- Anche una messa a fuoco scadente dell'immagine può influire sull'accuratezza. Se non ottieni risultati accettabili, chiedi all'utente di acquisire di nuovo l'immagine.