مقدمة
مرحبًا بكم برنامج تعليمي يستخدم إطار عمل MediaPipe لتطوير تطبيق Android على رسم بياني لـ MediaPipe على Android.
ما الذي ستقوم ببنائه
هو تطبيق كاميرا بسيط لرصد حافة Sobel في الوقت الفعلي على فيديو مباشر. بدء البث على جهاز Android
ضبط إعدادات الجهاز
- تثبيت إطار عمل MediaPipe على النظام، وراجِع تثبيت إطار العمل. الدليل للاطّلاع على التفاصيل.
- تثبيت حزمة تطوير البرامج (SDK) لتطوير Android وAndroid NDK. يُرجى الاطّلاع على كيفية إجراء ذلك أيضًا في [دليل تثبيت إطار العمل]
- فعِّل خيارات المطوّرين على جهاز 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
في الدرس التطبيقي حول الترميز
لاحظ أنه في المسار إلى التطبيق:
- يُطلق على التطبيق اسم
helloworld
. - السمة
$PACKAGE_PATH
للتطبيق هيcom.google.mediapipe.apps.basic
. يتم استخدام ذلك في مقتطفات الرمز في هذا البرنامج التعليمي، لذا يُرجى تذكر استخدامها$PACKAGE_PATH
الخاص بك عند نسخ/استخدام مقتطفات الرمز.
إضافة ملف activity_main.xml
إلى $APPLICATION_PATH/res/layout
يعرض هذا الإجراء
a 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
واستهداف إصدار الحزمة إلى 27
في
الملف نفسه:
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
يضمن ذلك أن يُطلَب من المستخدم طلب إذن الوصول إلى الكاميرا ويفعِّل استخدام مكتبة CameraX للوصول إلى الكاميرا.
لطلب أذونات الكاميرا، يمكننا استخدام أداة يوفّرها إطار عمل MediaPipe
المكوّنات، وهي 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.
يوفّر إطار العمل أداة باسم 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
في قاعدة helloworld
الثنائية لنظام Android
مع إدخال جديد في 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 عادي. يوفر إطار العمل فئة
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
الكائن:
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. سنضيف أولاً قاعدة إصدار لإنشاء cc_binary
باستخدام رمز JNI.
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"
إلى قاعدة الإصدار mediapipe_lib
في
ملف BUILD
.
بعد ذلك، نحتاج إلى إضافة تبعيات خاصة بمخطط MediaPipe الذي نريد استخدامه في التطبيق.
يُرجى أولاً إضافة التبعيات إلى كل رموز الآلة الحاسبة في libmediapipe_jni.so
.
قاعدة الإصدار:
"//mediapipe/graphs/edge_detection:mobile_calculators",
الرسوم البيانية MediaPipe هي ملفات .pbtxt
، ولكن لاستخدامها في التطبيق، نحتاج إلى
لاستخدام قاعدة الإصدار mediapipe_binary_graph
لإنشاء ملف .binarypb
.
في قاعدة إصدار Android الثنائي helloworld
، أضِف mediapipe_binary_graph
.
الاستهداف الخاص بالرسم البياني كمادة عرض:
assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
في قاعدة الإصدار 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
الآن تحميل إطار عمل MediaPipe. كما أن ورقة العمل
إطار العمل الخاص بها 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 قيد التشغيل على كاميرا مباشرة. خلاصة! تهانينا!
إذا واجهت أي مشاكل، يُرجى الاطّلاع على الرمز الكامل للبرنامج التعليمي هنا.