Anleitung zum Einbetten von Bildern für Android

Mit der MediaPipe-Aufgabe „Image Embedder“ können Sie Bilddaten in eine numerische Darstellung umwandeln, um ML-bezogene Bildverarbeitungsaufgaben auszuführen, z. B. den Vergleich der Ähnlichkeit zweier Bilder. In dieser Anleitung wird beschrieben, wie Sie den Bild-Embedder mit Android-Apps verwenden.

Weitere Informationen zu den Funktionen, Modellen und Konfigurationsoptionen dieser Aufgabe finden Sie in der Übersicht.

Codebeispiel

Der Beispielcode für MediaPipe Tasks ist eine einfache Implementierung einer Bild-Embedder-App für Android. Im Beispiel wird die Kamera eines physischen Android-Geräts verwendet, um Bilder kontinuierlich einzubetten. Der Embedder kann auch auf Bilddateien ausgeführt werden, die auf dem Gerät gespeichert sind.

Sie können die App als Ausgangspunkt für Ihre eigene Android-App verwenden oder sich an ihr orientieren, wenn Sie eine vorhandene App ändern. Der Beispielcode für den Image Embedder wird auf GitHub gehostet.

Code herunterladen

In der folgenden Anleitung wird beschrieben, wie Sie mit dem Befehlszeilentool git eine lokale Kopie des Beispielcodes erstellen.

So laden Sie den Beispielcode herunter:

  1. Klonen Sie das Git-Repository mit dem folgenden Befehl:
    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. Optional können Sie Ihre Git-Instanz so konfigurieren, dass eine spärliche Überprüfung verwendet wird, sodass nur die Dateien für die Beispielanwendung „Image Embedder“ vorhanden sind:
    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_embedder/android
    

Nachdem Sie eine lokale Version des Beispielcodes erstellt haben, können Sie das Projekt in Android Studio importieren und die App ausführen. Eine Anleitung dazu finden Sie im Einrichtungsleitfaden für Android.

Schlüsselkomponenten

Die folgenden Dateien enthalten den wichtigsten Code für diese Beispielanwendung für den Bild-Embedder:

  • ImageEmbedderHelper.kt: Hier wird der Bild-Embedder initialisiert und die Modell- und Delegate-Auswahl verarbeitet.
  • MainActivity.kt: Hier wird die Anwendung implementiert und die Komponenten der Benutzeroberfläche werden zusammengesetzt.

Einrichtung

In diesem Abschnitt werden die wichtigsten Schritte zum Einrichten Ihrer Entwicklungsumgebung und Codeprojekte für die Verwendung des Bild-Embedders beschrieben. Allgemeine Informationen zum Einrichten Ihrer Entwicklungsumgebung für die Verwendung von MediaPipe-Aufgaben, einschließlich Anforderungen an die Plattformversion, finden Sie im Einrichtungsleitfaden für Android.

Abhängigkeiten

Für den Bild-Embedder wird die com.google.mediapipe:tasks-vision-Bibliothek verwendet. Fügen Sie diese Abhängigkeit der Datei build.gradle Ihres Android-App-Entwicklungsprojekts hinzu. Importieren Sie die erforderlichen Abhängigkeiten mit dem folgenden Code:

dependencies {
    ...
    implementation 'com.google.mediapipe:tasks-vision:latest.release'
}

Modell

Für die MediaPipe-Aufgabe „Image Embedder“ ist ein trainiertes Modell erforderlich, das mit dieser Aufgabe kompatibel ist. Weitere Informationen zu verfügbaren trainierten Modellen für den Bild-Embedder finden Sie in der Aufgabenübersicht im Abschnitt „Modelle“.

Wählen Sie das Modell aus, laden Sie es herunter und speichern Sie es in Ihrem Projektverzeichnis:

<dev-project-root>/src/main/assets

Geben Sie den Pfad des Modells im Parameter ModelAssetPath an. Im Beispielcode wird das Modell in der Funktion setupImageEmbedder() in der Datei ImageEmbedderHelper.kt definiert:

Verwenden Sie die Methode BaseOptions.Builder.setModelAssetPath(), um den vom Modell verwendeten Pfad anzugeben. Auf diese Methode wird im Codebeispiel im nächsten Abschnitt verwiesen.

