Giới thiệu
Đây là Hello World! sử dụng Khung MediaPipe để phát triển một ứng dụng Android chạy biểu đồ MediaPipe trên Android.
Sản phẩm bạn sẽ tạo ra
Ứng dụng máy ảnh đơn giản giúp phát hiện cạnh Sobel theo thời gian thực, áp dụng cho video trực tiếp phát trực tuyến trên thiết bị Android.
Thiết lập
- Cài đặt Khung MediaPipe trên hệ thống của bạn, xem phần Cài đặt khung để biết thông tin chi tiết.
- Cài đặt Android NDK và SDK Phát triển Android. Xem cách thực hiện việc này cũng trong [Hướng dẫn cài đặt khung].
- Bật tuỳ chọn cho nhà phát triển trên thiết bị Android.
- Thiết lập Bazel trên hệ thống của bạn để xây dựng và triển khai ứng dụng Android.
Biểu đồ phát hiện cạnh
Chúng ta sẽ sử dụng biểu đồ sau đây, 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"
}
Biểu đồ trực quan được hiển thị dưới đây:
Biểu đồ này có một luồng đầu vào duy nhất tên là input_video
cho tất cả các khung hình sắp tới
do máy ảnh trên thiết bị của bạn cung cấp.
Nút đầu tiên trong biểu đồ, LuminanceCalculator
, nhận một gói duy nhất (hình ảnh
) và áp dụng thay đổi về độ chói bằng chương trình đổ bóng OpenGL. Kết quả
khung hình ảnh này sẽ được gửi đến luồng đầu ra luma_video
.
Nút thứ hai, SobelEdgesCalculator
áp dụng tính năng phát hiện cạnh cho lệnh đến
các gói trong luồng luma_video
và xuất ra kết quả đầu ra output_video
luồng.
Ứng dụng Android của chúng ta sẽ hiển thị khung hình ảnh đầu ra của
Luồng output_video
.
Thiết lập ứng dụng tối thiểu ban đầu
Đầu tiên, chúng ta bắt đầu với một ứng dụng Android đơn giản hiển thị thông báo "Hello World!"
trên màn hình. Bạn có thể bỏ qua bước này nếu bạn quen với việc xây dựng Android
bằng bazel
.
Tạo một thư mục mới để tạo ứng dụng Android. Cho
Ví dụ: bạn có thể tìm thấy mã hoàn chỉnh của hướng dẫn này tại
mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic
. Chúng tôi sẽ
hãy gọi đường dẫn này là $APPLICATION_PATH
trong suốt lớp học lập trình.
Lưu ý rằng trong đường dẫn đến ứng dụng:
- Ứng dụng này có tên là
helloworld
. $PACKAGE_PATH
của ứng dụng này làcom.google.mediapipe.apps.basic
. Cụm từ này được sử dụng trong các đoạn mã của hướng dẫn này, vì vậy hãy nhớ sử dụng$PACKAGE_PATH
của riêng bạn khi sao chép/sử dụng các đoạn mã.
Thêm một tệp activity_main.xml
vào $APPLICATION_PATH/res/layout
. Màn hình này
TextView
trên toàn màn hình của ứng dụng có chuỗi 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>
Thêm một MainActivity.java
đơn giản vào $APPLICATION_PATH
để tải nội dung
của bố cục activity_main.xml
như minh hoạ dưới đây:
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);
}
}
Thêm tệp kê khai, AndroidManifest.xml
vào $APPLICATION_PATH
khởi chạy MainActivity
khi khởi động ứng dụng:
<?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>
Trong ứng dụng này, chúng ta đang dùng giao diện Theme.AppCompat
trong ứng dụng nên cần
tham chiếu chủ đề thích hợp. Thêm colors.xml
vào
$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>
Thêm styles.xml
vào $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>
Để xây dựng ứng dụng, hãy thêm tệp BUILD
vào $APPLICATION_PATH
và
${appName}
và ${mainActivity}
trong tệp kê khai sẽ được thay thế bằng chuỗi
được chỉ định trong BUILD
như minh hoạ dưới đây.
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",
],
)
Quy tắc android_library
thêm các phần phụ thuộc cho MainActivity
, các tệp tài nguyên
và AndroidManifest.xml
.
Quy tắc android_binary
sử dụng thư viện Android basic_lib
được tạo để
tạo APK nhị phân để cài đặt trên thiết bị Android của bạn.
Để tạo bản dựng ứng dụng, hãy dùng lệnh sau:
bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
Cài đặt tệp APK đã tạo bằng adb install
. Ví dụ:
adb install bazel-bin/$APPLICATION_PATH/helloworld.apk
Mở ứng dụng trên thiết bị. Nút này sẽ hiển thị một màn hình có văn bản
Hello World!
.
Đang sử dụng máy ảnh qua CameraX
Quyền truy cập máy ảnh
Để sử dụng camera trong ứng dụng của chúng ta, chúng ta cần yêu cầu người dùng cung cấp
quyền truy cập vào máy ảnh. Để yêu cầu quyền truy cập vào camera, hãy thêm thông tin sau vào
AndroidManifest.xml
:
<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
Thay đổi phiên bản SDK tối thiểu thành 21
và nhắm mục tiêu phiên bản SDK thành 27
trong
cùng một tệp:
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
Điều này đảm bảo rằng người dùng được nhắc yêu cầu quyền truy cập vào camera và bật chúng tôi dùng thư viện CameraX để truy cập máy ảnh.
Để yêu cầu quyền truy cập vào camera, chúng ta có thể dùng một tiện ích do Khung MediaPipe cung cấp
thành phần chính, cụ thể là PermissionHelper
. Để sử dụng công cụ này, hãy thêm phần phụ thuộc
"//mediapipe/java/com/google/mediapipe/components:android_components"
trong
Quy tắc mediapipe_lib
trong BUILD
.
Để sử dụng PermissionHelper
trong MainActivity
, hãy thêm dòng sau vào phương thức
Hàm onCreate
:
PermissionHelper.checkAndRequestCameraPermissions(this);
Thao tác này sẽ nhắc người dùng thông qua một hộp thoại trên màn hình để yêu cầu cấp quyền sử dụng máy ảnh trong ứng dụng này.
Thêm mã sau đây để xử lý phản hồi của người dùng:
@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() {}
Tạm thời, chúng ta sẽ để trống phương thức startCamera()
. Khi người dùng trả lời
vào lời nhắc, MainActivity
sẽ tiếp tục và onResume()
sẽ được gọi.
Mã này sẽ xác nhận rằng bạn đã được cấp quyền sử dụng camera,
sau đó sẽ khởi động máy ảnh.
Tạo lại và cài đặt ứng dụng. Bây giờ, bạn sẽ thấy một lời nhắc yêu cầu quyền truy cập vào máy ảnh dành cho ứng dụng.
Quyền truy cập máy ảnh
Khi có quyền truy cập vào máy ảnh, chúng ta có thể bắt đầu và tìm nạp khung hình từ máy ảnh.
Để xem các khung hình từ máy ảnh, chúng ta sẽ sử dụng SurfaceView
. Mỗi khung
từ máy ảnh sẽ được lưu trữ trong đối tượng SurfaceTexture
. Để sử dụng các tính năng này, chúng tôi
trước tiên cần thay đổi bố cục của ứng dụng.
Xoá toàn bộ khối mã TextView
khỏi
$APPLICATION_PATH/res/layout/activity_main.xml
rồi thêm mã sau
thay vào đó:
<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>
Khối mã này có một FrameLayout
mới tên là preview_display_layout
và một
TextView
được lồng bên trong đó, có tên là no_camera_access_preview
. Khi camera
chưa được cấp quyền truy cập, ứng dụng của chúng tôi sẽ hiển thị
TextView
với một thông báo chuỗi, được lưu trữ trong biến no_camera_access
.
Thêm dòng sau vào tệp $APPLICATION_PATH/res/values/strings.xml
:
<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
Khi người dùng không cấp quyền truy cập vào camera, màn hình giờ đây sẽ trông giống như sau:
Bây giờ, chúng ta sẽ thêm các đối tượng SurfaceTexture
và SurfaceView
vào
MainActivity
:
private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;
Trong hàm onCreate(Bundle)
, hãy thêm hai dòng sau trước
yêu cầu quyền truy cập vào camera:
previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();
Và giờ hãy thêm mã xác định setupPreviewDisplayView()
:
private void setupPreviewDisplayView() {
previewDisplayView.setVisibility(View.GONE);
ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
viewGroup.addView(previewDisplayView);
}
Chúng ta xác định một đối tượng SurfaceView
mới và thêm đối tượng đó vào
preview_display_layout
FrameLayout
sao cho chúng ta có thể dùng đối tượng này để hiển thị
khung máy ảnh bằng cách sử dụng đối tượng SurfaceTexture
có tên previewFrameTexture
.
Để dùng previewFrameTexture
nhằm lấy khung hình máy ảnh, chúng ta sẽ dùng CameraX.
Khung cung cấp một tiện ích có tên CameraXPreviewHelper
để sử dụng CameraX.
Lớp này cập nhật một trình nghe khi máy ảnh được khởi động qua
onCameraStarted(@Nullable SurfaceTexture)
.
Để sử dụng phần mềm tiện ích này, hãy sửa đổi tệp BUILD
để thêm phần phụ thuộc vào
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper"
Bây giờ, hãy nhập CameraXPreviewHelper
và thêm dòng sau vào
MainActivity
:
private CameraXPreviewHelper cameraHelper;
Bây giờ, chúng ta có thể thêm phương thức triển khai vào 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);
});
}
Thao tác này sẽ tạo một đối tượng CameraXPreviewHelper
mới và thêm một đối tượng ẩn danh
trình nghe trên đối tượng. Khi cameraHelper
báo hiệu rằng camera đã khởi động
và có sẵn surfaceTexture
để lấy các khung hình, chúng tôi sẽ lưu
surfaceTexture
thành previewFrameTexture
và tạo previewDisplayView
để chúng ta có thể bắt đầu thấy các khung hình từ previewFrameTexture
.
Tuy nhiên, trước khi khởi động camera, chúng ta cần quyết định xem mình muốn camera nào
sử dụng. CameraXPreviewHelper
kế thừa từ CameraHelper
, cung cấp hai
FRONT
và BACK
. Chúng ta có thể chuyển quyết định từ tệp BUILD
làm siêu dữ liệu sao cho không cần thay đổi mã để tạo một phiên bản khác của
thông qua một camera khác.
Giả sử chúng ta muốn sử dụng camera BACK
để thực hiện phát hiện cạnh trên một cảnh trực tiếp
mà chúng ta xem từ camera, hãy thêm siêu dữ liệu vào AndroidManifest.xml
:
...
<meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
</application>
</manifest>
và chỉ định lựa chọn trong BUILD
trong quy tắc nhị phân helloworld
của android
với một mục mới trong manifest_values
:
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
Giờ đây, trong MainActivity
để truy xuất siêu dữ liệu được chỉ định trong manifest_values
,
thêm một đối tượng ApplicationInfo
:
private ApplicationInfo applicationInfo;
Trong hàm onCreate()
, hãy thêm:
try {
applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot find application info: " + e);
}
Giờ thì hãy thêm dòng sau vào cuối hàm startCamera()
:
CameraHelper.CameraFacing cameraFacing =
applicationInfo.metaData.getBoolean("cameraFacingFront", false)
? CameraHelper.CameraFacing.FRONT
: CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);
Tại thời điểm này, ứng dụng sẽ được tạo thành công. Tuy nhiên, khi chạy
ứng dụng trên thiết bị của mình, bạn sẽ thấy màn hình đen (mặc dù máy ảnh
đã được cấp quyền). Đó là vì mặc dù chúng tôi lưu
Biến surfaceTexture
được cung cấp bởi CameraXPreviewHelper
,
previewSurfaceView
chưa sử dụng dữ liệu đầu ra và chưa hiển thị kết quả đó trên màn hình.
Vì chúng ta muốn sử dụng các khung trong đồ thị MediaPipe, chúng ta sẽ không thêm mã vào hãy xem đầu ra của camera ngay trong hướng dẫn này. Thay vào đó, chúng ta sẽ chuyển sang cách chúng ta có thể gửi khung máy ảnh để xử lý lên đồ thị MediaPipe và hiển thị đầu ra của biểu đồ trên màn hình.
Thiết lập ExternalTextureConverter
SurfaceTexture
chụp khung hình ảnh từ một luồng dưới dạng OpenGL ES
hoạ tiết. Để sử dụng biểu đồ MediaPipe, khung hình được chụp từ máy ảnh phải
được lưu trữ trong đối tượng kết cấu Open GL thông thường. Khung này cung cấp một lớp,
ExternalTextureConverter
để chuyển đổi hình ảnh được lưu trữ trong SurfaceTexture
thành đối tượng hoạ tiết OpenGL thông thường.
Để sử dụng ExternalTextureConverter
, chúng ta cũng cần một EGLContext
,
do đối tượng EglManager
tạo và quản lý. Thêm phần phụ thuộc vào BUILD
để sử dụng EglManager
, "//mediapipe/java/com/google/mediapipe/glutil"
.
Trong MainActivity
, hãy thêm các nội dung khai báo sau:
private EglManager eglManager;
private ExternalTextureConverter converter;
Trong hàm onCreate(Bundle)
, hãy thêm một câu lệnh để khởi chạy
eglManager
trước khi yêu cầu quyền truy cập vào camera:
eglManager = new EglManager(null);
Hãy nhớ rằng chúng ta đã xác định hàm onResume()
trong MainActivity
để xác nhận
quyền truy cập vào camera đã được cấp và gọi startCamera()
. Trước đây
hãy kiểm tra, hãy thêm dòng sau vào onResume()
để khởi chạy converter
đối tượng:
converter = new ExternalTextureConverter(eglManager.getContext());
converter
này hiện sử dụng GLContext
do eglManager
quản lý.
Chúng ta cũng cần ghi đè hàm onPause()
trong MainActivity
để
nếu ứng dụng chuyển sang trạng thái tạm dừng, chúng ta sẽ đóng converter
đúng cách:
@Override
protected void onPause() {
super.onPause();
converter.close();
}
Để chuyển đầu ra của previewFrameTexture
vào converter
, hãy thêm phương thức
khối mã sau vào 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) {}
});
Trong khối mã này, chúng ta thêm một SurfaceHolder.Callback
tuỳ chỉnh vào
previewDisplayView
và triển khai hàm surfaceChanged(SurfaceHolder holder, int
format, int width, int height)
để tính toán kích thước hiển thị phù hợp
khung máy ảnh trên màn hình thiết bị và để buộc previewFrameTexture
và gửi khung của displaySize
đã tính toán đến converter
.
Chúng ta hiện đã sẵn sàng sử dụng khung máy ảnh trong biểu đồ MediaPipe.
Sử dụng biểu đồ MediaPipe trong Android
Thêm phần phụ thuộc có liên quan
Để sử dụng biểu đồ MediaPipe, chúng ta cần thêm các phần phụ thuộc vào khung MediaPipe
trên Android. Trước tiên, chúng ta sẽ thêm quy tắc bản dựng để tạo cc_binary
bằng mã JNI
của khung MediaPipe rồi xây dựng một quy tắc cc_library
để sử dụng tệp nhị phân này
trong ứng dụng của chúng tôi. Thêm khối mã dưới đây vào tệp 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,
)
Thêm phần phụ thuộc ":mediapipe_jni_lib"
vào quy tắc bản dựng mediapipe_lib
trong
tệp BUILD
.
Tiếp theo, chúng ta cần thêm các phần phụ thuộc cụ thể vào biểu đồ MediaPipe mà chúng ta muốn sử dụng trong ứng dụng.
Trước tiên, hãy thêm các phần phụ thuộc vào tất cả mã tính toán trong libmediapipe_jni.so
quy tắc tạo:
"//mediapipe/graphs/edge_detection:mobile_calculators",
Biểu đồ MediaPipe là các tệp .pbtxt
, nhưng để sử dụng chúng trong ứng dụng, chúng ta cần
để sử dụng quy tắc xây dựng mediapipe_binary_graph
nhằm tạo tệp .binarypb
.
Trong quy tắc bản dựng tệp nhị phân helloworld
cho Android, hãy thêm mediapipe_binary_graph
mục tiêu cụ thể cho biểu đồ dưới dạng nội dung:
assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
Trong quy tắc xây dựng assets
, bạn cũng có thể thêm các thành phần khác như TensorFlowLite
mô hình được sử dụng trong biểu đồ của bạn.
Ngoài ra, hãy thêm manifest_values
bổ sung cho những cơ sở lưu trú dành riêng cho
biểu đồ để truy xuất sau trong 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",
},
Lưu ý rằng binaryGraphName
cho biết tên tệp của biểu đồ nhị phân,
được xác định bởi trường output_name
trong mục tiêu mediapipe_binary_graph
.
inputVideoStreamName
và outputVideoStreamName
là dữ liệu đầu vào và đầu ra
tên luồng video tương ứng được chỉ định trong biểu đồ.
MainActivity
hiện cần tải khung MediaPipe. Ngoài ra,
khung sử dụng OpenCV, vì vậy MainActvity
cũng sẽ tải OpenCV
. Sử dụng
mã sau trong MainActivity
(bên trong lớp, nhưng không bên trong bất kỳ hàm nào)
để tải cả hai phần phụ thuộc:
static {
// Load all native libraries needed by the app.
System.loadLibrary("mediapipe_jni");
System.loadLibrary("opencv_java3");
}
Sử dụng biểu đồ trong MainActivity
Trước tiên, chúng ta cần tải thành phần chứa .binarypb
được biên dịch từ
tệp .pbtxt
của biểu đồ. Để thực hiện việc này, chúng ta có thể sử dụng tiện ích MediaPipe,
AndroidAssetUtil
.
Khởi chạy trình quản lý thành phần trong onCreate(Bundle)
trước khi khởi chạy
eglManager
:
// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);
Bây giờ, chúng ta cần thiết lập một đối tượng FrameProcessor
sẽ gửi khung máy ảnh
do converter
chuẩn bị cho biểu đồ MediaPipe rồi chạy biểu đồ, chuẩn bị
kết quả đầu ra rồi cập nhật previewDisplayView
để hiện kết quả. Thêm
mã sau đây để khai báo FrameProcessor
:
private FrameProcessor processor;
và khởi tạo trong onCreate(Bundle)
sau khi khởi tạo eglManager
:
processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
processor
cần sử dụng các khung hình đã chuyển đổi từ converter
để
đang xử lý. Thêm dòng sau vào onResume()
sau khi khởi tạo
converter
:
converter.setConsumer(processor);
processor
cần gửi kết quả đến previewDisplayView
. Để thực hiện việc này, hãy thêm
các định nghĩa hàm sau đây cho SurfaceHolder.Callback
tuỳ chỉnh của chúng ta:
@Override
public void surfaceCreated(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(null);
}
Khi SurfaceHolder
được tạo, chúng ta đã có Surface
cho
VideoSurfaceOutput
/processor
. Khi tài sản bị huỷ, chúng tôi sẽ xoá nó khỏi
VideoSurfaceOutput
của processor
.
Chỉ vậy thôi! Bây giờ, bạn có thể tạo và chạy thành công trên thiết bị và thấy tính năng phát hiện cạnh Sobel chạy trên camera trực tiếp nguồn cấp dữ liệu! Xin chúc mừng!
Nếu bạn gặp bất kỳ sự cố nào, vui lòng xem mã đầy đủ của hướng dẫn tại đây.