Hello World! auf Android

Einleitung

In dieser „Hello World!“-Anleitung wird das MediaPipe-Framework verwendet, um eine Android-Anwendung zu entwickeln, die ein MediaPipe-Diagramm unter Android ausführt.

Umfang

Eine einfache Kamera-App zur Echtzeiterkennung von Sobel Edge, die auf einen Live-Videostream auf einem Android-Gerät angewendet wird.

edge_detection_android_gpu_gif

Einrichtung

  1. Installieren Sie das MediaPipe-Framework auf Ihrem System. Weitere Informationen finden Sie in der Framework-Installationsanleitung.
  2. Installieren Sie das Android Development SDK und Android NDK. Eine entsprechende Anleitung finden Sie auch im [Framework-Installationshandbuch].
  3. Aktivieren Sie die Entwickleroptionen auf Ihrem Android-Gerät.
  4. Richten Sie Bazel auf Ihrem System ein, um die Android-App zu erstellen und bereitzustellen.

Grafik für Randerkennung

Wir verwenden das folgende Diagramm edge_detection_mobile_gpu.pbtxt:

# MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
# Used in the examples in
# mediapipe/examples/android/src/java/com/mediapipe/apps/basic and
# mediapipe/examples/ios/edgedetectiongpu.

# Images coming into and out of the graph.
input_stream: "input_video"
output_stream: "output_video"

# Converts RGB images into luminance images, still stored in RGB format.
node: {
  calculator: "LuminanceCalculator"
  input_stream: "input_video"
  output_stream: "luma_video"
}

# Applies the Sobel filter to luminance images stored in RGB format.
node: {
  calculator: "SobelEdgesCalculator"
  input_stream: "luma_video"
  output_stream: "output_video"
}

Hier eine Visualisierung der Grafik:

edge_detection_mobile_gpu

Dieses Diagramm enthält einen einzelnen Eingabestream mit dem Namen input_video für alle eingehenden Frames, die von der Kamera Ihres Geräts bereitgestellt werden.

Der erste Knoten im Diagramm, LuminanceCalculator, verwendet ein einzelnes Paket (Bildframe) und wendet eine Änderung der Helligkeit mithilfe eines OpenGL-Shaders an. Der resultierende Bildframe wird an den luma_video-Ausgabestream gesendet.

Der zweite Knoten, SobelEdgesCalculator, wendet die Edge-Erkennung auf eingehende Pakete im Stream luma_video an und gibt den Ausgabestream output_video aus.

Unsere Android-Anwendung zeigt die Ausgabe-Frames des output_video-Streams an.

Anfängliche minimale Anwendungseinrichtung

Wir beginnen mit einer einfachen Android-Anwendung, bei der „Hello World!“ auf dem Bildschirm angezeigt wird. Sie können diesen Schritt überspringen, wenn Sie mit dem Erstellen von Android-Apps mit bazel vertraut sind.

Erstellen Sie ein neues Verzeichnis, in dem Sie Ihre Android-App erstellen werden. Den vollständigen Code dieser Anleitung finden Sie beispielsweise unter mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic. Er wird im gesamten Codelab als $APPLICATION_PATH bezeichnet.

Beachten Sie im Pfad zur Anwendung Folgendes:

  • Die Anwendung heißt helloworld.
  • $PACKAGE_PATH der Anwendung ist com.google.mediapipe.apps.basic. Dieser wird in dieser Anleitung in Code-Snippets verwendet. Denken Sie daher daran, beim Kopieren und Verwenden der Code-Snippets Ihren eigenen $PACKAGE_PATH zu verwenden.

Fügen Sie $APPLICATION_PATH/res/layout eine Datei vom Typ „activity_main.xml“ hinzu. Daraufhin wird im Vollbildmodus der Anwendung ein TextView mit dem String Hello World! angezeigt:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Fügen Sie $APPLICATION_PATH ein einfaches MainActivity.java hinzu, um den Inhalt des activity_main.xml-Layouts zu laden, wie unten gezeigt:

package com.google.mediapipe.apps.basic;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

/** Bare-bones main activity. */
public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
}

