Bilder mit einem mit AutoML trainierten Modell unter Android mit Labels versehen

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
  • Das Modell ist Teil des APK deiner App
  • Das Modell ist sofort verfügbar, auch wenn das Android-Gerät offline ist
  • Kein Firebase-Projekt erforderlich
Mit Firebase gehostet
  • Hosten Sie das Modell, indem Sie es auf Firebase Machine Learning
  • Verringert die APK-Größe
  • Das Modell wird on demand heruntergeladen
  • Modellaktualisierungen senden, ohne die App neu zu veröffentlichen
  • Einfache A/B-Tests mit Firebase Remote Config
  • Firebase-Projekt erforderlich

Hinweis

  1. 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'
    }
    
  2. 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:

  1. 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).

  2. Fügen Sie das Modell und die zugehörigen Metadatendateien in das Anwendungspaket ein:

    1. 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“
    2. Erstellen Sie einen Unterordner unter dem Assets-Ordner, in dem das Modell gespeichert wird -Dateien.
    3. Kopieren Sie die Dateien model.tflite, dict.txt und manifest.json in den Unterordner (alle drei Dateien müssen sich im im selben Ordner).
  3. 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.

  4. 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 und GraphicOverlay-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.