Anleitung zum Einbetten von Bildern für Android

Mit der MediaPipe-Bildeinbettungsaufgabe können Sie Bilddaten in eine numerische Darstellung umwandeln, um ML-bezogene Bildverarbeitungsaufgaben auszuführen, z. B. die Ähnlichkeit von zwei Bildern zu vergleichen. In dieser Anleitung erfahren Sie, wie Sie den Bildeinbetter mit Android-Apps verwenden.

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

Codebeispiel

Der Beispielcode von MediaPipe Tasks ist eine einfache Implementierung einer App zur Bildeinbettung für Android. In diesem Beispiel wird die Kamera eines physischen Android-Geräts verwendet, um kontinuierlich Bilder einzubetten. Es kann auch der Einbettungsdienst für Bilddateien ausgeführt werden, die auf dem Gerät gespeichert sind.

Du kannst die App als Ausgangspunkt für deine eigene Android-App verwenden oder beim Ändern einer vorhandenen App darauf zurückgreifen. Der Beispielcode für den Bildeinbetter wird auf GitHub gehostet.

Code herunterladen

In der folgenden Anleitung erfahren Sie, wie Sie mit dem git-Befehlszeilentool 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. Konfigurieren Sie optional Ihre Git-Instanz für die Verwendung von Sparse Checkout, sodass Sie nur die Dateien für die Beispielanwendung „Image Embedder“ haben:
    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 finden Sie im Einrichtungsleitfaden für Android.

Schlüsselkomponenten

Die folgenden Dateien enthalten den wichtigen Code für diese Beispielanwendung zum Einbetten von Bildern:

  • ImageEmbedderHelper.kt: Initialisiert den Bildeinbetter und verarbeitet das Modell und die Auswahl.
  • MainActivity.kt: Implementiert die Anwendung und stellt die Komponenten der Benutzeroberfläche zusammen.

Einrichtung

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

Abhängigkeiten

Der Bildeinbetter verwendet die com.google.mediapipe:tasks-vision-Bibliothek. Füge diese Abhängigkeit in die Datei build.gradle deines Android-App-Entwicklungsprojekts ein. Importieren Sie die erforderlichen Abhängigkeiten mit dem folgenden Code:

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

Modell

Die MediaPipe-Bildeinbettungsaufgabe erfordert ein trainiertes Modell, das mit dieser Aufgabe kompatibel ist. Weitere Informationen zu verfügbaren trainierten Modellen für Bildeinbetter finden Sie in der Aufgabenübersicht im Abschnitt „Modelle“.

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

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

Geben Sie den Pfad des Modells innerhalb des Parameters 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 createFromOptions-Funktion verwenden, um die Aufgabe zu erstellen. Die Funktion createFromOptions akzeptiert Konfigurationsoptionen, um die Einbettungsoptionen festzulegen. Weitere Informationen zu Konfigurationsoptionen finden Sie unter Konfigurationsübersicht.

Die Aufgabe „Bild einbetten“ unterstützt drei Eingabedatentypen: Standbilder, Videodateien und Live-Videostreams. Beim Erstellen der Aufgabe müssen Sie den Ausführungsmodus angeben, der Ihrem Eingabedatentyp entspricht. Wählen Sie den Tab für Ihren Eingabedatentyp aus, um zu sehen, 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);
    

Die Implementierung des Beispielcodes ermöglicht es dem Nutzer, zwischen den Verarbeitungsmodi zu wechseln. Dadurch wird der Code zur Aufgabenerstellung komplizierter und ist für Ihren Anwendungsfall möglicherweise nicht geeignet. Sie finden diesen Code in der Funktion setupImageEmbedder() in der Datei ImageEmbedderHelper.kt.

Konfigurationsoptionen

Diese Aufgabe umfasst die folgenden Konfigurationsoptionen für Android-Apps:

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

IMAGE: Der Modus für Einzelbildeingaben.

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