Fügen Sie $APPLICATION_PATH die Manifestdatei AndroidManifest.xml hinzu, die beim Start der Anwendung MainActivity startet:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.mediapipe.apps.basic">

  <uses-sdk
      android:minSdkVersion="19"
      android:targetSdkVersion="19" />

  <application
      android:allowBackup="true"
      android:label="${appName}"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
      <activity
          android:name="${mainActivity}"
          android:exported="true"
          android:screenOrientation="portrait">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
  </application>

</manifest>

In unserer Anwendung verwenden wir ein Theme.AppCompat-Design, also benötigen wir geeignete Theme-Verweise. Fügen Sie colors.xml zu $APPLICATION_PATH/res/values/ hinzu:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>

Fügen Sie styles.xml zu $APPLICATION_PATH/res/values/ hinzu:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

Fügen Sie zum Erstellen der Anwendung eine BUILD-Datei zu $APPLICATION_PATH hinzu. ${appName} und ${mainActivity} im Manifest werden dann durch Strings ersetzt, die in BUILD angegeben werden (siehe unten).

android_library(
    name = "basic_lib",
    srcs = glob(["*.java"]),
    manifest = "AndroidManifest.xml",
    resource_files = glob(["res/**"]),
    deps = [
        "//third_party:android_constraint_layout",
        "//third_party:androidx_appcompat",
    ],
)

android_binary(
    name = "helloworld",
    manifest = "AndroidManifest.xml",
    manifest_values = {
        "applicationId": "com.google.mediapipe.apps.basic",
        "appName": "Hello World",
        "mainActivity": ".MainActivity",
    },
    multidex = "native",
    deps = [
        ":basic_lib",
    ],
)

Die Regel android_library fügt Abhängigkeiten für MainActivity, Ressourcendateien und AndroidManifest.xml hinzu.

Die Regel android_binary verwendet die Android-Bibliothek basic_lib, die generiert wird, um ein binäres APK für die Installation auf Ihrem Android-Gerät zu erstellen.

Erstellen Sie die Anwendung mit dem folgenden Befehl:

bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld

Installiere die generierte APK-Datei mit adb install. Beispiel:

adb install bazel-bin/$APPLICATION_PATH/helloworld.apk

Öffnen Sie die App auf Ihrem Gerät. Es sollte ein Bildschirm mit dem Text Hello World! angezeigt werden.

bazel_hello_world_android

Kamera über CameraX verwenden

Kameraberechtigungen

Um die Kamera in unserer Anwendung zu verwenden, müssen wir den Nutzer um Zugriff auf die Kamera bitten. Wenn Sie Kameraberechtigungen anfordern möchten, fügen Sie Folgendes zu AndroidManifest.xml hinzu:

<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

Ändern Sie in derselben Datei die SDK-Mindestversion in 21 und die SDK-Zielversion in 27:

<uses-sdk
    android:minSdkVersion="21"
    android:targetSdkVersion="27" />

Der Nutzer wird dann aufgefordert, die Kameraberechtigung anzufordern, und wir können die Bibliothek CameraX für den Kamerazugriff verwenden.

Kameraberechtigungen können über das Dienstprogramm PermissionHelper angefordert werden, das von den MediaPipe Framework-Komponenten bereitgestellt wird. Wenn Sie sie verwenden möchten, fügen Sie in der Regel mediapipe_lib in BUILD die Abhängigkeit "//mediapipe/java/com/google/mediapipe/components:android_components" hinzu.

Wenn Sie PermissionHelper in MainActivity verwenden möchten, fügen Sie der Funktion onCreate die folgende Zeile hinzu:

PermissionHelper.checkAndRequestCameraPermissions(this);

Daraufhin wird der Nutzer über ein Dialogfeld auf dem Bildschirm aufgefordert, Berechtigungen zur Verwendung der Kamera in dieser Anwendung anzufordern.

Fügen Sie den folgenden Code hinzu, um die Nutzerantwort zu verarbeiten:

