מבוא
שלום לעולם הזה! המדריך משתמש ב-MediaPipe Framework כדי לפתח אפליקציה ל-Android מפעיל תרשים MediaPipe ב-Android.
מה תפַתחו
אפליקציית מצלמה פשוטה לזיהוי קצה של Sobel בזמן אמת, שמופעלת על סרטון בשידור חי להפעיל סטרימינג במכשיר Android.
הגדרה
- למידע על התקנת MediaPipe Framework במערכת, ראו התקנת Framework מדריך לפרטים.
- מתקינים את Android Development SDK ו-Android NDK. איך לעשות את זה גם [מדריך להתקנת framework].
- מפעילים את האפשרויות למפתחים במכשיר Android.
- מגדירים את Bazel במערכת כדי ליצור ולפרוס את האפליקציה ל-Android.
תרשים לזיהוי קצה
נשתמש בתרשים הבא, 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"
}
המחשה של התרשים מוצגת למטה:
התרשים הזה כולל זרם קלט יחיד בשם input_video
לכל הפריימים הנכנסים
שמצלמת המכשיר תספק.
הצומת הראשון בתרשים, LuminanceCalculator
, לוקח חבילה אחת (תמונה)
מסגרת) והחיל שינוי ברמת הבהירות באמצעות תוכנת ההצללה של OpenGL. התוצאה של
מסגרת התמונה נשלחת לזרם הפלט של luma_video
.
הצומת השני, SobelEdgesCalculator
מחיל זיהוי קצה על כניסות
מספר המנות בזרם luma_video
ובפלט מוביל לפלט output_video
.
אפליקציית Android שלנו תציג את פריימים של תמונות הפלט
מקור הנתונים output_video
.
הגדרה מינימלית ראשונית של האפליקציה
אנחנו מתחילים עם אפליקציה פשוטה ל-Android שמציגה את הכיתוב 'Hello World! '
שמופיע במסך. אפשר לדלג על השלב הזה אם יש לך ניסיון בפיתוח גרסאות ל-Android
אפליקציות שמשתמשות ב-bazel
.
יוצרים ספרייה חדשה שבה יוצרים את אפליקציית Android. עבור
לדוגמה, הקוד המלא של המדריך הזה נמצא בכתובת
mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic
אנחנו
להתייחס לנתיב הזה בתור $APPLICATION_PATH
בכל ה-Codelab.
שימו לב שבנתיב לאפליקציה:
- שם האפליקציה הוא
helloworld
. - הערך
$PACKAGE_PATH
של האפליקציה הואcom.google.mediapipe.apps.basic
. הטקסט הזה משמש בקטעי קוד במדריך הזה, לכן חשוב לזכור$PACKAGE_PATH
משלך כשמעתיקים את קטעי הקוד או משתמשים בהם.
הוספת קובץ activity_main.xml
אל $APPLICATION_PATH/res/layout
. הפעולה הזו מציגה
TextView
במסך מלא של האפליקציה עם המחרוזת 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>
צריך להוסיף MainActivity.java
פשוט אל $APPLICATION_PATH
שטוען את התוכן
של הפריסה activity_main.xml
כפי שמוצג בהמשך:
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);
}
}
הוספת קובץ מניפסט, AndroidManifest.xml
אל $APPLICATION_PATH
,
מפעילה את MainActivity
בעת הפעלת האפליקציה:
<?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>
באפליקציה שלנו אנחנו משתמשים בעיצוב Theme.AppCompat
באפליקציה, לכן אנחנו צריכים
הפניות מתאימות לנושאים. הוספת colors.xml
אל
$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
אל $APPLICATION_PATH/res/values/
:
<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>
כדי ליצור את האפליקציה, מוסיפים קובץ BUILD
ל-$APPLICATION_PATH
.
${appName}
ו-${mainActivity}
במניפסט יוחלפו במחרוזות
שצוינו ב-BUILD
כפי שמוצג למטה.
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",
],
)
הכלל android_library
מוסיף יחסי תלות של קובצי משאבים, MainActivity
ו-AndroidManifest.xml
.
הכלל android_binary
, משתמש בספריית Android basic_lib
שנוצרה כדי
לבנות APK בינארי להתקנה במכשיר Android.
כדי ליצור את האפליקציה, משתמשים בפקודה הבאה:
bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
מתקינים את קובץ ה-APK שנוצר באמצעות adb install
. לדוגמה:
adb install bazel-bin/$APPLICATION_PATH/helloworld.apk
פותחים את האפליקציה במכשיר. צריך להציג מסך עם הטקסט
Hello World!
שימוש במצלמה דרך CameraX
הרשאות גישה למצלמה
כדי להשתמש במצלמה באפליקציה שלנו, צריך לבקש מהמשתמש לספק
גישה למצלמה. כדי לבקש הרשאות גישה למצלמה, צריך להוסיף את הפרטים הבאים אל
AndroidManifest.xml
:
<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
צריך לשנות את גרסת ה-SDK המינימלית ל-21
ואת גרסת היעד של ה-SDK ל-27
ב
אותו קובץ:
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
כך תוכלו לוודא שהמשתמש מתבקש לבקש הרשאת גישה למצלמה, להשתמש בספריית CameraX לצורך גישה למצלמה.
כדי לבקש הרשאות גישה למצלמה, אפשר להשתמש בכלי עזר שמסופק על ידי MediaPipe Framework
רכיבים, כלומר PermissionHelper
. כדי להשתמש בו, צריך להוסיף תלות
"//mediapipe/java/com/google/mediapipe/components:android_components"
ב-
כלל mediapipe_lib
בBUILD
.
כדי להשתמש ב-PermissionHelper
ב-MainActivity
, צריך להוסיף את השורה הבאה ל
פונקציית onCreate
:
PermissionHelper.checkAndRequestCameraPermissions(this);
הפעולה הזו תציג למשתמש תיבת דו-שיח עם בקשה להרשאות להשתמש במצלמה באפליקציה הזו.
כדי לטפל בתגובת המשתמש, מוסיפים את הקוד הבא:
@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() {}
בינתיים נשאיר את השדה startCamera()
ריק. כשהמשתמש מגיב
להנחיה, MainActivity
ימשיך לפעול ותתבצע קריאה ל-onResume()
.
הקוד יאשר שהוענקו הרשאות לשימוש במצלמה
ולאחר מכן תפעיל את המצלמה.
יוצרים מחדש ומתקינים את האפליקציה מחדש. עכשיו אמורה להופיע בקשה לגשת למצלמה של האפליקציה.
גישה למצלמה
כשהרשאות המצלמה זמינות, אנחנו יכולים להתחיל ולאחזר פריימים מצלמה.
כדי לצפות בפריימים במצלמה, נשתמש בSurfaceView
. כל מסגרת
מהמצלמה יישמרו באובייקט SurfaceTexture
. כדי להשתמש בנתונים האלה,
צריך קודם לשנות את הפריסה של האפליקציה.
מסירים את בלוק הקוד של TextView
במלואו מ-
$APPLICATION_PATH/res/layout/activity_main.xml
ולהוסיף את הקוד הבא
במקום זאת:
<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>
לבלוק הקוד הזה יש FrameLayout
חדש בשם preview_display_layout
, וגם
TextView
מקונן בתוכו, בשם no_camera_access_preview
. כשהמצלמה
הרשאות גישה לא הוענקו, האפליקציה שלנו תציג
הפונקציה TextView
עם הודעת מחרוזת, ששמורה במשתנה no_camera_access
.
מוסיפים את השורה הבאה בקובץ $APPLICATION_PATH/res/values/strings.xml
:
<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
כשהמשתמש לא יעניק הרשאת גישה למצלמה, המסך ייראה כך: הזה:
עכשיו נוסיף את האובייקטים SurfaceTexture
ו-SurfaceView
כדי
MainActivity
:
private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;
בפונקציה onCreate(Bundle)
, מוסיפים את שתי השורות הבאות לפני
מבקש הרשאות גישה למצלמה:
previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();
עכשיו מוסיפים את הקוד המגדיר את setupPreviewDisplayView()
:
private void setupPreviewDisplayView() {
previewDisplayView.setVisibility(View.GONE);
ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
viewGroup.addView(previewDisplayView);
}
אנחנו מגדירים אובייקט SurfaceView
חדש ומוסיפים אותו
preview_display_layout
של FrameLayout
אובייקט כדי שנוכל להשתמש בו כדי להציג
את הפריימים במצלמה באמצעות אובייקט SurfaceTexture
בשם previewFrameTexture
.
כדי להשתמש ב-previewFrameTexture
להשגת פריימים למצלמה, נשתמש ב-CameraX.
Framework מספק כלי שירות בשם CameraXPreviewHelper
לשימוש ב-CameraX.
הכיתה הזו מתעדכנת האזנה כשהמצלמה מופעלת דרך
onCameraStarted(@Nullable SurfaceTexture)
כדי להשתמש בכלי השירות הזה, צריך לשנות את הקובץ BUILD
כך שיוסיף תלות
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper"
.
עכשיו אפשר לייבא את CameraXPreviewHelper
ולהוסיף את השורה הבאה אל
MainActivity
:
private CameraXPreviewHelper cameraHelper;
עכשיו אפשר להוסיף את ההטמעה ל-startCamera()
:
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);
});
}
הפעולה הזו יוצרת אובייקט CameraXPreviewHelper
חדש ומוסיפה אנונימי
שמאזין לאובייקט. כשאותת cameraHelper
שהמצלמה הופעלה
ו-surfaceTexture
כדי להשיג פריימים זמין, אנחנו שומרים אותם
surfaceTexture
בתור previewFrameTexture
, והופכים את previewDisplayView
כדי שנוכל להתחיל לראות פריימים מpreviewFrameTexture
.
אבל לפני הפעלת המצלמה, אנחנו צריכים להחליט איזו מצלמה להפעיל
לשימוש. הפקודה CameraXPreviewHelper
עוברת בירושה מ-CameraHelper
, שמספקת
אפשרויות, FRONT
ו-BACK
. אנחנו יכולים להעביר את ההחלטה מהקובץ BUILD
מטא-נתונים כך שלא נדרש שינוי בקוד כדי לבנות גרסה נוספת של
באמצעות מצלמה אחרת.
בהנחה שאנחנו רוצים להשתמש במצלמה של BACK
כדי לבצע זיהוי קצה בסצנה בשידור חי
שאנחנו צופים בו מהמצלמה, מוסיפים את המטא-נתונים ל-AndroidManifest.xml
:
...
<meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
</application>
</manifest>
ומציינים את הבחירה ב-BUILD
בכלל הבינארי של Android helloworld
עם רשומה חדשה ב-manifest_values
:
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
עכשיו, ב-MainActivity
כדי לאחזר את המטא-נתונים שצוינו ב-manifest_values
,
מוסיפים אובייקט ApplicationInfo
:
private ApplicationInfo applicationInfo;
בפונקציה onCreate()
, מוסיפים:
try {
applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot find application info: " + e);
}
עכשיו מוסיפים את השורה הבאה בסוף הפונקציה startCamera()
:
CameraHelper.CameraFacing cameraFacing =
applicationInfo.metaData.getBoolean("cameraFacingFront", false)
? CameraHelper.CameraFacing.FRONT
: CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);
בשלב הזה האפליקציה אמורה להיפתח. אבל כשמריצים
של האפליקציה במכשיר, תראו מסך שחור (למרות שהמצלמה
ניתנו הרשאות). הסיבה לכך היא שלמרות שאנחנו שומרים את
המשתנה surfaceTexture
סופק על ידי הפרמטר CameraXPreviewHelper
,
previewSurfaceView
עדיין לא משתמש בפלט שלו ומציג אותו במסך.
מכיוון שאנחנו רוצים להשתמש במסגרות בתרשים MediaPipe, לא נוסיף קוד לצפות בפלט של המצלמה ישירות במדריך הזה. במקום זאת, אנחנו מדלגים קדימה אנחנו יכולים לשלוח פריימים של המצלמה לעיבוד לתרשים MediaPipe ולהציג הפלט של התרשים על המסך.
הגדרת ExternalTextureConverter
SurfaceTexture
מצלמים פריימים של תמונות משידור בפורמט OpenGL ES
טקסטורה. כדי להשתמש בתרשים MediaPipe, פריימים שצולמו במצלמה צריכים להיות
מאוחסנים באובייקט מרקם רגיל של Open GL. ה-framework מספק מחלקה,
ExternalTextureConverter
כדי להמיר את התמונה ששמורה בקובץ SurfaceTexture
לאובייקט מרקם רגיל של OpenGL.
כדי להשתמש ב-ExternalTextureConverter
, אנחנו צריכים גם EGLContext
,
נוצר ומנוהל על ידי אובייקט EglManager
. צריך להוסיף תלות ל-BUILD
קובץ לשימוש ב-EglManager
, "//mediapipe/java/com/google/mediapipe/glutil"
.
ב-MainActivity
, צריך להוסיף את ההצהרות הבאות:
private EglManager eglManager;
private ExternalTextureConverter converter;
בפונקציה onCreate(Bundle)
, מוסיפים הצהרה כדי לאתחל את
אובייקט eglManager
לפני שמבקשים הרשאות גישה למצלמה:
eglManager = new EglManager(null);
תזכורת שהגדרנו את הפונקציה onResume()
ב-MainActivity
כדי לאשר
ניתנו הרשאות גישה למצלמה ואפשר להתקשר אל startCamera()
. לפני כן
צריך להוסיף את השורה הבאה ב-onResume()
כדי לאתחל את converter
object:
converter = new ExternalTextureConverter(eglManager.getContext());
המכשיר converter
משתמש עכשיו בGLContext
שמנוהל על ידי eglManager
.
אנחנו גם צריכים לשנות את הפונקציה onPause()
ב-MainActivity
כדי
אם האפליקציה עוברת למצב מושהה, אנחנו סוגרים את converter
כמו שצריך:
@Override
protected void onPause() {
super.onPause();
converter.close();
}
כדי להעביר את הפלט של previewFrameTexture
אל converter
, צריך להוסיף את
קטע הקוד הבא אל 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) {}
});
בבלוק הקוד הזה, אנחנו מוסיפים SurfaceHolder.Callback
בהתאמה אישית
previewDisplayView
והטמיעו את הפונקציה surfaceChanged(SurfaceHolder holder, int
format, int width, int height)
כדי לחשב גודל תצוגה מתאים
של מסגרות המצלמה במסך המכשיר ולקשור את previewFrameTexture
ולשלוח פריימים של ה-displaySize
המחושב אל converter
.
עכשיו אנחנו מוכנים להשתמש בפריימים של המצלמה בתרשים MediaPipe.
שימוש בתרשים MediaPipe ב-Android
הוספת יחסי תלות רלוונטיים
כדי להשתמש בתרשים MediaPipe, צריך להוסיף יחסי תלות למסגרת MediaPipe
ב-Android. קודם נוסיף כלל build כדי לבנות cc_binary
באמצעות קוד JNI
של ה-framework של MediaPipe ואז ליצור כלל cc_library
כדי להשתמש בקובץ הבינארי הזה
באפליקציה שלנו. מוסיפים את בלוק הקוד הבא לקובץ BUILD
:
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,
)
צריך להוסיף את התלות ":mediapipe_jni_lib"
לכלל ה-build של mediapipe_lib
ב-
את הקובץ BUILD
.
בשלב הבא צריך להוסיף יחסי תלות ספציפיים לתרשים MediaPipe שבו רוצים להשתמש באפליקציה.
קודם כול צריך להוסיף יחסי תלות לכל קוד המחשבון בשדה libmediapipe_jni.so
כלל build:
"//mediapipe/graphs/edge_detection:mobile_calculators",
תרשימי MediaPipe הם קבצים מסוג .pbtxt
, אך כדי להשתמש בהם באפליקציה אנחנו צריכים
כדי להשתמש בכלל ה-build mediapipe_binary_graph
כדי ליצור קובץ .binarypb
.
בכלל ה-build הבינארי של Android helloworld
, מוסיפים את mediapipe_binary_graph
ספציפי לתרשים כנכס:
assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
בכלל ה-build של assets
, אפשר גם להוסיף נכסים אחרים כמו TensorFlowLite
שבהם משתמשים בתרשים.
בנוסף, צריך להוסיף עוד manifest_values
לנכסים שספציפיים אל
של התרשים, כדי לאחזר אותו מאוחר יותר ב-MainActivity
:
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",
},
שימו לב ש-binaryGraphName
מציין את שם הקובץ של התרשים הבינארי,
נקבע לפי השדה output_name
ביעד mediapipe_binary_graph
.
inputVideoStreamName
ו-outputVideoStreamName
הם הקלט והפלט
השם של שידור הווידאו שמצוין בתרשים, בהתאמה.
עכשיו, ב-MainActivity
צריך לטעון את ה-framework של MediaPipe. כמו כן,
framework משתמש ב-OpenCV, ולכן גם MainActvity
צריך לטעון את OpenCV
. משתמשים ב
את הקוד הבא ב-MainActivity
(בתוך המחלקה, אבל לא בתוך פונקציה כלשהי)
כדי לטעון את שני יחסי התלות:
static {
// Load all native libraries needed by the app.
System.loadLibrary("mediapipe_jni");
System.loadLibrary("opencv_java3");
}
שימוש בתרשים בפונקציה MainActivity
קודם כול, צריך לטעון את הנכס שמכיל את השדה .binarypb
שנאסף מ:
את הקובץ .pbtxt
של הגרף. אפשר לעשות את זה בעזרת הכלי של MediaPipe,
AndroidAssetUtil
יש להפעיל את מנהל הנכסים ב-onCreate(Bundle)
לפני האתחול
eglManager
:
// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);
עכשיו צריך להגדיר אובייקט FrameProcessor
ששולח פריימים של המצלמה
שהוכן על ידי converter
לתרשים MediaPipe ומריץ את התרשים,
של הפלט, ואז מעדכנים את previewDisplayView
כדי להציג את הפלט. הוסף
הקוד הבא כדי להצהיר על FrameProcessor
:
private FrameProcessor processor;
ומאתחלים אותו ב-onCreate(Bundle)
אחרי האתחול של eglManager
:
processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
ה-processor
צריך לצרוך את הפריימים המומרים מ-converter
בשביל
בעיבוד. צריך להוסיף את השורה הבאה אל onResume()
אחרי אתחול של
converter
:
converter.setConsumer(processor);
הפלט של ה-processor
צריך להישלח אל previewDisplayView
. לשם כך, צריך להוסיף
את הגדרות הפונקציות הבאות לSurfaceHolder.Callback
המותאם אישית שלנו:
@Override
public void surfaceCreated(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(null);
}
כשה-SurfaceHolder
נוצר, Surface
היה
VideoSurfaceOutput
מתוך processor
. לאחר שהוא מושמד, אנחנו מסירים אותו
VideoSurfaceOutput
של processor
.
זה הכול! עכשיו אמורה להיות לכם אפשרות ליצור ולהפעיל בהצלחה את אפליקציה במכשיר ולראות את הזיהוי של Sobel End פועלת במצלמה בשידור חי פיד! מזל טוב!
אם נתקלת בבעיות, אפשר לעיין בקוד המלא של המדריך כאן.