LIVE_STREAM: Der Modus für einen Livestream der Eingabedaten, z. B. von einer Kamera. In diesem Modus muss resultListener aufgerufen werden, um einen Listener einzurichten, der die Ergebnisse asynchron empfängt.
{IMAGE, VIDEO, LIVE_STREAM} IMAGE
l2_normalize Gibt an, ob der zurückgegebene Featurevektor mit der L2-Norm normalisiert werden soll. Verwenden Sie diese Option nur, wenn das Modell noch keinen nativen L2_NORMALIZATION TFLite-Vorgang enthält. In den meisten Fällen ist dies bereits der Fall und die L2-Normalisierung wird daher durch TFLite-Inferenz erreicht, ohne dass diese Option erforderlich ist. Boolean False
quantize Gibt an, ob die zurückgegebene Einbettung über eine skalare Quantisierung in Byte quantisiert werden soll. Bei Einbettungen wird implizit davon ausgegangen, dass es sich um eine Einheitsnorm handelt. Daher hat jede Dimension garantiert einen Wert bei [-1,0, 1,0]. Sollte dies nicht der Fall sein, verwenden Sie die Option „l2_normalize“. Boolean False
resultListener Legt den Ergebnis-Listener so fest, dass er die Einbettungsergebnisse asynchron empfängt, wenn sich der Bildeinbetter im Livestreammodus befindet. Kann nur verwendet werden, wenn der Laufmodus auf LIVE_STREAM festgelegt ist Nicht festgelegt
errorListener Legt einen optionalen Fehler-Listener fest. Nicht festgelegt

Daten vorbereiten

Der Bildeinbetter funktioniert mit Bildern, Videodateien und Videos per Livestream. Die Aufgabe übernimmt die Vorverarbeitung der Dateneingabe, einschließlich Größenanpassung, Rotation und Wertnormalisierung.

Sie müssen das Eingabebild oder den Eingabeframe in ein com.google.mediapipe.framework.image.MPImage-Objekt konvertieren, bevor Sie es an die Aufgabe zum Einbinden von Bildern übergeben.

Bild

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

// Load an image on the user’s device as a Bitmap object using BitmapFactory.

// Convert an Android’s Bitmap object to a MediaPipe’s 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 video’s metadata, load the METADATA_KEY_DURATION and
// METADATA_KEY_VIDEO_FRAME_COUNT value. You’ll need them
// to calculate the timestamp of each frame later.

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

// Convert the Android’s Bitmap object to a MediaPipe’s 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 CameraX’s ImageAnalysis to continuously receive frames
// from the device’s camera. Configure it to output frames in RGBA_8888
// format to match with what is required by the model.

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

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

Task ausführen

Sie können die Funktion embed für Ihren Ausführungsmodus aufrufen, 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:

  • Im Video- oder Livestreammodus musst du auch den Zeitstempel des Eingabeframes für die Aufgabe zur Bildeinbettung bereitstellen.
  • Im Bild- oder Videomodus blockiert die Aufgabe „Bild einbetten“ den aktuellen Thread, bis die Verarbeitung des Eingabebilds oder ‐frames abgeschlossen ist. Damit der aktuelle Thread nicht blockiert wird, führen Sie die Verarbeitung in einem Hintergrundthread aus.
  • Im Livestreammodus blockiert die Aufgabe zur Bildeinbettung den aktuellen Thread nicht, sondern wird sofort zurückgegeben. Jedes Mal, wenn ein Eingabeframe verarbeitet wurde, ruft er seinen Ergebnis-Listener mit dem Erkennungsergebnis auf. Wenn die Funktion embedAsync aufgerufen wird, während die Aufgabe des Bildeinbetters mit der Verarbeitung eines weiteren Frames beschäftigt ist, ignoriert die Aufgabe den neuen Eingabeframe.

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

Ergebnisse verarbeiten und anzeigen

Beim Ausführen der Inferenz gibt die Aufgabe zur Bildeinbettung ein ImageEmbedderResult-Objekt zurück, das eine Liste von Einbettungen (entweder Gleitkomma- oder skalarquantisiert) für das Eingabebild enthält.

Im Folgenden sehen 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 Einbetten des folgenden Bildes erzielt:

Mit der Funktion ImageEmbedder.cosineSimilarity können Sie die Ähnlichkeit von zwei Einbettungen vergleichen. Im folgenden Code sehen Sie ein Beispiel.

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