Nachdem Sie Ihr eigenes Modell mit AutoML Vision Edge trainiert haben, führen Sie folgende Schritte aus: können Sie sie in Ihrer App zum Beschriften von Bildern verwenden.
Es gibt zwei Möglichkeiten, mit AutoML Vision Edge trainierte Modelle einzubinden: bündeln Sie das Modell, indem Sie es im Asset-Ordner Ihrer App ablegen. Alternativ können Sie dynamisch von Firebase herunterladen.
Optionen für Modellbündelung | |
---|---|
In deiner App enthalten |
|
Mit Firebase gehostet |
|
Hinweis
Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken Ihres Moduls Gradle-Datei auf App-Ebene, in der Regel
app/build.gradle
:So bündeln Sie ein Modell mit Ihrer App:
dependencies { // ... // Image labeling feature with bundled automl model implementation 'com.google.mlkit:image-labeling-custom:16.3.1' }
Fügen Sie zum dynamischen Herunterladen eines Modells aus Firebase den
linkFirebase
hinzu. Abhängigkeit:dependencies { // ... // Image labeling feature with automl model downloaded // from firebase implementation 'com.google.mlkit:image-labeling-custom:16.3.1' implementation 'com.google.mlkit:linkfirebase:16.1.0' }
Wenn Sie ein Modell herunterladen möchten, müssen Sie Firebase zu Ihrem Android-Projekt hinzufügen, falls noch nicht geschehen. Dies ist nicht erforderlich, wenn Sie das Modell als Paket anbieten.
1. Modell laden
Lokale Modellquelle konfigurieren
So bündeln Sie das Modell mit Ihrer App:
Modell und seine Metadaten aus dem heruntergeladenen ZIP-Archiv extrahieren aus der Firebase-Konsole. Wir empfehlen, die Dateien so zu verwenden, wie Sie sie heruntergeladen haben. ohne Änderungen (einschließlich der Dateinamen).
Fügen Sie das Modell und die zugehörigen Metadatendateien in das Anwendungspaket ein:
- Wenn Sie in Ihrem Projekt keinen Asset-Ordner haben, erstellen Sie einen
mit der rechten Maustaste auf den Ordner
app/
und dann auf Neu > Ordner > Ordner „Assets“ - Erstellen Sie einen Unterordner unter dem Assets-Ordner, in dem das Modell gespeichert wird -Dateien.
- Kopieren Sie die Dateien
model.tflite
,dict.txt
undmanifest.json
in den Unterordner (alle drei Dateien müssen sich im im selben Ordner).
- Wenn Sie in Ihrem Projekt keinen Asset-Ordner haben, erstellen Sie einen
mit der rechten Maustaste auf den Ordner
Fügen Sie der Datei
build.gradle
Ihrer App Folgendes hinzu, um sicherzustellen, Gradle komprimiert die Modelldatei beim Erstellen der App nicht:android { // ... aaptOptions { noCompress "tflite" } }
Die Modelldatei wird in das App-Paket aufgenommen und ML Kit als Roh-Asset zur Verfügung gestellt.
Erstellen Sie ein
LocalModel
-Objekt und geben Sie dabei den Pfad zum Modellmanifest an. Datei:Java
AutoMLImageLabelerLocalModel localModel = new AutoMLImageLabelerLocalModel.Builder() .setAssetFilePath("manifest.json") // or .setAbsoluteFilePath(absolute file path to manifest file) .build();
Kotlin
val localModel = LocalModel.Builder() .setAssetManifestFilePath("manifest.json") // or .setAbsoluteManifestFilePath(absolute file path to manifest file) .build()
Von Firebase gehostete Modellquelle konfigurieren
Erstellen Sie ein CustomRemoteModel
-Objekt, um das remote gehostete Modell zu verwenden.
Geben Sie den Namen an, den Sie dem Modell bei der Veröffentlichung zugewiesen haben:
Java
// Specify the name you assigned in the Firebase console.
FirebaseModelSource firebaseModelSource =
new FirebaseModelSource.Builder("your_model_name").build();
CustomRemoteModel remoteModel =
new CustomRemoteModel.Builder(firebaseModelSource).build();
Kotlin
// Specify the name you assigned in the Firebase console.
val firebaseModelSource = FirebaseModelSource.Builder("your_model_name")
.build()
val remoteModel = CustomRemoteModel.Builder(firebaseModelSource).build()
Starten Sie dann den Modelldownload und geben Sie die Bedingungen an, unter denen Sie Downloads zulassen möchten. Wenn das Modell nicht auf dem Gerät installiert ist oder ein neueres Modell Version des Modells verfügbar ist, lädt die Aufgabe asynchron das aus Firebase verwenden:
Java
DownloadConditions downloadConditions = new DownloadConditions.Builder()
.requireWifi()
.build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(@NonNull Task<Void> task) {
// Success.
}
});
Kotlin
val downloadConditions = DownloadConditions.Builder()
.requireWifi()
.build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
.addOnSuccessListener {
// Success.
}
Viele Apps starten die Downloadaufgabe in ihrem Initialisierungscode, Sie können dies aber auch jederzeit tun, bevor Sie das Modell verwenden müssen.
Labelersteller für Bilder aus Ihrem Modell erstellen
Nachdem Sie Ihre Modellquellen konfiguriert haben, erstellen Sie ein ImageLabeler
-Objekt aus einer der Quellen.
Wenn Sie nur ein lokal gebündeltes Modell haben, erstellen Sie einfach einen Labelersteller
CustomImageLabelerOptions
-Objekt und konfigurieren Sie den Konfidenzwert
Grenzwert, den Sie verlangen möchten (siehe Modell bewerten):
Java
CustomImageLabelerOptions customImageLabelerOptions = new CustomImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0.0f) // Evaluate your model in the Cloud console
// to determine an appropriate value.
.build();
ImageLabeler labeler = ImageLabeling.getClient(customImageLabelerOptions);
Kotlin
val customImageLabelerOptions = CustomImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0.0f) // Evaluate your model in the Cloud console
// to determine an appropriate value.
.build()
val labeler = ImageLabeling.getClient(customImageLabelerOptions)
Wenn Sie ein extern gehostetes Modell haben, müssen Sie prüfen,
die Sie vor der Ausführung heruntergeladen haben. Sie können den Status des Modelldownloads
mit der Methode isModelDownloaded()
des Modellmanagers.
Sie müssen dies nur vor dem Ausführen des Labelerstellers bestätigen. Wenn Sie ein remote gehostetes und ein lokal gebündeltes Modell haben, Sinn, diese Prüfung bei der Instanziierung des Labelerstellers für Bilder durchzuführen: Labelersteller aus dem Remote-Modell, falls es heruntergeladen wurde, und vom lokalen modellieren.
Java
RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean isDownloaded) {
CustomImageLabelerOptions.Builder optionsBuilder;
if (isDownloaded) {
optionsBuilder = new CustomImageLabelerOptions.Builder(remoteModel);
} else {
optionsBuilder = new CustomImageLabelerOptions.Builder(localModel);
}
CustomImageLabelerOptions options = optionsBuilder
.setConfidenceThreshold(0.0f) // Evaluate your model in the Cloud console
// to determine an appropriate threshold.
.build();
ImageLabeler labeler = ImageLabeling.getClient(options);
}
});
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener { isDownloaded ->
val optionsBuilder =
if (isDownloaded) {
CustomImageLabelerOptions.Builder(remoteModel)
} else {
CustomImageLabelerOptions.Builder(localModel)
}
// Evaluate your model in the Cloud console to determine an appropriate threshold.
val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
val labeler = ImageLabeling.getClient(options)
}
Wenn Sie nur ein remote gehostetes Modell haben, sollten Sie die modellbezogenen
wie z. B. das Ausgrauen oder Ausblenden eines Teils der Benutzeroberfläche, bis
bestätigen Sie, dass das Modell heruntergeladen wurde. Dazu fügen Sie der download()
-Methode des Modellmanagers einen Listener hinzu:
Java
RemoteModelManager.getInstance().download(remoteModel, conditions)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@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.
}
});
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.
}
2. Eingabebild vorbereiten
Erstellen Sie dann für jedes Bild, dem Sie ein Label hinzufügen möchten, ein InputImage
-Element.
Objekt aus Ihrem Bild. Mit einem Bitmap
wird der Labelersteller für das Bild am schnellsten ausgeführt.
oder, wenn Sie die camera2 API verwenden, ein YUV_420_888 media.Image
, die
wenn möglich.
Sie können eine InputImage
aus verschiedenen Quellen erstellen, die unten erläutert werden.
Mit einem media.Image
Um ein InputImage
-Objekt aus einem
media.Image
-Objekts erstellen, beispielsweise wenn Sie ein Bild von einem
des Geräts an und übergib das media.Image
-Objekt und die
Rotation auf InputImage.fromMediaImage()
.
Wenn Sie die Methode
CameraX-Bibliothek, den OnImageCapturedListener
und
ImageAnalysis.Analyzer
-Klassen berechnen den Rotationswert
für Sie.
Kotlin+KTX
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) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees); // Pass image to an ML Kit Vision API // ... } }
Wenn Sie keine Kamerabibliothek verwenden, die Ihnen den Drehungsgrad des Bildes anzeigt, lässt sich anhand des Drehungsgrads des Geräts und der Ausrichtung der Kamera berechnen. Sensor im Gerät:
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 }
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; }
Übergeben Sie dann das media.Image
-Objekt und den
Wert für Rotationsgrad auf InputImage.fromMediaImage()
:
Kotlin+KTX
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Datei-URI verwenden
Wenn Sie ein InputImage
-Objekt aus einem Datei-URI erstellen möchten, übergeben Sie den App-Kontext und den Datei-URI an InputImage.fromFilePath()
. Dies ist nützlich, wenn Sie
Verwenden Sie den Intent ACTION_GET_CONTENT
, um den Nutzer zur Auswahl aufzufordern
ein Bild aus ihrer Galerie-App.
Kotlin+KTX
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(); }
ByteBuffer
oder ByteArray
verwenden
Um ein InputImage
-Objekt aus einem
ByteBuffer
oder ByteArray
, berechnen Sie zuerst das Bild
Drehung wie zuvor für die media.Image
-Eingabe beschrieben.
Erstellen Sie dann das InputImage
-Objekt mit dem Zwischenspeicher oder Array
Höhe, Breite, Farbcodierungsformat und Drehungsgrad:
Kotlin+KTX
val image = InputImage.fromByteBuffer( byteBuffer, /* 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 );
Mit einem Bitmap
Um ein InputImage
-Objekt aus einem
Bitmap
-Objekt die folgende Deklaration:
Kotlin+KTX
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Das Bild wird durch ein Bitmap
-Objekt in Verbindung mit Drehungsgrad dargestellt.
3. Bildlabeler ausführen
Wenn Sie Objekte in einem Bild mit Labels versehen möchten, übergeben Sie das image
-Objekt an die process()
-Methode des ImageLabeler
-Objekts.
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
// ...
}
});
Kotlin
labeler.process(image)
.addOnSuccessListener { labels ->
// Task completed successfully
// ...
}
.addOnFailureListener { e ->
// Task failed with an exception
// ...
}
4. Informationen zu Objekten mit Label abrufen
Wenn der Vorgang zur Labelerstellung für das Bild erfolgreich ist, wird eine Liste von ImageLabel
-Objekte an den Erfolgs-Listener übergeben. Jedes ImageLabel
-Objekt steht für
was im Bild beschriftet war. Sie können den Text der einzelnen Labels abrufen,
die Beschreibung, den Konfidenzwert der Übereinstimmung und den Index der Übereinstimmung.
Beispiel:
Java
for (ImageLabel label : labels) {
String text = label.getText();
float confidence = label.getConfidence();
int index = label.getIndex();
}
Kotlin
for (label in labels) {
val text = label.text
val confidence = label.confidence
val index = label.index
}
Tipps zum Verbessern der Leistung in Echtzeit
Wenn Sie Bilder in einer Echtzeitanwendung mit Labels versehen möchten, gehen Sie so vor: um optimale Framerates zu erzielen:
- Drosselung von Aufrufen an den Labelersteller für Bilder Wenn ein neuer Videoframe
wenn der Labelersteller ausgeführt wird, lassen Sie den Frame weg. Siehe
VisionProcessorBase
in der Beispielanwendung „Kurzanleitung“ finden Sie ein Beispiel. - Wenn Sie die Ausgabe des Bildlabelerstellers verwenden, um Grafiken
das Eingabebild, rufen Sie zuerst das Ergebnis ab und rendern Sie das Bild
in einem Schritt übereinanderlegen. Dadurch rendern Sie auf der Anzeigeoberfläche
für jeden Eingabe-Frame nur einmal. Siehe
CameraSourcePreview
undGraphicOverlay
-Klassen in der Schnellstart-Beispiel-App für ein Beispiel. -
Wenn Sie die Camera2 API verwenden, nehmen Sie Bilder in
ImageFormat.YUV_420_888
-Format.Wenn Sie die ältere Camera API verwenden, nehmen Sie Bilder in
ImageFormat.NV21
-Format.