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 in modo dinamico tramite 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 utilizzo di esempio di questa API.
Prima di iniziare
- Nel file
build.gradle
a livello di progetto, assicurati di includere il Repository Maven di Google in entrambe le sezionibuildscript
eallprojects
. - 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 dell'app dal Play Store. A questo scopo, 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'APIModuleInstallClient.
Se non abiliti i download dei modelli al momento dell'installazione o richiedi il download esplicito, il modello viene scaricato la prima volta che esegui il segmento. Le richieste effettuate prima del completamento del download non generano risultati.
1. Prepara l'immagine di input
Per eseguire la segmentazione su un'immagine, crea un oggetto InputImage
da un array di byte Bitmap
, media.Image
, ByteBuffer
, da un file o da un file sul dispositivo.
Puoi creare un oggetto InputImage
da origini diverse, ognuna 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, trasmetti l'oggetto media.Image
e la rotazione dell'immagine a InputImage.fromMediaImage()
.
Se usi la libreria
FotocameraX, le classi OnImageCapturedListener
e ImageAnalysis.Analyzer
calcolano automaticamente il valore di rotazione.
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 raccolta della fotocamera che 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 dell'URI di un file
Per creare un oggetto InputImage
da un URI del file, passa il contesto dell'app e l'URI del file a InputImage.fromFilePath()
. Questo è utile quando
utilizzi un intent ACTION_GET_CONTENT
per richiedere all'utente di selezionare
un'immagine dall'app 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 ByteBuffer
o ByteArray
Per creare un oggetto InputImage
da un oggetto ByteBuffer
o 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 ad altezza, larghezza, formato di codifica del colore e 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 con i gradi di rotazione.
2. Crea un'istanza di SubjectSegmenter
Definisci le opzioni dei segmenti di pubblico
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();
Di seguito sono riportati i dettagli di ciascuna opzione:
Maschera di confidenza in primo piano
La maschera di confidenza in primo piano ti consente di distinguere il soggetto in primo piano dallo sfondo.
La chiamata enableForegroundConfidenceMask()
nelle opzioni ti consente di recuperare
la maschera in primo piano in un secondo momento 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 ottenere una bitmap del soggetto in primo piano.
La chiamata a enableForegroundBitmap()
nelle opzioni ti consente di recuperare in un secondo momento 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 confidenza di più soggetti
Come per le opzioni in primo piano, puoi utilizzare l'SubjectResultOptions
per attivare la maschera di confidenza per ogni soggetto in primo piano, come indicato di seguito:
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 con più soggetti
Analogamente, puoi abilitare la bitmap per ciascun 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 segmento di pubblico
Dopo aver specificato le opzioni SubjectSegmenterOptions
, crea un'istanza SubjectSegmenter
chiamando getClient()
e passando le opzioni come parametro:
Kotlin
val segmenter = SubjectSegmentation.getClient(options)
Java
SubjectSegmenter segmenter = SubjectSegmentation.getClient(options);
3. Elabora un'immagine
Passa l'oggetto InputImage
preparato al metodo process
di 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. Ottenere il risultato della segmentazione del soggetto
Recuperare maschere in primo piano e bitmap
Una volta elaborata la richiesta, puoi recuperare la maschera in primo piano per l'immagine che chiama
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 che chiama 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 ciascun oggetto 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 ciascun soggetto segmentato nel seguente modo:
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 rispetto alle inferenze successive dovute all'inizializzazione del modello. Se è fondamentale una bassa latenza, prova a chiamare subito un'inferenza "fittizia".
La qualità dei risultati dipende dalla qualità dell'immagine di input:
- Affinché ML Kit possa ottenere un risultato di segmentazione accurato, l'immagine deve essere di almeno 512 x 512 pixel.
- Anche una scarsa messa a fuoco delle immagini può influire sulla precisione. Se non ottieni risultati accettabili, chiedi all'utente di recuperare l'immagine.