@Override
public void onRequestPermissionsResult(
    int requestCode, String[] permissions, int[] grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
  PermissionHelper.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

@Override
protected void onResume() {
  super.onResume();
  if (PermissionHelper.cameraPermissionsGranted(this)) {
    startCamera();
  }
}

public void startCamera() {}

Lassen Sie die Methode startCamera() vorerst leer. Wenn der Nutzer auf die Aufforderung reagiert, wird MainActivity fortgesetzt und onResume() wird aufgerufen. Mit dem Code wird bestätigt, dass die Berechtigungen zur Verwendung der Kamera gewährt wurden, und startet dann die Kamera.

Erstellen Sie die Anwendung neu und installieren Sie sie. Sie sollten nun eine Aufforderung sehen, in der Sie für die Anwendung Zugriff auf die Kamera anfordern.

Kamerazugriff

Wenn Kameraberechtigungen verfügbar sind, können wir Frames starten und von der Kamera abrufen.

Um die Frames von der Kamera zu betrachten, verwenden wir ein SurfaceView. Jeder Frame der Kamera wird in einem SurfaceTexture-Objekt gespeichert. Dazu müssen wir zuerst das Layout der App ändern.

Entfernen Sie den gesamten TextView-Codeblock aus $APPLICATION_PATH/res/layout/activity_main.xml und fügen Sie stattdessen den folgenden Code hinzu:

<FrameLayout
    android:id="@+id/preview_display_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1">
    <TextView
        android:id="@+id/no_camera_access_view"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:gravity="center"
        android:text="@string/no_camera_access" />
</FrameLayout>

Dieser Codeblock enthält ein neues FrameLayout namens preview_display_layout und ein darin verschachteltes TextView namens no_camera_access_preview. Wenn Berechtigungen für den Kamerazugriff nicht gewährt werden, zeigt unsere Anwendung das TextView mit einer Stringnachricht an, die in der Variablen no_camera_access gespeichert ist. Fügen Sie der Datei $APPLICATION_PATH/res/values/strings.xml die folgende Zeile hinzu:

<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>

Wenn der Nutzer keine Kameraberechtigung erteilt, sieht der Bildschirm so aus:

missing_camera_permission_android

Jetzt fügen wir die Objekte SurfaceTexture und SurfaceView zu MainActivity hinzu:

private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;

Fügen Sie in der Funktion onCreate(Bundle) die folgenden zwei Zeilen ein, bevor Sie Kameraberechtigungen anfordern:

previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();

Fügen Sie jetzt den Code hinzu, der setupPreviewDisplayView() definiert:

private void setupPreviewDisplayView() {
  previewDisplayView.setVisibility(View.GONE);
  ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
  viewGroup.addView(previewDisplayView);
}

Wir definieren ein neues SurfaceView-Objekt und fügen es dem preview_display_layout FrameLayout-Objekt hinzu, damit wir es zum Anzeigen der Kameraframes mithilfe eines SurfaceTexture-Objekts namens previewFrameTexture verwenden können.

Um Kamerarahmen mit previewFrameTexture zu erhalten, verwenden wir CameraX. Framework stellt ein Dienstprogramm namens CameraXPreviewHelper zur Verwendung von CameraX bereit. Diese Klasse aktualisiert einen Listener, wenn die Kamera über onCameraStarted(@Nullable SurfaceTexture) gestartet wird.

Ändern Sie die Datei BUILD, um eine Abhängigkeit von "//mediapipe/java/com/google/mediapipe/components:android_camerax_helper" hinzuzufügen, um dieses Dienstprogramm zu verwenden.

Importieren Sie jetzt CameraXPreviewHelper und fügen Sie die folgende Zeile zu MainActivity hinzu:

private CameraXPreviewHelper cameraHelper;

Jetzt können wir unsere Implementierung zu startCamera() hinzufügen:

public void startCamera() {
  cameraHelper = new CameraXPreviewHelper();
  cameraHelper.setOnCameraStartedListener(
    surfaceTexture -> {
      previewFrameTexture = surfaceTexture;
      // Make the display view visible to start showing the preview.
      previewDisplayView.setVisibility(View.VISIBLE);
    });
}

Dadurch wird ein neues CameraXPreviewHelper-Objekt erstellt und ein anonymer Listener für das Objekt hinzugefügt. Wenn cameraHelper signalisiert, dass die Kamera gestartet wurde, und eine surfaceTexture zum Erfassen der Frames verfügbar ist, speichern wir diesen surfaceTexture als previewFrameTexture und machen den previewDisplayView sichtbar, damit wir Frames aus previewFrameTexture sehen können.

Bevor wir die Kamera starten, müssen wir jedoch entscheiden, welche Kamera verwendet werden soll. CameraXPreviewHelper übernimmt von CameraHelper, das zwei Optionen bietet: FRONT und BACK. Wir können die Entscheidung aus der Datei BUILD als Metadaten übergeben, sodass keine Codeänderung erforderlich ist, um eine andere Version der App mit einer anderen Kamera zu erstellen.

Angenommen, wir möchten die Kamera BACK verwenden, um in einer Live-Szene, die wir von der Kamera aus betrachten, eine Kantenerkennung durchzuführen. Fügen Sie die Metadaten zu AndroidManifest.xml hinzu:

      ...
      <meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
  </application>
</manifest>

und geben die Auswahl in BUILD in der Android-Binärregel helloworld mit einem neuen Eintrag in manifest_values an:

manifest_values = {
    "applicationId": "com.google.mediapipe.apps.basic",
    "appName": "Hello World",
    "mainActivity": ".MainActivity",
    "cameraFacingFront": "False",
},

Fügen Sie nun in MainActivity ein ApplicationInfo-Objekt hinzu, um die in manifest_values angegebenen Metadaten abzurufen:

private ApplicationInfo applicationInfo;

Fügen Sie der Funktion onCreate() Folgendes hinzu:

try {
  applicationInfo =
      getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
  Log.e(TAG, "Cannot find application info: " + e);
}

Fügen Sie nun am Ende der Funktion startCamera() die folgende Zeile ein:

CameraHelper.CameraFacing cameraFacing =
    applicationInfo.metaData.getBoolean("cameraFacingFront", false)
        ? CameraHelper.CameraFacing.FRONT
        : CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);

