Bilder mit einem von AutoML trainierten Modell unter Android mit Labels versehen
Nachdem Sie Ihr eigenes Modell mit AutoML Vision Edge trainiert haben, können Sie damit in Ihrer App Bilder mit Labels versehen. Es gibt zwei Möglichkeiten, mit AutoML Vision Edge trainierte Modelle zu integrieren: Sie können das Modell bündeln, indem Sie es im Asset-Ordner Ihrer App ablegen, oder es dynamisch von Firebase herunterladen.Optionen für Modellbündelung | |
---|---|
In deiner App gebündelt |
|
Mit Firebase gehostet |
|
Ausprobieren
- Probieren Sie die Beispiel-App aus, um ein Beispiel für die Verwendung dieser API zu sehen.
Hinweis
1. In die Dateibuild.gradle
auf Projektebene muss das Maven-Repository von Google in die Abschnitte buildscript
und allprojects
aufgenommen werden.2. Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken der Gradle-Datei auf App-Ebene Ihres Moduls hinzu, die in der Regel
app/build.gradle
ist:
So bündeln Sie ein Modell mit Ihrer App:
dependencies { // ... // Image labeling feature with bundled automl model implementation 'com.google.mlkit:image-labeling-automl:16.2.1' }Fügen Sie die
linkFirebase
-Abhängigkeit hinzu, um ein Modell dynamisch aus Firebase herunterzuladen:
dependencies { // ... // Image labeling feature with automl model downloaded // from firebase implementation 'com.google.mlkit:image-labeling-automl:16.2.1' implementation 'com.google.mlkit:linkfirebase:16.0.1' }3. Wenn Sie ein Modell herunterladen möchten, müssen Sie Firebase zu Ihrem Android-Projekt hinzufügen, falls Sie dies noch nicht getan haben. Dies ist beim Bündeln des Modells nicht erforderlich.
1. Modell laden
Lokale Modellquelle konfigurieren
So bündeln Sie das Modell mit Ihrer Anwendung:1. Extrahieren Sie das Modell und seine Metadaten aus dem ZIP-Archiv, das Sie aus der Firebase Console heruntergeladen haben. Wir empfehlen Ihnen, die Dateien so zu verwenden, wie Sie sie heruntergeladen haben, ohne Änderungen (einschließlich der Dateinamen).
2. Fügen Sie das Modell und die zugehörigen Metadatendateien in das Anwendungspaket ein:
a. Wenn Sie in Ihrem Projekt keinen Asset-Ordner haben, erstellen Sie einen. Klicken Sie dazu mit der rechten Maustaste auf den Ordner
app/
und dann auf Neu > Ordner > Asset-Ordner.b. Erstellen Sie unter dem Asset-Ordner einen Unterordner, der die Modelldateien enthalten soll.
c. Kopieren Sie die Dateien
model.tflite
, dict.txt
und manifest.json
in den Unterordner. Alle drei Dateien müssen sich im selben Ordner befinden.3. Fügen Sie der Datei
build.gradle
Ihrer App Folgendes hinzu, damit Gradle die Modelldatei beim Erstellen der App nicht komprimiert:
android { // ... aaptOptions { noCompress "tflite" } }Die Modelldatei ist im App-Paket enthalten und steht ML Kit als Roh-Asset zur Verfügung.
Hinweis: Ab Version 4.1 des Android-Gradle-Plug-ins wird .tflite standardmäßig der noCompress-Liste hinzugefügt und die oben genannten Schritte werden nicht mehr benötigt.
4. Erstellen Sie ein
LocalModel
-Objekt und geben Sie den Pfad zur Modellmanifestdatei an:
Kotlin
val localModel = AutoMLImageLabelerLocalModel.Builder() .setAssetFilePath("manifest.json") // or .setAbsoluteFilePath(absolute file path to manifest file) .build()
Java
AutoMLImageLabelerLocalModel localModel = new AutoMLImageLabelerLocalModel.Builder() .setAssetFilePath("manifest.json") // or .setAbsoluteFilePath(absolute file path to manifest file) .build();
Eine von Firebase gehostete Modellquelle konfigurieren
Wenn Sie das extern gehostete Modell verwenden möchten, erstellen Sie ein RemoteModel
-Objekt und geben Sie dabei den Namen an, den Sie dem Modell bei der Veröffentlichung zugewiesen haben:
Kotlin
// Specify the name you assigned in the Firebase console. val remoteModel = AutoMLImageLabelerRemoteModel.Builder("your_model_name").build()
Java
// Specify the name you assigned in the Firebase console. AutoMLImageLabelerRemoteModel remoteModel = new AutoMLImageLabelerRemoteModel.Builder("your_model_name").build();
Starten Sie dann die Aufgabe zum Herunterladen des Modells und geben Sie die Bedingungen an, unter denen Sie das Herunterladen zulassen möchten. Wenn sich das Modell nicht auf dem Gerät befindet oder eine neuere Version des Modells verfügbar ist, wird es von der Aufgabe asynchron von Firebase heruntergeladen:
Kotlin
val downloadConditions = DownloadConditions.Builder() .requireWifi() .build() RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener { // Success. }
Java
DownloadConditions downloadConditions = new DownloadConditions.Builder() .requireWifi() .build(); RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(@NonNull Task task) { // Success. } });
Viele Anwendungen starten die Downloadaufgabe im Initialisierungscode, aber Sie können dies jederzeit tun, bevor Sie das Modell verwenden müssen.
Bildlabelersteller aus dem Modell erstellen
Nachdem Sie die Modellquellen konfiguriert haben, erstellen Sie aus einer davon ein ImageLabeler
-Objekt.
Wenn Sie nur ein lokal gebündeltes Modell haben, erstellen Sie einfach einen Labelersteller aus Ihrem AutoMLImageLabelerLocalModel
-Objekt und konfigurieren Sie den erforderlichen Konfidenzgrenzwert (siehe Modell bewerten):
Kotlin
val autoMLImageLabelerOptions = AutoMLImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0) // Evaluate your model in the Firebase console // to determine an appropriate value. .build() val labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)
Java
AutoMLImageLabelerOptions autoMLImageLabelerOptions = new AutoMLImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console // to determine an appropriate value. .build(); ImageLabeler labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)
Wenn Sie ein extern gehostetes Modell haben, müssen Sie prüfen, ob es heruntergeladen wurde, bevor Sie es ausführen. Sie können den Status der Modelldownloadaufgabe mit der Methode isModelDownloaded()
des Modellmanagers prüfen.
Sie müssen dies nur vor dem Ausführen des Labelerstellers prüfen. Wenn Sie jedoch sowohl ein extern gehostetes Modell als auch ein lokal gebündeltes Modell haben, kann es sinnvoll sein, diese Prüfung beim Instanziieren des Bildlabelerstellers durchzuführen: Erstellen Sie einen Labelersteller aus dem Remotemodell, wenn es heruntergeladen wurde, und andernfalls aus dem lokalen Modell.
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener { isDownloaded -> val optionsBuilder = if (isDownloaded) { AutoMLImageLabelerOptions.Builder(remoteModel) } else { AutoMLImageLabelerOptions.Builder(localModel) } // Evaluate your model in the Firebase console to determine an appropriate threshold. val options = optionsBuilder.setConfidenceThreshold(0.0f).build() val labeler = ImageLabeling.getClient(options) }
Java
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Boolean isDownloaded) { AutoMLImageLabelerOptions.Builder optionsBuilder; if (isDownloaded) { optionsBuilder = new AutoMLImageLabelerOptions.Builder(remoteModel); } else { optionsBuilder = new AutoMLImageLabelerOptions.Builder(localModel); } AutoMLImageLabelerOptions options = optionsBuilder .setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console // to determine an appropriate threshold. .build(); ImageLabeler labeler = ImageLabeling.getClient(options); } });
Wenn Sie nur ein extern gehostetes Modell haben, sollten Sie die modellbezogenen Funktionen deaktivieren und z. B. einen Teil Ihrer UI ausblenden oder ausblenden, bis Sie bestätigt haben, dass das Modell heruntergeladen wurde. Dazu hängen Sie einen Listener an die Methode download()
des Modellmanagers an:
Kotlin
RemoteModelManager.getInstance().download(remoteModel, conditions) .addOnSuccessListener { // Download complete. Depending on your app, you could enable the ML // feature, or switch from the local model to the remote model, etc. }
Java
RemoteModelManager.getInstance().download(remoteModel, conditions) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Void v) { // Download complete. Depending on your app, you could enable // the ML feature, or switch from the local model to the remote // model, etc. } });
2. Eingabebild vorbereiten
Erstellen Sie dann für jedes Bild, das Sie mit einem Label versehen möchten, ein InputImage
-Objekt aus Ihrem Bild. Der Bildlabelersteller wird am schnellsten ausgeführt, wenn Sie Bitmap
oder, wenn Sie die Camera2 API verwenden, eine YUV_420_888-media.Image
verwenden. Diese werden nach Möglichkeit empfohlen.
Sie können ein InputImage
-Objekt aus verschiedenen Quellen erstellen. Diese werden unten jeweils erläutert.
Mit einem media.Image
Wenn Sie ein InputImage
-Objekt aus einem media.Image
-Objekt erstellen möchten, z. B. wenn Sie ein Bild mit der Kamera eines Geräts aufnehmen, übergeben Sie das media.Image
-Objekt und die Bilddrehung an InputImage.fromMediaImage()
.
Wenn Sie die
CameraX-Bibliothek verwenden, wird der Rotationswert von den Klassen OnImageCapturedListener
und ImageAnalysis.Analyzer
berechnet.
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 // ... } } }
Wenn Sie keine Kamerabibliothek verwenden, die den Drehgrad des Bildes angibt, können Sie ihn anhand des Gerätedrehungsgrads und der Ausrichtung des Kamerasensors im Gerät berechnen:
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; }
Übergeben Sie dann das media.Image
-Objekt und den Wert für den Rotationsgrad an InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Datei-URI verwenden
Übergeben Sie den App-Kontext und den Datei-URI an InputImage.fromFilePath()
, um ein InputImage
-Objekt aus einem Datei-URI zu erstellen. Das ist nützlich, wenn du mit einem ACTION_GET_CONTENT
-Intent den Nutzer auffordern möchtest, ein Bild aus seiner Galerie-App auszuwählen.
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(); }
Mithilfe von ByteBuffer
oder ByteArray
Zum Erstellen eines InputImage
-Objekts aus einem ByteBuffer
- oder ByteArray
-Objekt müssen Sie zuerst den Grad der Bilddrehung berechnen, wie zuvor für die media.Image
-Eingabe beschrieben.
Erstellen Sie dann das InputImage
-Objekt mit dem Zwischenspeicher oder Array sowie der Höhe, Breite, Farbcodierungsformat und Rotationsgrad des Bildes:
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 );
Mit einem Bitmap
Mit der folgenden Deklaration kannst du ein InputImage
-Objekt aus einem Bitmap
-Objekt erstellen:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Das Bild wird durch ein Bitmap
-Objekt zusammen mit Grad der Drehung dargestellt.
3. Labelersteller für Bilder ausführen
Wenn Sie Objekte in einem Bild mit Labels versehen möchten, übergeben Sie dasimage
-Objekt an die Methode process()
der ImageLabeler
.
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
labeler.process(image) .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() { @Override public void onSuccess(List<ImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Informationen zu mit Labels versehenen Objekten abrufen
Wenn der Vorgang zum Beschriften von Bildern erfolgreich ist, wird eine Liste von ImageLabel
-Objekten an den Erfolgs-Listener übergeben. Jedes ImageLabel
-Objekt steht für etwas, das im Bild mit einem Label versehen wurde. Sie können die Textbeschreibung jedes Labels, den Konfidenzwert der Übereinstimmung und den Index der Übereinstimmung abrufen.
Beispiel:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
Java
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
Tipps zur Verbesserung der Echtzeitleistung
Wenn Sie Bilder in einer Echtzeitanwendung mit Labels versehen möchten, beachten Sie die folgenden Richtlinien, um die besten Framerates zu erzielen:
- Wenn Sie die
Camera
odercamera2
API verwenden, drosseln Sie Aufrufe an den Bildlabelersteller. Wenn ein neuer Videoframe verfügbar wird, während der Bildlabelersteller ausgeführt wird, löschen Sie ihn. Ein Beispiel hierfür finden Sie in der Beispiel-App der Kurzanleitung in der KlasseVisionProcessorBase
. - Wenn Sie die
CameraX
API verwenden, muss die Rückdruckstrategie auf den StandardwertImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
festgelegt sein. Dadurch wird sichergestellt, dass jeweils nur ein Bild zur Analyse geliefert wird. Werden weitere Bilder erstellt, während das Analysetool ausgelastet ist, werden diese automatisch gelöscht und nicht in die Warteschlange gestellt. Nachdem das zu analysierende Bild durch Aufrufen von ImageProxy.close() geschlossen wurde, wird das jeweils neueste Bild bereitgestellt. - Wenn Sie die Ausgabe des Bildlabelerstellers verwenden, um Grafiken auf dem Eingabebild einzublenden, rufen Sie zuerst das Ergebnis aus dem ML Kit ab und rendern dann das Bild und das Overlay in einem einzigen Schritt. Dies wird für jeden Eingabeframe nur einmal auf der Anzeigeoberfläche gerendert. Ein entsprechendes Beispiel finden Sie in der Beispiel-App der Kurzanleitung in den Klassen
CameraSourcePreview
undGraphicOverlay
. - Wenn du die Camera2 API verwendest, nimm Bilder im
ImageFormat.YUV_420_888
-Format auf. Wenn du die ältere Camera API verwendest, nimm Bilder imImageFormat.NV21
-Format auf.