مقدمه
این سلام دنیا! آموزش از MediaPipe Framework برای توسعه یک برنامه اندرویدی استفاده می کند که یک نمودار MediaPipe را در اندروید اجرا می کند.
آنچه را که خواهید ساخت
یک برنامه دوربین ساده برای تشخیص لبه Sobel در زمان واقعی که در یک پخش ویدیوی زنده در دستگاه Android اعمال می شود.
راه اندازی
- MediaPipe Framework را روی سیستم خود نصب کنید، برای جزئیات به راهنمای نصب Framework مراجعه کنید.
- Android Development SDK و Android NDK را نصب کنید. نحوه انجام این کار را نیز در [راهنمای نصب چارچوب] مشاهده کنید.
- گزینه های توسعه دهنده را در دستگاه Android خود فعال کنید.
- Bazel را روی سیستم خود راه اندازی کنید تا برنامه اندروید را بسازید و اجرا کنید.
نمودار برای تشخیص لبه
ما از نمودار زیر، 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
خروجی می دهد.
برنامه اندروید ما فریم های تصویر خروجی جریان output_video
را نمایش می دهد.
حداقل راه اندازی اولیه برنامه
ابتدا با یک برنامه اندروید ساده شروع می کنیم که "Hello World!" روی صفحه نمایش اگر با ساخت برنامه های اندروید با استفاده از bazel
آشنا هستید، می توانید این مرحله را نادیده بگیرید.
یک دایرکتوری جدید ایجاد کنید که در آن برنامه اندروید خود را ایجاد کنید. برای مثال، کد کامل این آموزش را میتوانید در 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
اضافه کنید. این یک 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
در قانون باینری اندروید 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
ایجاد و مدیریت می شود. برای استفاده از EglManager
، "//mediapipe/java/com/google/mediapipe/glutil"
یک وابستگی به فایل BUILD
اضافه کنید.
در 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 در اندروید
وابستگی های مربوطه را اضافه کنید
برای استفاده از نمودار MediaPipe، باید وابستگی هایی را به چارچوب MediaPipe در اندروید اضافه کنیم. ابتدا یک قانون ساخت برای ساختن یک 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
استفاده کنیم.
در قانون ساخت باینری اندروید 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
استفاده کنیم.
قبل از مقداردهی اولیه eglManager
، مدیر دارایی را در onCreate(Bundle)
راه اندازی کنید:
// 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;
و پس از مقداردهی اولیه eglManager
آن را در onCreate(Bundle)
مقداردهی اولیه کنید:
processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
processor
باید فریم های تبدیل شده را از converter
برای پردازش مصرف کند. پس از مقداردهی اولیه converter
، خط زیر را به onResume()
اضافه کنید:
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 را در حال اجرا در فید دوربین زنده مشاهده کنید! تبریک میگم
اگر با مشکلی مواجه شدید، لطفاً کد کامل آموزش را اینجا ببینید.