शुरुआती जानकारी
यह Hello World! ट्यूटोरियल, MediaPipe फ़्रेमवर्क का इस्तेमाल करके ऐसा Android ऐप्लिकेशन डेवलप करता है जो Android पर MediaPipe ग्राफ़ चलाता है.
आप क्या बनाएंगे
इस आसान कैमरा ऐप्लिकेशन को, Android डिवाइस पर लाइव वीडियो स्ट्रीम के लिए रीयल-टाइम में Sobel के किनारे की पहचान करने की सुविधा दी गई है.
सेटअप
- अपने सिस्टम पर MediaPipe फ़्रेमवर्क इंस्टॉल करें. ज़्यादा जानकारी के लिए, फ़्रेमवर्क इंस्टॉल करने की गाइड देखें.
- Android डेवलपमेंट SDK टूल और Android NDK इंस्टॉल करें. इसका तरीका जानने के लिए, [फ़्रेमवर्क इंस्टॉलेशन गाइड] में भी जाएं.
- अपने Android डिवाइस पर डेवलपर के लिए सेटिंग और टूल चालू करें.
- 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
आउटपुट
स्ट्रीम में दिखाता है.
हमारा Android ऐप्लिकेशन, output_video
स्ट्रीम के आउटपुट इमेज फ़्रेम दिखाएगा.
शुरुआती मिनिमम ऐप्लिकेशन सेटअप
हम सबसे पहले एक आसान Android ऐप्लिकेशन से शुरू करते हैं, जो स्क्रीन पर "नमस्ते दुनिया!"
दिखाता है. अगर आपको bazel
का इस्तेमाल करके Android ऐप्लिकेशन बनाने के बारे में जानकारी है, तो इस चरण को छोड़ा जा सकता है.
एक नई डायरेक्ट्री बनाएं, जहां आपको अपना Android ऐप्लिकेशन बनाना होगा. उदाहरण के लिए, इस ट्यूटोरियल का पूरा कोड mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic
पर देखा जा सकता है. हम पूरे कोडलैब में, इस पाथ को $APPLICATION_PATH
के तौर पर इस्तेमाल करेंगे.
ध्यान दें कि ऐप्लिकेशन के पाथ में:
- ऐप्लिकेशन का नाम
helloworld
है. - ऐप्लिकेशन का
$PACKAGE_PATH
com.google.mediapipe.apps.basic
है. इस ट्यूटोरियल में कोड स्निपेट में इसका इस्तेमाल किया गया है. इसलिए, कृपया कोड स्निपेट को कॉपी/इस्तेमाल करते समय अपना खुद का$PACKAGE_PATH
इस्तेमाल करना न भूलें.
$APPLICATION_PATH/res/layout
में activity_main.xml
फ़ाइल जोड़ें. इससे ऐप्लिकेशन की फ़ुल स्क्रीन पर, Hello
World!
स्ट्रिंग के साथ TextView
दिखता है:
<?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>
नीचे दिखाए गए तरीके के मुताबिक, activity_main.xml
लेआउट के कॉन्टेंट को लोड करने के लिए, $APPLICATION_PATH
में सामान्य MainActivity.java
जोड़ें:
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>
ऐप्लिकेशन बनाने के लिए, $APPLICATION_PATH
में BUILD
फ़ाइल जोड़ें और
मेनिफ़ेस्ट में ${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 डिवाइस पर इंस्टॉल करने के लिए बाइनरी APK बनाने के लिए जनरेट की गई basic_lib
Android लाइब्रेरी का इस्तेमाल करता है.
ऐप्लिकेशन बनाने के लिए, नीचे दिए गए कमांड का इस्तेमाल करें:
bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
adb install
का इस्तेमाल करके जनरेट की गई APK फ़ाइल इंस्टॉल करें. उदाहरण के लिए:
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 इतने फ़्रेमवर्क के तहत काम करने वाली सुविधा का इस्तेमाल कर सकते हैं, इन कॉम्पोनेंट के नाम PermissionHelper
हैं. इसका इस्तेमाल करने के लिए, BUILD
में mediapipe_lib
नियम में
"//mediapipe/java/com/google/mediapipe/components:android_components"
डिपेंडेंसी जोड़ें.
MainActivity
में PermissionHelper
का इस्तेमाल करने के लिए, 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
ऑब्जेक्ट में सेव किया जाएगा. इनका इस्तेमाल करने के लिए, हमें पहले
अपने ऐप्लिकेशन का लेआउट बदलना होगा.
$APPLICATION_PATH/res/layout/activity_main.xml
से पूरा TextView
कोड ब्लॉक हटाएं और
इसके बजाय, यहां दिया गया कोड जोड़ें:
<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>
इस कोड ब्लॉक में, preview_display_layout
नाम का एक नया FrameLayout
और इसके अंदर no_camera_access_preview
नाम का एक TextView
नेस्ट किया गया है. कैमरे के ऐक्सेस की अनुमतियां न मिलने पर, हमारा ऐप्लिकेशन 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
ऑब्जेक्ट में जोड़ा, ताकि इसका इस्तेमाल करके कैमरे के फ़्रेम दिखाने के लिए, previewFrameTexture
नाम के SurfaceTexture
ऑब्जेक्ट को दिखाया जा सके.
कैमरा फ़्रेम पाने के लिए, previewFrameTexture
का इस्तेमाल करने के लिए, हम CameraX का इस्तेमाल करेंगे.
फ़्रेमवर्क, CameraX का इस्तेमाल करने के लिए CameraXPreviewHelper
नाम की एक सुविधा देता है.
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>
और manifest_values
में एक नई एंट्री के साथ helloworld
के Android बाइनरी नियम में, BUILD
में चुना हुआ विकल्प तय करें:
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
अब, manifest_values
में बताए गए मेटाडेटा को वापस पाने के लिए, MainActivity
में जाकर, कोई 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);
इस स्थिति में, ऐप्लिकेशन सही से बन जाना चाहिए. हालांकि, जब आप अपने डिवाइस पर ऐप्लिकेशन चलाएंगे, तो आपको एक काली स्क्रीन दिखेगी.
भले ही, कैमरे की अनुमतियां दी गई हों. ऐसा इसलिए, क्योंकि हम CameraXPreviewHelper
से मिले
surfaceTexture
वैरिएबल को सेव करते हैं, लेकिन
previewSurfaceView
इसके आउटपुट का इस्तेमाल नहीं करता है और उसे अभी स्क्रीन पर नहीं दिखाता है.
हम MediaPipe ग्राफ़ में फ़्रेम का इस्तेमाल करना चाहते हैं, इसलिए हम सीधे इस ट्यूटोरियल में कैमरा आउटपुट देखने के लिए कोड नहीं जोड़ेंगे. इसके बजाय, अब हम जानते हैं कि हम MediaPipe ग्राफ़ पर प्रोसेसिंग के लिए कैमरा फ़्रेम कैसे भेज सकते हैं और स्क्रीन पर ग्राफ़ के आउटपुट कैसे दिखा सकते हैं.
ExternalTextureConverter
सेटअप
SurfaceTexture
, किसी स्ट्रीम से OpenGL ES टेक्सचर के तौर पर इमेज फ़्रेम को कैप्चर करता है. MediaPipe ग्राफ़ का इस्तेमाल करने के लिए, कैमरे से कैप्चर किए गए फ़्रेम को सामान्य Open GL टेक्सचर ऑब्जेक्ट में सेव किया जाना चाहिए. फ़्रेमवर्क, SurfaceTexture
ऑब्जेक्ट में सेव की गई इमेज को सामान्य OpenGL टेक्सचर ऑब्जेक्ट में बदलने के लिए, ExternalTextureConverter
क्लास उपलब्ध कराता है.
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);
याद रखें कि हमने MainActivity
में onResume()
फ़ंक्शन को तय किया है, ताकि यह पुष्टि की जा सके कि कैमरा ऐक्सेस करने की अनुमतियां दी गई हैं. इसके बाद, startCamera()
को कॉल करें. इस जांच से पहले, converter
ऑब्जेक्ट को शुरू करने के लिए, onResume()
में यहां दी गई लाइन जोड़ें:
converter = new ExternalTextureConverter(eglManager.getContext());
यह converter
अब GLContext
का इस्तेमाल करता है, जिसे eglManager
मैनेज करता है.
हमें MainActivity
में onPause()
फ़ंक्शन को भी बदलना होगा, ताकि अगर ऐप्लिकेशन 'रोका गया' की स्थिति में चला जाए, तो हम 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) {}
});
इस कोड ब्लॉक में, हम previewDisplayView
के लिए कस्टम SurfaceHolder.Callback
को जोड़ते हैं. साथ ही, डिवाइस की स्क्रीन पर कैमरे के फ़्रेम के सही डिसप्ले साइज़ का पता लगाने के लिए, surfaceChanged(SurfaceHolder holder, int
format, int width, int height)
फ़ंक्शन लागू करते हैं. साथ ही, हम previewFrameTexture
ऑब्जेक्ट को जोड़ने और कंप्यूट किए गए displaySize
के फ़्रेम को converter
पर भेजते हैं.
अब हम MediaPipe ग्राफ़ में कैमरा फ़्रेम का इस्तेमाल करने के लिए तैयार हैं.
Android में MediaPipe ग्राफ़ का इस्तेमाल करना
काम की डिपेंडेंसी जोड़ना
MediaPipe ग्राफ़ का इस्तेमाल करने के लिए, हमें Android पर MediaPipe फ़्रेमवर्क
में डिपेंडेंसी जोड़नी होगी. हम पहले MediaPipe फ़्रेमवर्क के JNI कोड का इस्तेमाल करके cc_binary
बनाने के लिए एक बिल्ड नियम जोड़ेंगे. इसके बाद, अपने ऐप्लिकेशन में इस बाइनरी का इस्तेमाल करने के लिए, एक 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,
)
BUILD
फ़ाइल में, mediapipe_lib
बिल्ड नियम
में डिपेंडेंसी ":mediapipe_jni_lib"
जोड़ें.
इसके बाद, हमें ऐप्लिकेशन में इस्तेमाल किए जाने वाले MediaPipe ग्राफ़ के लिए खास डिपेंडेंसी जोड़नी होंगी.
सबसे पहले, libmediapipe_jni.so
बिल्ड नियम में सभी कैलकुलेटर कोड के लिए डिपेंडेंसी जोड़ें:
"//mediapipe/graphs/edge_detection:mobile_calculators",
MediaPipe ग्राफ़, .pbtxt
फ़ाइलें होती हैं, लेकिन ऐप्लिकेशन में उनका इस्तेमाल करने के लिए, हमें
mediapipe_binary_graph
बिल्ड नियम का इस्तेमाल करके .binarypb
फ़ाइल जनरेट करनी होगी.
helloworld
Android बाइनरी बिल्ड नियम में, ग्राफ़ से जुड़े 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
, बाइनरी ग्राफ़ का फ़ाइल नाम दिखाता है.
इसे mediapipe_binary_graph
टारगेट के output_name
फ़ील्ड से तय किया जाता है.
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
में ग्राफ़ का इस्तेमाल करें
सबसे पहले, हमें वह एसेट लोड करनी होगी जिसमें ग्राफ़ की .pbtxt
फ़ाइल से कंपाइल किया गया
.binarypb
हो. ऐसा करने के लिए, हम 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
बनाया गया था, तब processor
के VideoSurfaceOutput
में हमारे पास Surface
था. इसके खत्म होने पर, हम इसे processor
के VideoSurfaceOutput
से हटा देते हैं.
यह बहुत आसान है! अब आप डिवाइस पर ऐप्लिकेशन बना सकेंगे और उसे चला सकेंगे. साथ ही, आपको लाइव कैमरा फ़ीड पर Sobel Edge की पहचान करने की सुविधा दिखेगी! बधाई हो!
अगर आपको कोई समस्या हुई है, तो ट्यूटोरियल का पूरा कोड यहां देखें.