บทนำ
สวัสดีโลกนี้ บทแนะนำ ใช้เฟรมเวิร์ก MediaPipe เพื่อพัฒนาแอปพลิเคชัน Android ที่ เรียกใช้กราฟ MediaPipe บน Android
สิ่งที่คุณจะสร้าง
แอปกล้องที่ใช้งานง่ายสำหรับการตรวจจับขอบของ Sobel แบบเรียลไทม์ที่ใช้กับวิดีโอสด สตรีมบนอุปกรณ์ Android

ตั้งค่า
- ติดตั้งเฟรมเวิร์ก MediaPipe ในระบบของคุณ โปรดดูหัวข้อการติดตั้งเฟรมเวิร์ก เพื่อดูรายละเอียด
- ติดตั้ง Android Development SDK และ 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
โหนดที่ 2 SobelEdgesCalculator ใช้การตรวจหา Edge กับขาเข้า
แพ็กเก็ตในสตรีมและเอาต์พุตของ 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);
}
}
เพิ่มไฟล์ Manifest 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} ในไฟล์ Manifest ด้วยสตริง
ที่ระบุใน BUILD ดังที่แสดงด้านล่าง
android_library(
name = "basic_lib",
srcs = glob(["*.java"]),
manifest = "AndroidManifest.xml",
resource_files = glob(["res/**"]),
deps = [
"@maven//:androidx_appcompat_appcompat",
"@maven//:androidx_constraintlayout_constraintlayout",
],
)
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 จะเพิ่มทรัพยากร Dependency สำหรับ 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 หากต้องการใช้งาน ให้เพิ่มทรัพยากร Dependency
"//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) ให้เพิ่ม 2 บรรทัดต่อไปนี้ก่อน
กำลังขอสิทธิ์ใช้กล้อง:
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 เพื่อเพิ่มทรัพยากร Dependency
"//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 ใหม่และเพิ่มแบบไม่ระบุตัวตน
Listener ของออบเจ็กต์ เมื่อ 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
ของ Google ในการใช้กราฟ 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
เพิ่มทรัพยากร Dependency ที่เกี่ยวข้อง
ในการใช้กราฟ 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,
)
เพิ่มทรัพยากร Dependency ":mediapipe_jni_lib" ไปยังกฎบิลด์ mediapipe_lib ใน
ไฟล์ BUILD
ต่อไป เราต้องเพิ่มทรัพยากร Dependency เฉพาะสำหรับกราฟ MediaPipe ที่ต้องการใช้ ในแอปพลิเคชัน
ก่อนอื่นให้เพิ่มทรัพยากร Dependency ลงในรหัสเครื่องคำนวณทั้งหมดใน 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 (ในชั้นเรียน แต่ไม่ได้อยู่ในฟังก์ชันใดๆ)
เพื่อโหลดทรัพยากร Dependency ทั้ง 2 แบบดังนี้
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 ทำงานบนกล้องแบบสด ฟีด! ยินดีด้วย

หากพบปัญหา โปรดดูโค้ดแบบเต็มของบทแนะนำ ที่นี่