Hello World! auf Android

Einführung

Dies ist Hello World! im Tutorial mithilfe des MediaPipe-Frameworks eine Android-App entwickeln, die ein MediaPipe-Diagramm unter Android ausführt.

Umfang

Eine einfache Kamera-App für die Sobel-Kantenerkennung in Echtzeit, die auf ein Live-Video angewendet wird auf einem Android-Gerät streamen.

edge_detection_android_gpu_gif

Einrichtung

  1. MediaPipe Framework auf Ihrem System installieren (siehe Framework-Installation) .
  2. Installieren Sie das Android Development SDK und das Android NDK. Eine Anleitung dazu finden Sie auch unter [Framework-Installationsanleitung].
  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 die Kantenerkennung

Wir verwenden die folgende Grafik, 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"
}

Im Folgenden sehen Sie eine Visualisierung des Diagramms:

edge_detection_mobile_gpu

Dieses Diagramm enthält einen einzelnen Eingabestream namens input_video für alle eingehenden Frames die von der Kamera Ihres Geräts bereitgestellt wird.

Der erste Knoten im Diagramm, LuminanceCalculator, verwendet ein einzelnes Paket (Bild Frame) und wendet eine Änderung der Leuchtdichte mithilfe eines OpenGL-Shaders an. Das Ergebnis Bildframe an den luma_video-Ausgabestream gesendet.

Der zweite Knoten, SobelEdgesCalculator, wendet die Edge-Erkennung auf eingehende Anfragen an Pakete im luma_video-Stream und Ausgaben führen zu einer output_video-Ausgabe .

Unsere Android-App zeigt die Ausgabe-Bildframes der Stream "output_video".

Minimale anfängliche Einrichtung der Anwendung

Wir beginnen mit einer einfachen Android-Anwendung, in der „Hello World!“ auf dem Bildschirm. Sie können diesen Schritt überspringen, wenn Sie mit der Erstellung von Android-Apps vertraut sind. Anwendungen, die bazel verwenden.

Erstellen Sie ein neues Verzeichnis, in dem Sie Ihre Android-App erstellen. Für den vollständigen Code dieser Anleitung unter mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic Wir werden im gesamten Codelab als $APPLICATION_PATH.

Beachten Sie im Pfad zur Anwendung Folgendes:

  • Die Anwendung heißt helloworld.
  • Die $PACKAGE_PATH der Anwendung ist com.google.mediapipe.apps.basic. Dies wird in Code-Snippets in diesem Tutorial verwendet. Denken Sie daher daran, Ihre eigene $PACKAGE_PATH, wenn Sie die Code-Snippets kopieren/verwenden.

Fügen Sie die Datei activity_main.xml zu $APPLICATION_PATH/res/layout hinzu. Dadurch wird Folgendes angezeigt: ein TextView im Vollbildmodus der Anwendung mit dem String Hello World!:

<?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 eine einfache MainActivity.java zu $APPLICATION_PATH hinzu, die den Inhalt lädt. des activity_main.xml-Layouts 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 die Manifestdatei AndroidManifest.xml zu $APPLICATION_PATH hinzu, die startet MainActivity beim Start der App:

<?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. Deshalb müssen wir angemessene Themenreferenzen. colors.xml hinzufügen zu $APPLICATION_PATH/res/values/:

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

styles.xml zu $APPLICATION_PATH/res/values/ hinzufügen:

<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 durch Strings ersetzt wie unten in BUILD angegeben.

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 die Ressourcendateien MainActivity hinzu und AndroidManifest.xml.

Die Regel „android_binary“ verwendet die Android-Bibliothek basic_lib, die generiert wurde, um Erstelle ein binäres APK zur Installation auf deinem Android-Gerät.

Verwenden Sie den folgenden Befehl, um die App zu erstellen:

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. Nun sollte ein Bildschirm mit dem Text Hello World!

bazel_hello_world_android

Kamera über CameraX verwenden

Kameraberechtigungen

Um die Kamera in unserer App verwenden zu können, müssen wir den Nutzer auf die Kamera zugreifen. Füge Folgendes hinzu, um Kameraberechtigungen anzufordern: AndroidManifest.xml:

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

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

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

So wird sichergestellt, dass der Nutzer aufgefordert wird, die Kameraberechtigung anzufordern, und aktiviert die CameraX-Bibliothek für den Kamerazugriff zu verwenden.

Zum Anfordern von Kameraberechtigungen können wir ein von MediaPipe Framework bereitgestelltes Dienstprogramm verwenden Komponenten, nämlich PermissionHelper. Fügen Sie eine Abhängigkeit hinzu, um sie zu verwenden "//mediapipe/java/com/google/mediapipe/components:android_components" in der mediapipe_lib-Regel in BUILD.

Um die PermissionHelper in MainActivity zu verwenden, fügen Sie die folgende Zeile zum onCreate-Funktion:

PermissionHelper.checkAndRequestCameraPermissions(this);

Dadurch wird der Nutzer in einem Dialogfeld aufgefordert, die Berechtigungen für die Kamera in dieser App verwenden.

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 antwortet wird MainActivity fortgesetzt und onResume() aufgerufen. Der Code bestätigt, dass die Berechtigungen zur Verwendung der Kamera erteilt wurden. und startet dann die Kamera.

Erstellen Sie die Anwendung neu und installieren Sie sie. Sie sollten nun eine Aufforderung sehen, für die App Zugriff auf die Kamera gewähren.

Kamerazugriff

Wenn Kameraberechtigungen verfügbar sind, können wir Frames aus der Kamera.

Um die Bilder von der Kamera zu sehen, verwenden wir ein SurfaceView. Jeder Frame von der Kamera werden in einem SurfaceTexture-Objekt gespeichert. Um sie zu verwenden, müssen wir zuerst das Layout unserer App ändern.

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

<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 hat ein neues FrameLayout mit dem Namen preview_display_layout und einen TextView ist darin verschachtelt und heißt no_camera_access_preview. Wenn Kamera Zugriffsberechtigungen nicht gewährt haben, zeigt die Anwendung die TextView durch eine Stringnachricht, gespeichert in der Variablen no_camera_access. Fügen Sie die folgende Zeile in die Datei $APPLICATION_PATH/res/values/strings.xml ein:

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

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

missing_camera_permission_android

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

private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;

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

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

Fügen Sie jetzt den Code hinzu, mit dem setupPreviewDisplayView() definiert wird:

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 zum preview_display_layout-FrameLayout-Objekt, damit wir es zum Anzeigen ein SurfaceTexture-Objekt namens previewFrameTexture ein.

Um previewFrameTexture zum Abrufen von Kamerarahmen zu verwenden, nutzen 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)

Ändern Sie zur Verwendung dieses Dienstprogramms die Datei BUILD, um eine Abhängigkeit für hinzuzufügen. "//mediapipe/java/com/google/mediapipe/components:android_camerax_helper".

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

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 anonymes Objekt Listener für das Objekt. Wenn cameraHelper signalisiert, dass die Kamera gestartet wurde und ein surfaceTexture zum Erfassen von Frames verfügbar ist, speichern wir surfaceTexture als previewFrameTexture festlegen und previewDisplayView sichtbar sind, sodass wir Frames aus previewFrameTexture sehen können.

Bevor wir die Kamera starten, müssen wir jedoch entscheiden, welche Kamera verwenden. CameraXPreviewHelper übernimmt CameraHelper. Dadurch werden zwei Optionen FRONT und BACK. Wir können die Entscheidung anhand der Datei BUILD übergeben. enthalten, sodass keine Codeänderungen erforderlich sind, um eine andere Version des App mit einer anderen Kamera.

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

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

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

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

Rufen Sie nun in MainActivity die in manifest_values angegebenen Metadaten ab: Fügen Sie ein ApplicationInfo-Objekt hinzu:

private ApplicationInfo applicationInfo;

Fügen Sie in 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 jetzt am Ende der Funktion startCamera() die folgende Zeile hinzu:

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

Die Anwendung sollte nun erfolgreich erstellt werden. Wenn Sie jedoch die App auf Ihrem Gerät öffnen, sehen Sie einen schwarzen Bildschirm, obwohl die Kamera Berechtigungen wurden gewährt). Das liegt daran, dass wir zwar die Variable surfaceTexture von CameraXPreviewHelper, der previewSurfaceView verwendet die Ausgabe noch nicht und zeigt sie noch nicht auf dem Bildschirm an.

Da wir die Frames in einem MediaPipe-Diagramm verwenden möchten, fügen wir sehen Sie sich die Kameraausgabe direkt in dieser Anleitung an. Wir überspringen stattdessen können wir Kameraframes zur Verarbeitung an ein MediaPipe-Diagramm senden der Grafik auf dem Bildschirm.

ExternalTextureConverter einrichten

Ein SurfaceTexture erfasst Bildframes aus einem Stream als OpenGL ES Textur. Um ein MediaPipe-Diagramm zu verwenden, sollten mit der Kamera aufgenommene Frames die in einem regulären Open GL-Texturobjekt gespeichert sind. Das Framework stellt eine Klasse bereit, ExternalTextureConverter, um das in einem SurfaceTexture gespeicherte Bild zu konvertieren -Objekt zu einem normalen OpenGL-Texturobjekt werden.

Zur Verwendung von ExternalTextureConverter benötigen wir auch eine EGLContext, die von einem EglManager-Objekt erstellt und verwaltet werden. Abhängigkeit zu BUILD hinzufügen Datei zur Verwendung von EglManager, "//mediapipe/java/com/google/mediapipe/glutil".

Fügen Sie in MainActivity die folgenden Deklarationen hinzu:

private EglManager eglManager;
private ExternalTextureConverter converter;