Aufgabe erstellen

Sie können die Aufgabe mit der Funktion createFromOptions erstellen. Die Funktion createFromOptions akzeptiert Konfigurationsoptionen, um die Optionen für den Einbettungscode festzulegen. Weitere Informationen zu den Konfigurationsoptionen finden Sie unter Konfiguration – Übersicht.

Die Aufgabe „Image Embedder“ unterstützt drei Eingabedatentypen: Standbilder, Videodateien und Livestreams. Sie müssen beim Erstellen der Aufgabe den Ausführungsmodus angeben, der dem Datentyp der Eingabe entspricht. Wählen Sie den Tab für den Datentyp Ihrer Eingabedaten aus, um zu erfahren, wie Sie die Aufgabe erstellen und die Inferenz ausführen.

Bild

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.IMAGE)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

Video

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.VIDEO)
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

Livestream

ImageEmbedderOptions options =
  ImageEmbedderOptions.builder()
    .setBaseOptions(
      BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setQuantize(true)
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setResultListener((result, inputImage) -> {
         // Process the embedding result here.
    })
    .build();
imageEmbedder = ImageEmbedder.createFromOptions(context, options);
    

Mit der Beispielcodeimplementierung kann der Nutzer zwischen den Verarbeitungsmodi wechseln. Dieser Ansatz macht den Code zum Erstellen von Aufgaben komplizierter und ist möglicherweise nicht für Ihren Anwendungsfall geeignet. Diesen Code findest du in der Funktion setupImageEmbedder() in der Datei ImageEmbedderHelper.kt.

Konfigurationsoptionen

Für diese Aufgabe sind die folgenden Konfigurationsoptionen für Android-Apps verfügbar:

Option Beschreibung Wertebereich Standardwert
runningMode Legt den Ausführungsmodus für die Aufgabe fest. Es gibt drei Modi:

IMAGE: Der Modus für Eingaben mit einem einzelnen Bild.

VIDEO: Der Modus für decodierte Frames eines Videos.

LIVE_STREAM: Der Modus für einen Livestream von Eingabedaten, z. B. von einer Kamera. In diesem Modus muss resultListener aufgerufen werden, um einen Listener für den asynchronen Empfang von Ergebnissen einzurichten.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
l2_normalize Gibt an, ob der zurückgegebene Feature-Vektor mit der L2-Norm normalisiert werden soll. Verwenden Sie diese Option nur, wenn das Modell noch keine native TFLite-Operation vom Typ „L2_NORMALIZATION“ enthält. In den meisten Fällen ist das bereits der Fall und die L2-Normalisierung wird durch TFLite-Inferenz erreicht, sodass diese Option nicht erforderlich ist. Boolean False
quantize Gibt an, ob die zurückgegebene Einbettung über die Skalarquantisierung in Bytes quantisiert werden soll. Für Einbettungen wird implizit davon ausgegangen, dass sie normiert sind. Daher hat jede Dimension garantiert einen Wert im Bereich [−1,0; 1,0]. Verwenden Sie die Option „l2_normalize“, wenn dies nicht der Fall ist. Boolean False
resultListener Legt fest, dass der Ergebnisempfänger die Einbettungsergebnisse asynchron empfängt, wenn sich der Bild-Embedder im Livestream-Modus befindet. Kann nur verwendet werden, wenn der Ausführungsmodus auf LIVE_STREAM festgelegt ist. Nicht festgelegt
errorListener Legt einen optionalen Fehler-Listener fest. Nicht festgelegt

Daten vorbereiten

Der Bild-Embedder funktioniert mit Bildern, Videodateien und Live-Streams. Die Aufgabe übernimmt die Vorverarbeitung der Dateneingabe, einschließlich Größenänderung, Drehung und Wertnormalisierung.

Sie müssen das Eingabebild oder den Frame in ein com.google.mediapipe.framework.image.MPImage-Objekt konvertieren, bevor Sie es an die Aufgabe „Image Embedder“ übergeben.

Bild

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load an image on the users device as a Bitmap object using BitmapFactory.

// Convert an Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(bitmap).build();
    

Video

import com.google.mediapipe.framework.image.BitmapImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Load a video file on the user's device using MediaMetadataRetriever

// From the videos metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. Youll need them
// to calculate the timestamp of each frame later.

// Loop through the video and load each frame as a Bitmap object.

// Convert the Androids Bitmap object to a MediaPipes Image object.
Image mpImage = new BitmapImageBuilder(frame).build();
    

Livestream

import com.google.mediapipe.framework.image.MediaImageBuilder;
import com.google.mediapipe.framework.image.MPImage;

// Create a CameraXs ImageAnalysis to continuously receive frames
// from the devices camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

// For each Androids ImageProxy object received from the ImageAnalysis,
// extract the encapsulated Androids Image object and convert it to
// a MediaPipes Image object.
android.media.Image mediaImage = imageProxy.getImage()
Image mpImage = new MediaImageBuilder(mediaImage).build();
    

Im Beispielcode wird die Datenvorbereitung in der Datei ImageEmbedderHelper.kt ausgeführt.

Aufgabe ausführen

Sie können die embed-Funktion aufrufen, die dem aktuellen Ausführungsmodus entspricht, um Inferenzen auszulösen. Die Image Embedder API gibt die Einbettungsvektoren für das Eingabebild oder den Eingabeframe zurück.

Bild

ImageEmbedderResult embedderResult = imageEmbedder.embed(image);
    

Video

// Calculate the timestamp in milliseconds of the current frame.
long frame_timestamp_ms = 1000 * video_duration * frame_index / frame_count;

// Run inference on the frame.
ImageEmbedderResult embedderResult =
    imageEmbedder.embedForVideo(image, frameTimestampMs);
    

Livestream

// Run inference on the frame. The embedding results will be available
// via the `resultListener` provided in the `ImageEmbedderOptions` when
// the image embedder was created.
imageEmbedder.embedAsync(image, frameTimestampMs);
    

Wichtige Hinweise:

  • Wenn Sie die Funktion im Video- oder Livestream-Modus ausführen, müssen Sie der Aufgabe „Image Embedder“ auch den Zeitstempel des Eingabeframes angeben.
  • Wenn die Ausführung im Bild- oder Videomodus erfolgt, blockiert die Aufgabe „Image Embedder“ den aktuellen Thread, bis die Verarbeitung des Eingabebilds oder ‑frames abgeschlossen ist. Führen Sie die Verarbeitung in einem Hintergrund-Thread aus, um den aktuellen Thread nicht zu blockieren.
  • Wenn die Ausführung im Livestream-Modus erfolgt, blockiert die Aufgabe „Image Embedder“ den aktuellen Thread nicht, sondern gibt sofort eine Rückgabe zurück. Jedes Mal, wenn die Verarbeitung eines Eingabeframes abgeschlossen ist, wird der Ergebnis-Listener mit dem Erkennungsergebnis aufgerufen. Wenn die embedAsync-Funktion aufgerufen wird, während die Aufgabe „Image Embedder“ gerade einen anderen Frame verarbeitet, wird der neue Eingabeframe ignoriert.

Im Beispielcode ist die Funktion embed in der Datei ImageEmbedderHelper.kt definiert.

Ergebnisse verarbeiten und anzeigen

Nach der Ausführung der Inferenz gibt die Aufgabe „Image Embedder“ ein ImageEmbedderResult-Objekt zurück, das eine Liste von Einbettungen (entweder Gleitkomma- oder skalarquantifiziert) für das Eingabebild enthält.

Im Folgenden finden Sie ein Beispiel für die Ausgabedaten dieser Aufgabe:

ImageEmbedderResult:
  Embedding #0 (sole embedding head):
    float_embedding: {0.0, 0.0, ..., 0.0, 1.0, 0.0, 0.0, 2.0}
    head_index: 0

Dieses Ergebnis wurde durch das Einbetten des folgenden Bildes erzielt:

Halbnahe Aufnahme einer exotischen Katze

Mit der Funktion ImageEmbedder.cosineSimilarity können Sie die Ähnlichkeit zweier Embeddings vergleichen. Im folgenden Code finden Sie ein Beispiel.

// Compute cosine similarity.
double similarity = ImageEmbedder.cosineSimilarity(
  result.embeddingResult().embeddings().get(0),
  otherResult.embeddingResult().embeddings().get(0));