Puoi utilizzare ML Kit per etichettare gli oggetti riconosciuti in un'immagine, utilizzando un modello sul dispositivo o un modello cloud. Consulta la panoramica per conoscere i vantaggi di ciascun approccio.
Prima di iniziare
- Se non l'hai già fatto, aggiungi Firebase al tuo progetto Android .
- Aggiungi le dipendenze per le librerie Android ML Kit al file Gradle del modulo (a livello di app) (in genere
app/build.gradle
):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-image-label-model:20.0.1' }
- Facoltativo ma consigliato : se utilizzi l'API sul dispositivo, configura la tua app per scaricare automaticamente il modello ML sul dispositivo dopo aver installato l'app dal Play Store.
A tale scopo, aggiungi la seguente dichiarazione al file
AndroidManifest.xml
della tua app:<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="label" /> <!-- To use multiple models: android:value="label,model2,model3" --> </application>
Se non si abilitano i download del modello al momento dell'installazione, il modello verrà scaricato la prima volta che si esegue il rilevatore sul dispositivo. Le richieste effettuate prima del completamento del download non produrranno risultati. Se desideri utilizzare il modello basato su cloud e non hai già abilitato le API basate su cloud per il tuo progetto, fallo ora:
- Apri la pagina API ML Kit della console Firebase.
Se non hai già aggiornato il tuo progetto a un piano tariffario Blaze, fai clic su Aggiorna per farlo. (Ti verrà richiesto di eseguire l'aggiornamento solo se il tuo progetto non è incluso nel piano Blaze.)
Solo i progetti a livello di Blaze possono utilizzare API basate su cloud.
- Se le API basate su cloud non sono già abilitate, fai clic su Abilita API basate su cloud .
Se desideri utilizzare solo il modello su dispositivo, puoi saltare questo passaggio.
Ora sei pronto per etichettare le immagini utilizzando un modello sul dispositivo o un modello basato su cloud.
1. Preparare l'immagine di input
Crea un oggettoFirebaseVisionImage
dalla tua immagine. L'etichettatore di immagini viene eseguito più velocemente quando si utilizza una Bitmap
o, se si utilizza l'API camera2, un media.Image
in formato JPEG, che sono consigliati quando possibile.Per creare un oggetto
FirebaseVisionImage
da un oggettomedia.Image
, ad esempio quando acquisisci un'immagine dalla fotocamera di un dispositivo, passa l'oggettomedia.Image
e la rotazione dell'immagine aFirebaseVisionImage.fromMediaImage()
.Se utilizzi la libreria CameraX , le classi
OnImageCapturedListener
eImageAnalysis.Analyzer
calcolano il valore di rotazione per te, quindi devi solo convertire la rotazione in una delle costantiROTATION_
di ML Kit prima di chiamareFirebaseVisionImage.fromMediaImage()
:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin+KTX
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Se non utilizzi una libreria di fotocamere che fornisce la rotazione dell'immagine, puoi calcolarla dalla rotazione del dispositivo e dall'orientamento del sensore della fotocamera nel dispositivo:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin+KTX
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Quindi, passa l'oggetto
media.Image
e il valore di rotazione aFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Per creare un oggetto
FirebaseVisionImage
da un URI di file, passa il contesto dell'app e l'URI del file aFirebaseVisionImage.fromFilePath()
. Ciò è utile quando utilizzi un intentoACTION_GET_CONTENT
per chiedere all'utente di selezionare un'immagine dalla propria app della galleria.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin+KTX
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Per creare un oggetto
FirebaseVisionImage
da unByteBuffer
o da un array di byte, calcola prima la rotazione dell'immagine come descritto sopra per l'inputmedia.Image
.Quindi, crea un oggetto
FirebaseVisionImageMetadata
che contenga l'altezza, la larghezza, il formato di codifica del colore e la rotazione dell'immagine:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin+KTX
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
Utilizza il buffer o l'array e l'oggetto metadati per creare un oggetto
FirebaseVisionImage
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin+KTX
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Per creare un oggetto
FirebaseVisionImage
da un oggettoBitmap
:L'immagine rappresentata dall'oggettoJava
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
deve essere verticale, senza necessità di ulteriore rotazione.
2. Configurare ed eseguire l'etichettatore di immagini
Per etichettare gli oggetti in un'immagine, passa l'oggettoFirebaseVisionImage
al metodo processImage
di FirebaseVisionImageLabeler
.Innanzitutto, ottieni un'istanza di
FirebaseVisionImageLabeler
.Se desideri utilizzare l'etichettatrice di immagini sul dispositivo:
Java
FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() .getOnDeviceImageLabeler(); // Or, to set the minimum confidence required: // FirebaseVisionOnDeviceImageLabelerOptions options = // new FirebaseVisionOnDeviceImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build(); // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() // .getOnDeviceImageLabeler(options);
Kotlin+KTX
val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler() // Or, to set the minimum confidence required: // val options = FirebaseVisionOnDeviceImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build() // val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler(options)
Se desideri utilizzare l'etichettatore di immagini cloud:
Java
FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() .getCloudImageLabeler(); // Or, to set the minimum confidence required: // FirebaseVisionCloudImageLabelerOptions options = // new FirebaseVisionCloudImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build(); // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() // .getCloudImageLabeler(options);
Kotlin+KTX
val labeler = FirebaseVision.getInstance().getCloudImageLabeler() // Or, to set the minimum confidence required: // val options = FirebaseVisionCloudImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build() // val labeler = FirebaseVision.getInstance().getCloudImageLabeler(options)
Quindi, passa l'immagine al metodo
processImage()
:Java
labeler.processImage(image) .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionImageLabel>>() { @Override public void onSuccess(List<FirebaseVisionImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin+KTX
labeler.processImage(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
3. Ottieni informazioni sugli oggetti etichettati
Se l'operazione di etichettatura dell'immagine ha esito positivo, un elenco di oggettiFirebaseVisionImageLabel
verrà passato al listener di successo. Ogni oggetto FirebaseVisionImageLabel
rappresenta qualcosa che è stato etichettato nell'immagine. Per ogni etichetta, puoi ottenere la descrizione testuale dell'etichetta, l'ID dell'entità del Knowledge Graph (se disponibile) e il punteggio di confidenza della corrispondenza. Per esempio: Java
for (FirebaseVisionImageLabel label: labels) {
String text = label.getText();
String entityId = label.getEntityId();
float confidence = label.getConfidence();
}
Kotlin+KTX
for (label in labels) {
val text = label.text
val entityId = label.entityId
val confidence = label.confidence
}
Suggerimenti per migliorare le prestazioni in tempo reale
Se desideri etichettare le immagini in un'applicazione in tempo reale, segui queste linee guida per ottenere i migliori framerate:
- Limita le chiamate all'etichettatore di immagini. Se un nuovo fotogramma video diventa disponibile mentre l'etichettatrice di immagini è in esecuzione, rilascia il fotogramma.
- Se stai utilizzando l'output dell'etichettatore di immagini per sovrapporre la grafica all'immagine di input, ottieni prima il risultato da ML Kit, quindi esegui il rendering dell'immagine e della sovrapposizione in un unico passaggio. In questo modo, viene eseguito il rendering sulla superficie di visualizzazione solo una volta per ciascun fotogramma di input.
Se utilizzi l'API Camera2, acquisisci immagini nel formato
ImageFormat.YUV_420_888
.Se utilizzi la versione precedente dell'API Camera, acquisisci immagini nel formato
ImageFormat.NV21
.
Prossimi passi
- Prima di distribuire in produzione un'app che utilizza un'API cloud, dovresti adottare alcune misure aggiuntive per prevenire e mitigare gli effetti dell'accesso API non autorizzato .