Jetzt sollte die Anwendung erfolgreich erstellt worden sein. Wenn Sie die Anwendung jedoch auf Ihrem Gerät ausführen, sehen Sie einen schwarzen Bildschirm, obwohl Kameraberechtigungen erteilt wurden. Das liegt daran, dass die von CameraXPreviewHelper bereitgestellte Variable surfaceTexture zwar gespeichert wird, der previewSurfaceView aber seine Ausgabe noch nicht verwendet und sie noch nicht auf dem Bildschirm anzeigt.

Da wir die Frames in einer MediaPipe-Grafik verwenden möchten, fügen wir keinen Code hinzu, um die Kameraausgabe direkt in dieser Anleitung anzusehen. Stattdessen erfahren Sie, wie wir Kameraframes zur Verarbeitung an eine MediaPipe-Grafik senden und die Ausgabe der Grafik auf dem Bildschirm anzeigen können.

ExternalTextureConverter einrichten

Ein SurfaceTexture erfasst Bildframes aus einem Stream als OpenGL ES-Textur. Wenn Sie eine MediaPipe-Grafik verwenden möchten, sollten von der Kamera aufgenommene Frames in einem regulären Open GL-Texturobjekt gespeichert werden. Das Framework stellt die Klasse ExternalTextureConverter bereit, mit der das in einem SurfaceTexture-Objekt gespeicherte Bild in ein reguläres OpenGL-Texturobjekt konvertiert wird.

Zur Verwendung von ExternalTextureConverter benötigen wir außerdem ein EGLContext, das von einem EglManager-Objekt erstellt und verwaltet wird. Fügen Sie der Datei BUILD eine Abhängigkeit hinzu, um EglManager, "//mediapipe/java/com/google/mediapipe/glutil" zu verwenden.

Fügen Sie in MainActivity die folgenden Deklarationen hinzu:

private EglManager eglManager;
private ExternalTextureConverter converter;