Fügen Sie in der Funktion onCreate(Bundle) eine Anweisung hinzu, um die eglManager-Objekt, bevor Kameraberechtigungen angefordert werden:

eglManager = new EglManager(null);

Zur Bestätigung: Wir haben die Funktion onResume() in MainActivity definiert. Kameraberechtigungen wurden gewährt. Rufe startCamera() auf. Vorher die folgende Zeile in onResume() einfügen, um converter zu initialisieren -Objekt enthält:

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

Für dieses Gerät (converter) wird jetzt das von eglManager verwaltete GLContext verwendet.

Außerdem müssen wir die Funktion onPause() in MainActivity überschreiben, Wenn die Anwendung in den Status „Pausiert“ wechselt, wird converter ordnungsgemäß geschlossen:

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

Für eine Weiterleitung der Ausgabe von previewFrameTexture an converter fügen Sie den Parameter folgenden Codeblock an setupPreviewDisplayView():

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 ein benutzerdefiniertes SurfaceHolder.Callback-Element hinzu, previewDisplayView und implementieren Sie die Funktion surfaceChanged(SurfaceHolder holder, int format, int width, int height), um eine geeignete Anzeigegröße zu berechnen. Kameraframes auf dem Gerätebildschirm und die previewFrameTexture und sendet Frames des berechneten displaySize-Objekts an converter.

Jetzt können Kameraframes in einem MediaPipe-Diagramm verwendet werden.

MediaPipe-Diagramm in Android verwenden

Relevante Abhängigkeiten hinzufügen

Um ein MediaPipe-Diagramm zu verwenden, müssen Sie Abhängigkeiten zum MediaPipe-Framework hinzufügen für Android. Zunächst fügen wir eine Build-Regel hinzu, um eine cc_binary mit JNI-Code zu erstellen. des MediaPipe-Frameworks und erstellen Sie dann eine cc_library-Regel zur Verwendung dieses Binärprogramms. in unserer App. 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 die Abhängigkeit ":mediapipe_jni_lib" hinzu: BUILD-Datei.

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

Fügen Sie zuerst Abhängigkeiten zum gesamten Rechnercode in der Datei libmediapipe_jni.so hinzu. Erstellungsregel:

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

MediaPipe-Grafiken sind .pbtxt-Dateien, aber um sie in der Anwendung verwenden zu können, benötigen wir um mit der Build-Regel mediapipe_binary_graph eine .binarypb-Datei zu generieren.

Füge in der Android-Binär-Build-Regel helloworld die mediapipe_binary_graph hinzu. für das Diagramm spezifisches Ziel als Asset:

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

In der Build-Regel assets können Sie auch andere Assets wie TensorFlowLite hinzufügen die in Ihrer Grafik verwendet werden.

Fügen Sie außerdem zusätzliche manifest_values für Attribute hinzu, die für die Graph, der später in MainActivity abgerufen wird:

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ären Graphen angibt. durch das Feld output_name im Ziel mediapipe_binary_graph festgelegt. inputVideoStreamName und outputVideoStreamName sind die Ein- und Ausgabe Video-Stream-Name, der in der Grafik angegeben ist.

Jetzt muss der MainActivity das MediaPipe-Framework laden. Die Framework verwendet OpenCV, daher sollte MainActvity auch OpenCV laden. Verwenden Sie die Methode nachstehender Code in MainActivity (innerhalb der Klasse, aber nicht innerhalb 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");
}

Das Diagramm in MainActivity verwenden

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

Initialisieren Sie den Asset-Manager vor der Initialisierung in onCreate(Bundle) eglManager:

// 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 Kameraframes sendet. die von der converter an die MediaPipe-Grafik die Ausgabe und aktualisiert dann previewDisplayView, um die Ausgabe anzuzeigen. Hinzufügen folgenden Code zur Deklaration von FrameProcessor:

private FrameProcessor processor;

und initialisieren Sie es nach der Initialisierung von eglManager in onCreate(Bundle):

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

processor muss die umgewandelten Frames aus dem converter für die Datenverarbeitung. Fügen Sie die folgende Zeile zu onResume() hinzu, nachdem Sie den converter:

converter.setConsumer(processor);

Die Ausgabe von processor sollte an previewDisplayView gesendet werden. Fügen Sie dazu die folgenden Funktionsdefinitionen in unser benutzerdefiniertes SurfaceHolder.Callback ein:

@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 für die VideoSurfaceOutput von processor. Wenn es zerstört wird, entfernen wir es aus der VideoSurfaceOutput von processor.

Webseite. Sie sollten nun in der Lage sein, den auf dem Gerät ausführen und sehen, wie die Sobel-Edge-Erkennung an einer Live-Kamera ausgeführt wird Feed! Glückwunsch!

edge_detection_android_gpu_gif

Falls Probleme aufgetreten sind, sehen Sie sich den vollständigen Code der Anleitung an. hier.