Mit ML Kit können Sie Entitäten in einem Bild erkennen und mit Labels versehen. Diese API unterstützt eine Vielzahl benutzerdefinierter Bildklassifizierungsmodelle. Weitere Informationen zu den Anforderungen an die Modellkompatibilität, zur Suche nach vortrainierten Modellen und zum Trainieren Ihrer eigenen Modelle finden Sie unter Benutzerdefinierte Modelle mit ML Kit.
Es gibt zwei Möglichkeiten, Image-Labels in benutzerdefinierte Modelle zu integrieren: durch Bündeln der Pipeline als Teil Ihrer Anwendung oder durch Verwenden einer Pipeline ohne Bundle, die von den Google Play-Diensten abhängig ist. Wenn Sie die entbündelte Pipeline auswählen, ist Ihre Anwendung kleiner. Weitere Details finden Sie in der Tabelle unten.
Gebündelt | Nicht gebündelt | |
---|---|---|
Name der Bibliothek | com.google.mlkit:image-labeling-custom | com.google.android.gms:play-services-mlkit-image-labeling-custom |
Implementierung | Die Pipeline ist bei der Erstellung statisch mit Ihrer Anwendung verknüpft. | Die Pipeline wird dynamisch über die Google Play-Dienste heruntergeladen. |
App-Größe | Ca.3,8 MB größer. | Ca. 200 KB |
Initialisierungszeit | Pipeline ist sofort verfügbar. | Möglicherweise muss vor der ersten Verwendung auf den Download der Pipeline gewartet werden. |
API-Lebenszyklusphase | General Availability (GA) | Beta |
Es gibt zwei Möglichkeiten, ein benutzerdefiniertes Modell zu integrieren: bündeln Sie das Modell, indem Sie es in den Asset-Ordner Ihrer App verschieben, oder laden Sie es dynamisch aus Firebase herunter. In der folgenden Tabelle werden diese beiden Optionen verglichen.
Gebündeltes Modell | Gehostetes Modell |
---|---|
Das Modell ist Teil des APK Ihrer App, das seine Größe vergrößert. | Das Modell ist nicht Teil Ihres APK. Sie wird durch Hochladen in Firebase Machine Learning gehostet. |
Das Modell ist sofort verfügbar, auch wenn das Android-Gerät offline ist | Das Modell wird bei Bedarf heruntergeladen |
Kein Firebase-Projekt erforderlich | Firebase-Projekt erforderlich |
Du musst deine App noch einmal veröffentlichen, um das Modell zu aktualisieren | Modellaktualisierungen ohne erneute Veröffentlichung Ihrer App veröffentlichen |
Keine integrierten A/B-Tests | Einfache A/B-Tests mit Firebase Remote Config |
Testen
- Ein Beispiel für die Verwendung des gebündelten Modells finden Sie in der Vision-Kurzanleitungs-App. Die Verwendung des gehosteten Modells wird in der Kurzanleitung für AutoML ML veranschaulicht.
Hinweis
Achten Sie darauf, dass Sie in der Datei
build.gradle
auf Projektebene das Maven-Repository von Google in die Abschnittebuildscript
undallprojects
aufnehmen.Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken in die Gradle-Datei des Moduls auf App-Ebene ein. Diese ist normalerweise
app/build.gradle
. Wählen Sie je nach Ihren Anforderungen eine der folgenden Abhängigkeiten aus:So bündeln Sie die Pipeline mit Ihrer Anwendung:
dependencies { // ... // Use this dependency to bundle the pipeline with your app implementation 'com.google.mlkit:image-labeling-custom:17.0.1' }
So verwenden Sie die Pipeline in Google Play-Diensten:
dependencies { // ... // Use this dependency to use the dynamically downloaded pipeline in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta4' }
Wenn Sie die Pipeline in den Google Play-Diensten verwenden, können Sie Ihre App so konfigurieren, dass sie automatisch auf das Gerät heruntergeladen wird, nachdem sie aus dem Play Store installiert wurde. Fügen Sie dazu der Datei
AndroidManifest.xml
Ihrer Anwendung die folgende Deklaration hinzu:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="custom_ica" /> <!-- To use multiple downloads: android:value="custom_ica,download2,download3" --> </application>
Sie können die Verfügbarkeit der Pipeline auch explizit überprüfen und den Download über die ModuleInstallClient API der Google Play-Dienste anfordern.
Wenn Sie die Pipelineinstallationsdownloads nicht aktivieren oder einen expliziten Download anfordern, wird die Pipeline heruntergeladen, wenn Sie den Labelersteller das erste Mal ausführen. Anfragen, die vor dem Download eingehen, führen zu keinem Ergebnis.
Fügen Sie die Abhängigkeit
linkFirebase
hinzu, wenn Sie ein Modell dynamisch aus Firebase herunterladen möchten:Fügen Sie die Abhängigkeit
linkFirebase
hinzu, um ein Modell dynamisch aus Firebase herunterzuladen:dependencies { // ... // Image labeling feature with model downloaded from Firebase implementation 'com.google.mlkit:image-labeling-custom:17.0.1' // Or use the dynamically downloaded pipeline in Google Play Services // implementation 'com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta4' implementation 'com.google.mlkit:linkfirebase:17.0.0' }
Wenn Sie ein Modell herunterladen möchten, müssen Sie Ihrem Android-Projekt Firebase hinzufügen, falls Sie dies noch nicht getan haben. Dies ist nicht erforderlich, wenn Sie das Modell bündeln.
1. Modell laden
Lokale Modellquelle konfigurieren
So bündeln Sie das Modell mit Ihrer App:
Kopieren Sie die Modelldatei (normalerweise mit
.tflite
oder.lite
) in den Ordnerassets/
Ihrer Anwendung. Möglicherweise müssen Sie den Ordner zuerst erstellen, indem Sie mit der rechten Maustaste auf den Ordnerapp/
klicken und dann auf Neu > Ordner > Asset-Ordner klicken.Fügen Sie dann der Datei
build.gradle
Ihrer Anwendung Folgendes hinzu, damit Gradle beim Erstellen der Anwendung nicht die Modelldatei komprimiert:android { // ... aaptOptions { noCompress "tflite" // or noCompress "lite" } }
Die Modelldatei wird im App-Paket enthalten und für ML Kit als Roh-Asset verfügbar sein.
Erstellen Sie das Objekt
LocalModel
und geben Sie den Pfad zur Modelldatei an:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build()
Java
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build();
Von Firebase gehostete Modellquelle konfigurieren
Wenn Sie das remote gehostete Modell verwenden möchten, erstellen Sie mit FirebaseModelSource
ein RemoteModel
-Objekt und geben Sie 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 = CustomRemoteModel .Builder(FirebaseModelSource.Builder("your_model_name").build()) .build()
Java
// Specify the name you assigned in the Firebase console. CustomRemoteModel remoteModel = new CustomRemoteModel .Builder(new FirebaseModelSource.Builder("your_model_name").build()) .build();
Starten Sie dann die Aufgabe zum Modelldownload 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, lädt die Aufgabe das Modell asynchron aus Firebase herunter:
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 in ihrem Initialisierungscode. Sie können dies jedoch jederzeit tun, bevor Sie das Modell verwenden müssen.
Image-Labelersteller konfigurieren
Nachdem Sie die Modellquellen konfiguriert haben, erstellen Sie aus einer dieser Quellen ein ImageLabeler
-Objekt.
Folgende Optionen sind verfügbar:
Optionen | |
---|---|
confidenceThreshold
|
Minimaler Konfidenzwert der erkannten Labels. Wenn nichts anderes festgelegt ist, wird jeder Klassifikatorgrenzwert verwendet, der in den Metadaten des Modells angegeben ist. Wenn das Modell keine Metadaten enthält oder die Metadaten keinen Klassifikatorschwellenwert angeben, wird der Standardgrenzwert 0,0 verwendet. |
maxResultCount
|
Maximale Anzahl der Labels, die zurückgegeben werden sollen. Wenn nicht festgelegt, wird der Standardwert 10 verwendet. |
Wenn Sie nur ein lokal gebündeltes Modell haben, erstellen Sie einfach einen Labelersteller aus Ihrem LocalModel
-Objekt:
Kotlin
val customImageLabelerOptions = CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(customImageLabelerOptions)
Java
CustomImageLabelerOptions customImageLabelerOptions = new CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(customImageLabelerOptions);
Wenn Sie ein remote 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 zwar nur vor dem Ausführen des Labelerstellers bestätigen, aber wenn Sie sowohl ein remote gehostetes Modell als auch ein lokal gebündeltes Modell haben, kann es sinnvoll sein, diese Prüfung bei der Instanziierung des Bild-Labelerstellers durchzuführen: Erstellen Sie einen Labeler für das Remote-Modell, wenn es heruntergeladen wurde, oder aus dem lokalen Modell.
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener { isDownloaded -> val optionsBuilder = if (isDownloaded) { CustomImageLabelerOptions.Builder(remoteModel) } else { CustomImageLabelerOptions.Builder(localModel) } val options = optionsBuilder .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(options) }
Java
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener(new OnSuccessListener() { @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.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(options); } });
Wenn Sie nur ein remote gehostetes Modell haben, sollten Sie modellbezogene Funktionen deaktivieren, z. B. einen Teil Ihrer UI ausblenden oder ausblenden, bis Sie bestätigt haben, dass das Modell heruntergeladen wurde. Hängen Sie dazu 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, einInputImage
-Objekt aus dem Bild. Der Image-Labelersteller wird am schnellsten ausgeführt, wenn Sie eine Bitmap
oder, wenn Sie die Camera2 API verwenden, eine YUV_420_888-media.Image
verwenden, die nach Möglichkeit empfohlen wird.
Sie können ein InputImage
-Objekt aus verschiedenen Quellen erstellen. Dies wird im Folgenden 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 von der Kamera eines Geräts aufnehmen, übergeben Sie das Objekt media.Image
und die Rotation des Bildes an InputImage.fromMediaImage()
.
Wenn Sie die KameraX-Bibliothek verwenden, berechnen die Klassen OnImageCapturedListener
und ImageAnalysis.Analyzer
den Rotationswert für Sie.
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 Ihnen den Grad der Drehung des Bildes angibt, können Sie ihn anhand des Grads der Drehung 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 Objekt media.Image
und den Rotationsgradwert an InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Datei-URI verwenden
Übergeben Sie den Anwendungskontext und den Datei-URI an InputImage.fromFilePath()
, um ein InputImage
-Objekt aus einem Datei-URI zu erstellen. Dies ist nützlich, wenn Sie den Intent ACTION_GET_CONTENT
verwenden, um den Nutzer aufzufordern, ein Bild aus der 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(); }
Mit ByteBuffer
oder ByteArray
Berechnen Sie zum Erstellen eines InputImage
-Objekts aus einem ByteBuffer
oder einem ByteArray
zuerst den Grad der Bilddrehung, wie zuvor für die media.Image
-Eingabe beschrieben.
Erstellen Sie dann das Objekt InputImage
mit dem Zwischenspeicher oder Array, zusammen mit 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
Erstellen Sie zum Anlegen eines InputImage
-Objekts aus einem Bitmap
-Objekt die folgende Deklaration:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Das Bild wird durch ein Bitmap
-Objekt zusammen mit einem Rotationsgrad dargestellt.
3. Image-Labelersteller ausführen
Übergeben Sie das Objekt image
an die Methode process()
von ImageLabeler
, um Objekte in einem Bild mit Labels zu versehen.
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 Elementen mit Labels abrufen
Wenn das Labeling des Bildes erfolgreich ist, wird eine Liste der Objekte vom TypImageLabel
an den Erfolgs-Listener übergeben. Jedes ImageLabel
-Objekt stellt ein Element dar, das im Bild mit einem Label versehen wurde. Sie können die Textbeschreibung jedes Labels (sofern in den Metadaten der TensorFlow Lite-Modelldatei verfügbar), den Konfidenzwert und den Index 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, folgen Sie diesen Richtlinien, um die besten Frame-Rates zu erzielen:
- Wenn Sie die API
Camera
odercamera2
verwenden, drosseln Sie Aufrufe an den Labelersteller von Bildern. Wenn während der Ausführung des Labelerstellers ein neuer Videoframe verfügbar ist, lassen Sie den Frame los. Ein Beispiel finden Sie in der Kurzanleitungs-Beispielanwendung in der KlasseVisionProcessorBase
. - Achten Sie bei Verwendung der
CameraX
API darauf, dass die Rückdruckstrategie auf den StandardwertImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
festgelegt ist. Dadurch wird garantiert, dass jeweils nur ein Bild zur Analyse übermittelt wird. Wenn das Analyseprogramm ausgelastet ist, werden mehr Bilder erstellt, damit sie nicht zur Auslieferung in die Warteschlange gestellt werden. Sobald das analysierte Bild durch Aufrufen von ImageProxy.close() geschlossen wird, wird das nächste neueste Bild gesendet. - Wenn Sie die Ausgabe des Bild-Labelers verwenden, um Grafiken auf dem Eingabebild einzublenden, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern dann das Bild und das Overlay in einem einzigen Schritt. Wird für jeden Eingabeframe nur einmal auf der Anzeigeoberfläche gerendert. Ein Beispiel findest du in den Beispielklassen
CameraSourcePreview
undGraphicOverlay
. - Wenn Sie die Camera2 API verwenden, nehmen Sie Bilder im Format
ImageFormat.YUV_420_888
auf. Wenn Sie die ältere Camera API verwenden, nehmen Sie Bilder im FormatImageFormat.NV21
auf.