Fügen Sie der Funktion onCreate(Bundle) eine Anweisung hinzu, um das eglManager-Objekt zu initialisieren, bevor Sie Kameraberechtigungen anfordern:

eglManager = new EglManager(null);

Wie Sie wissen, haben wir in MainActivity die Funktion onResume() definiert, um zu bestätigen, dass Kameraberechtigungen gewährt wurden, und rufen startCamera() auf. Fügen Sie vor dieser Prüfung die folgende Zeile in onResume() ein, um das converter-Objekt zu initialisieren:

converter = new ExternalTextureConverter(eglManager.getContext());

Auf diesem converter wird jetzt die GLContext verwendet, die von eglManager verwaltet wird.

Außerdem muss die Funktion onPause() in MainActivity überschrieben werden, damit converter ordnungsgemäß geschlossen wird, wenn die Anwendung in den Status „Pausiert“ versetzt wird:

@Override
protected void onPause() {
  super.onPause();
  converter.close();
}

Fügen Sie den folgenden Codeblock zu setupPreviewDisplayView() hinzu, um die Ausgabe von previewFrameTexture an converter zu übergeben:

previewDisplayView
 .getHolder()
 .addCallback(
     new SurfaceHolder.Callback() {
       @Override
       public void surfaceCreated(SurfaceHolder holder) {}

       @Override
       public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
         // (Re-)Compute the ideal size of the camera-preview display (the area that the
         // camera-preview frames get rendered onto, potentially with scaling and rotation)
         // based on the size of the SurfaceView that contains the display.
         Size viewSize = new Size(width, height);
         Size displaySize = cameraHelper.computeDisplaySizeFromViewSize(viewSize);

         // Connect the converter to the camera-preview frames as its input (via
         // previewFrameTexture), and configure the output width and height as the computed
         // display size.
         converter.setSurfaceTextureAndAttachToGLContext(
             previewFrameTexture, displaySize.getWidth(), displaySize.getHeight());
       }

       @Override
       public void surfaceDestroyed(SurfaceHolder holder) {}
     });

In diesem Codeblock fügen wir previewDisplayView einen benutzerdefinierten SurfaceHolder.Callback hinzu und implementieren die Funktion surfaceChanged(SurfaceHolder holder, int format, int width, int height), um eine geeignete Anzeigegröße der Kameraframes auf dem Gerätebildschirm zu berechnen, das previewFrameTexture-Objekt zu verknüpfen und Frames des berechneten displaySize-Objekts an converter zu senden.

Jetzt können Kameraframes in einer MediaPipe-Grafik verwendet werden.

MediaPipe-Grafik in Android verwenden

Relevante Abhängigkeiten hinzufügen

Um eine MediaPipe-Grafik zu verwenden, müssen wir dem MediaPipe-Framework unter Android Abhängigkeiten hinzufügen. Wir fügen zuerst eine Build-Regel hinzu, um ein cc_binary mit dem JNI-Code des MediaPipe-Frameworks zu erstellen, und dann eine cc_library-Regel, um dieses Binärprogramm in unserer Anwendung zu verwenden. Fügen Sie der Datei BUILD den folgenden Codeblock hinzu:

cc_binary(
    name = "libmediapipe_jni.so",
    linkshared = 1,
    linkstatic = 1,
    deps = [
        "//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
    ],
)

cc_library(
    name = "mediapipe_jni_lib",
    srcs = [":libmediapipe_jni.so"],
    alwayslink = 1,
)

Fügen Sie der Build-Regel mediapipe_lib in der Datei BUILD die Abhängigkeit ":mediapipe_jni_lib" hinzu.

Als Nächstes müssen wir Abhängigkeiten hinzufügen, die spezifisch für die MediaPipe-Grafik sind, die wir in der Anwendung verwenden möchten.

Fügen Sie zuerst dem gesamten Rechnercode in der Build-Regel libmediapipe_jni.so Abhängigkeiten hinzu:

"//mediapipe/graphs/edge_detection:mobile_calculators",

MediaPipe-Diagramme sind .pbtxt-Dateien, aber um sie in der Anwendung zu verwenden, müssen wir mit der Build-Regel mediapipe_binary_graph eine .binarypb-Datei generieren.

