Utilizza ML Kit per aggiungere facilmente funzionalità di segmentazione dei soggetti alla tua app.
Funzionalità | Dettagli |
---|---|
Nome SDK | play-services-mlkit-subject-segmentation |
Implementazione | Separato: 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.gradle
a livello di progetto, assicurati di includere il repository Maven di Google nelle 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 di solito è
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 la tua 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
della tua 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 i servizi Google Play 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 su un'immagine, crea un oggetto InputImage
da un Bitmap
, media.Image
, ByteBuffer
, array di byte o un file sul dispositivo.
Puoi creare un oggetto InputImage
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
libreria
CameraX, 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 di fotocamere 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; }
Poi, 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 file
Per creare un oggetto InputImage
da un URI file, passa il contesto dell'app e l'URI file a
InputImage.fromFilePath()
. Questa funzionalità è utile quando
utilizzi un intent ACTION_GET_CONTENT
per chiedere all'utente di selezionare
un'immagine dalla sua 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 ByteBuffer
o da 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 del colore 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 confidenza del primo piano
La maschera di confidenza del primo piano consente di distinguere il soggetto in primo piano dallo sfondo.
La chiamata enableForegroundConfidenceMask()
nelle opzioni consente di recuperare in un secondo momento
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.
Chiama enableForegroundBitmap()
nelle opzioni per 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 multisoggetto
Come per le opzioni in primo piano, puoi utilizzare SubjectResultOptions
per attivare
la maschera di confidenza 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 multisoggetto
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()
Creare il segmentatore del soggetto
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. Elaborare 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 e bitmap in primo piano
Una volta elaborata, puoi recuperare la maschera del primo piano per la chiamata di immagini
getForegroundConfidenceMask()
nel seguente modo:
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 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 delle inferenze successive a causa dell'inizializzazione del modello. Se la bassa latenza è fondamentale, valuta la possibilità di chiamare un'inferenza "fittizia" in anticipo.
La qualità dei risultati dipende dalla qualità dell'immagine di input:
- Affinché ML Kit ottenga un risultato di segmentazione accurato, l'immagine deve essere di almeno 512 x 512 pixel.
- Anche una messa a fuoco scadente dell'immagine può influire sull'accuratezza. Se i risultati non sono accettabili, chiedi all'utente di acquisire nuovamente l'immagine.