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.
Einrichtung
- Installieren Sie das MediaPipe-Framework auf Ihrem System. Weitere Informationen finden Sie in der Framework-Installationsanleitung.
- Installieren Sie das Android Development SDK und Android NDK. Eine entsprechende Anleitung finden Sie auch im [Framework-Installationshandbuch].
- Aktivieren Sie die Entwickleroptionen auf Ihrem Android-Gerät.
- 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:
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 istcom.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.
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:
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!
Falls Probleme aufgetreten sind, finden Sie unter diesem Link den vollständigen Code der Anleitung.