Füge in der Android-Build-Regel helloworld das für das Diagramm spezifische Ziel mediapipe_binary_graph als Asset hinzu:

assets = [
  "//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",

In der Build-Regel assets können Sie auch andere Assets hinzufügen, z. B. die in Ihrer Grafik verwendeten TensorFlowLite-Modelle.

Fügen Sie außerdem eine zusätzliche manifest_values für Attribute hinzu, die für das Diagramm spezifisch sind und später in MainActivity abgerufen werden sollen:

manifest_values = {
    "applicationId": "com.google.mediapipe.apps.basic",
    "appName": "Hello World",
    "mainActivity": ".MainActivity",
    "cameraFacingFront": "False",
    "binaryGraphName": "mobile_gpu.binarypb",
    "inputVideoStreamName": "input_video",
    "outputVideoStreamName": "output_video",
},

Beachten Sie, dass binaryGraphName den Dateinamen des Binärdiagramms angibt, der durch das Feld output_name im Ziel mediapipe_binary_graph bestimmt wird. inputVideoStreamName und outputVideoStreamName sind die im Diagramm angegebenen Namen des Ein- bzw. Ausgabevideostreams.

Jetzt muss der MainActivity das MediaPipe-Framework laden. Außerdem verwendet das Framework OpenCV, sodass MainActvity auch OpenCV laden sollte. Verwenden Sie den folgenden Code in MainActivity (innerhalb der Klasse, aber nicht in einer Funktion), um beide Abhängigkeiten zu laden:

static {
  // Load all native libraries needed by the app.
  System.loadLibrary("mediapipe_jni");
  System.loadLibrary("opencv_java3");
}

Diagramm in MainActivity verwenden

Zuerst müssen wir das Asset laden, das .binarypb aus der Datei .pbtxt der Grafik enthält. Dazu können Sie das MediaPipe-Dienstprogramm AndroidAssetUtil verwenden.

Initialisieren Sie den Asset-Manager in onCreate(Bundle), bevor Sie eglManager initialisieren:

// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);

Jetzt müssen wir ein FrameProcessor-Objekt einrichten, das von converter vorbereitete Kamerarahmen an die MediaPipe-Grafik sendet und die Grafik ausführt, die Ausgabe vorbereitet und dann previewDisplayView aktualisiert, um die Ausgabe anzuzeigen. Fügen Sie den folgenden Code hinzu, um FrameProcessor zu deklarieren:

private FrameProcessor processor;

und initialisieren Sie sie in onCreate(Bundle), nachdem Sie eglManager initialisiert haben:

processor =
    new FrameProcessor(
        this,
        eglManager.getNativeContext(),
        applicationInfo.metaData.getString("binaryGraphName"),
        applicationInfo.metaData.getString("inputVideoStreamName"),
        applicationInfo.metaData.getString("outputVideoStreamName"));

Der processor muss die konvertierten Frames aus dem converter zur Verarbeitung abrufen. Fügen Sie die folgende Zeile zu onResume() hinzu, nachdem Sie converter initialisiert haben:

converter.setConsumer(processor);

processor sollte seine Ausgabe an previewDisplayView senden. Fügen Sie dazu unserem benutzerdefinierten SurfaceHolder.Callback die folgenden Funktionsdefinitionen hinzu:

@Override
public void surfaceCreated(SurfaceHolder holder) {
  processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
  processor.getVideoSurfaceOutput().setSurface(null);
}

Beim Erstellen der SurfaceHolder hatten wir die Surface auf die VideoSurfaceOutput der processor gesetzt. Wenn er gelöscht wird, wird er aus dem VideoSurfaceOutput von processor entfernt.

Webseite. Sie sollten jetzt die Anwendung erfolgreich auf dem Gerät erstellen und ausführen und sehen können, wie die Sobel-Edge-Erkennung in einem Live-Kamerafeed ausgeführt wird. Glückwunsch!

edge_detection_android_gpu_gif

Falls Probleme aufgetreten sind, finden Sie unter diesem Link den vollständigen Code der Anleitung.