บทนำ
สวัสดีโลกนี้ บทแนะนำ ใช้เฟรมเวิร์ก 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 = [
"//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
จะเพิ่มทรัพยากร 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 ทำงานบนกล้องแบบสด ฟีด! ยินดีด้วย
หากพบปัญหา โปรดดูโค้ดแบบเต็มของบทแนะนำ ที่นี่