Pengantar
Halo Dunia! menggunakan Kerangka Kerja MediaPipe untuk mengembangkan aplikasi Android yang menjalankan grafik MediaPipe di Android.
Yang akan Anda build
Aplikasi kamera sederhana untuk deteksi tepi Sobel secara real-time yang diterapkan ke video live melakukan streaming di perangkat Android.
Penyiapan
- Instal Framework MediaPipe di sistem Anda, lihat Penginstalan framework untuk mengetahui detailnya.
- Instal Android Development SDK dan Android NDK. Lihat cara melakukannya juga di [Panduan penginstalan framework].
- Aktifkan opsi developer di perangkat Android Anda.
- Siapkan Bazel di sistem Anda untuk membangun dan men-deploy aplikasi Android.
Grafik untuk deteksi tepi
Kita akan menggunakan grafik berikut, 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"
}
Visualisasi grafik ditampilkan di bawah ini:
Grafik ini memiliki satu aliran input bernama input_video
untuk semua frame yang masuk
yang akan disediakan oleh kamera perangkat Anda.
Node pertama dalam grafik, LuminanceCalculator
, mengambil satu paket (gambar
) dan menerapkan perubahan luminans menggunakan shader OpenGL. Hasil
frame gambar dikirim ke aliran data output luma_video
.
Node kedua, SobelEdgesCalculator
, menerapkan deteksi edge ke peristiwa masuk
paket di aliran data luma_video
dan menghasilkan output berupa output output_video
feed.
Aplikasi Android kita akan menampilkan bingkai gambar output dari
Feed output_video
.
Penyiapan aplikasi minimal awal
Pertama-tama, kita mulai dengan aplikasi Android sederhana yang menampilkan "Hello World!" (Halo Dunia)
di layar. Anda dapat melewati langkah ini jika sudah terbiasa membangun Android
aplikasi menggunakan bazel
.
Buat direktori baru tempat Anda akan membuat aplikasi Android. Sebagai
contoh, kode lengkap dari tutorial ini dapat ditemukan di
mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic
. Kami akan
lihat jalur ini sebagai $APPLICATION_PATH
di seluruh codelab.
Perhatikan bahwa dalam jalur ke aplikasi:
- Aplikasinya bernama
helloworld
. $PACKAGE_PATH
aplikasi adalahcom.google.mediapipe.apps.basic
. Ini digunakan dalam cuplikan kode di tutorial ini, jadi harap ingat untuk menggunakan$PACKAGE_PATH
Anda sendiri saat menyalin/menggunakan cuplikan kode.
Tambahkan file activity_main.xml
ke $APPLICATION_PATH/res/layout
. Ini menampilkan
TextView
di layar penuh aplikasi dengan string 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>
Menambahkan MainActivity.java
sederhana ke $APPLICATION_PATH
yang memuat konten
dari tata letak activity_main.xml
seperti yang ditunjukkan di bawah ini:
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);
}
}
Menambahkan file manifes, AndroidManifest.xml
ke $APPLICATION_PATH
, yang
meluncurkan MainActivity
saat aplikasi dimulai:
<?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>
Di aplikasi, kita menggunakan tema Theme.AppCompat
di aplikasi sehingga kita perlu
referensi tema yang tepat. Tambahkan colors.xml
ke
$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>
Tambahkan styles.xml
ke $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>
Untuk membangun aplikasi, tambahkan file BUILD
ke $APPLICATION_PATH
, dan
${appName}
dan ${mainActivity}
dalam manifes akan diganti dengan string
ditentukan di BUILD
seperti yang ditunjukkan di bawah ini.
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",
],
)
Aturan android_library
menambahkan dependensi untuk MainActivity
, file resource
dan AndroidManifest.xml
.
Aturan android_binary
, menggunakan library Android basic_lib
yang dibuat untuk
membuat APK biner untuk diinstal di perangkat Android Anda.
Untuk membangun aplikasi, gunakan perintah berikut:
bazel build -c opt --config=android_arm64 $APPLICATION_PATH:helloworld
Instal file APK yang dihasilkan menggunakan adb install
. Contoh:
adb install bazel-bin/$APPLICATION_PATH/helloworld.apk
Buka aplikasi di perangkat Anda. Layar akan ditampilkan dengan teks
Hello World!
.
Menggunakan kamera melalui CameraX
Izin Akses Kamera
Untuk menggunakan kamera di aplikasi, kita perlu meminta pengguna untuk menyediakan
akses ke kamera. Untuk meminta izin kamera, tambahkan hal berikut ke
AndroidManifest.xml
:
<!-- For using the camera -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
Ubah versi SDK minimum menjadi 21
dan versi SDK target ke 27
di
file yang sama:
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="27" />
Hal ini memastikan bahwa pengguna diminta untuk meminta izin kamera dan mengaktifkan kami untuk menggunakan library CameraX untuk akses kamera.
Untuk meminta izin kamera, kita dapat menggunakan utilitas yang disediakan oleh MediaPipe Framework
komponen, yaitu PermissionHelper
. Untuk menggunakannya, tambahkan dependensi
"//mediapipe/java/com/google/mediapipe/components:android_components"
dalam
mediapipe_lib
aturan di BUILD
.
Untuk menggunakan PermissionHelper
di MainActivity
, tambahkan baris berikut ke elemen
Fungsi onCreate
:
PermissionHelper.checkAndRequestCameraPermissions(this);
Tindakan ini meminta izin kepada pengguna dengan dialog di layar untuk menggunakan kamera di aplikasi ini.
Tambahkan kode berikut untuk menangani respons pengguna:
@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() {}
Biarkan metode startCamera()
kosong untuk saat ini. Ketika pengguna merespons
perintah tersebut, MainActivity
akan dilanjutkan dan onResume()
akan dipanggil.
Kode itu akan mengonfirmasi bahwa izin akses
untuk menggunakan kamera telah diberikan,
kemudian kita akan memulai kamera.
Build ulang dan instal aplikasi. Anda akan melihat dialog yang meminta akses ke kamera untuk aplikasi.
Akses Kamera
Dengan izin kamera yang tersedia, kita dapat memulai dan mengambil {i>frame<i} dari kamera.
Untuk melihat frame dari kamera, kita akan menggunakan SurfaceView
. Setiap frame
dari kamera akan disimpan dalam objek SurfaceTexture
. Untuk menggunakannya, kita
kita perlu mengubah
tata letak aplikasi kita terlebih dahulu.
Hapus seluruh blok kode TextView
dari
$APPLICATION_PATH/res/layout/activity_main.xml
dan tambahkan kode berikut
sebagai gantinya:
<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>
Blok kode ini memiliki FrameLayout
baru bernama preview_display_layout
dan
TextView
bersarang di dalamnya, bernama no_camera_access_preview
. Saat kamera
izin akses tidak diberikan, aplikasi kita akan menampilkan
TextView
dengan pesan string, disimpan dalam variabel no_camera_access
.
Tambahkan baris berikut di file $APPLICATION_PATH/res/values/strings.xml
:
<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
Jika pengguna tidak memberikan izin kamera, layar sekarang akan terlihat seperti ini:
Sekarang, kita akan menambahkan objek SurfaceTexture
dan SurfaceView
ke
MainActivity
:
private SurfaceTexture previewFrameTexture;
private SurfaceView previewDisplayView;
Pada fungsi onCreate(Bundle)
, tambahkan dua baris berikut sebelum
meminta izin kamera:
previewDisplayView = new SurfaceView(this);
setupPreviewDisplayView();
Sekarang tambahkan kode yang menentukan setupPreviewDisplayView()
:
private void setupPreviewDisplayView() {
previewDisplayView.setVisibility(View.GONE);
ViewGroup viewGroup = findViewById(R.id.preview_display_layout);
viewGroup.addView(previewDisplayView);
}
Kita menentukan objek SurfaceView
baru dan menambahkannya ke
preview_display_layout
FrameLayout
agar kita dapat menggunakannya untuk menampilkan
frame kamera menggunakan objek SurfaceTexture
bernama previewFrameTexture
.
Untuk menggunakan previewFrameTexture
guna mendapatkan bingkai kamera, kita akan menggunakan CameraX.
Framework menyediakan utilitas bernama CameraXPreviewHelper
untuk menggunakan CameraX.
Class ini mengupdate pemroses saat kamera dimulai melalui
onCameraStarted(@Nullable SurfaceTexture)
.
Untuk menggunakan utilitas ini, ubah file BUILD
untuk menambahkan dependensi pada
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper"
.
Sekarang impor CameraXPreviewHelper
dan tambahkan baris berikut ke
MainActivity
:
private CameraXPreviewHelper cameraHelper;
Sekarang, kita dapat menambahkan implementasi kita ke 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);
});
}
Tindakan ini akan membuat objek CameraXPreviewHelper
baru dan menambahkan objek anonim
pemroses pada objek. Saat cameraHelper
memberikan sinyal bahwa kamera telah dimulai
dan surfaceTexture
untuk mengambil {i>frame<i} juga tersedia, kita menyimpannya
surfaceTexture
sebagai previewFrameTexture
, dan buat previewDisplayView
terlihat sehingga kita bisa mulai melihat frame dari previewFrameTexture
.
Namun, sebelum memulai kamera, kita
perlu memutuskan kamera mana
gunakan. CameraXPreviewHelper
mewarisi dari CameraHelper
yang memberikan dua
opsi, FRONT
dan BACK
. Kita dapat meneruskan keputusan dari file BUILD
seperti metadata sehingga tidak diperlukan perubahan kode untuk membangun versi lain dari
aplikasi menggunakan kamera yang berbeda.
Dengan asumsi kita ingin menggunakan kamera BACK
untuk melakukan deteksi tepi pada adegan live
yang dilihat dari kamera, tambahkan metadata ke AndroidManifest.xml
:
...
<meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>
</application>
</manifest>
dan tentukan pemilihan di BUILD
dalam aturan biner android helloworld
dengan entri baru di manifest_values
:
manifest_values = {
"applicationId": "com.google.mediapipe.apps.basic",
"appName": "Hello World",
"mainActivity": ".MainActivity",
"cameraFacingFront": "False",
},
Sekarang, di MainActivity
untuk mengambil metadata yang ditentukan di manifest_values
,
tambahkan objek ApplicationInfo
:
private ApplicationInfo applicationInfo;
Dalam fungsi onCreate()
, tambahkan:
try {
applicationInfo =
getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
} catch (NameNotFoundException e) {
Log.e(TAG, "Cannot find application info: " + e);
}
Sekarang tambahkan baris berikut di akhir fungsi startCamera()
:
CameraHelper.CameraFacing cameraFacing =
applicationInfo.metaData.getBoolean("cameraFacingFront", false)
? CameraHelper.CameraFacing.FRONT
: CameraHelper.CameraFacing.BACK;
cameraHelper.startCamera(this, cameraFacing, /*unusedSurfaceTexture=*/ null);
Pada tahap ini, aplikasi akan berhasil dibuat. Namun, ketika Anda menjalankan
aplikasi di perangkat, Anda akan melihat layar hitam (meskipun kamera
izin telah diberikan). Hal ini karena meskipun kita menyimpan
Variabel surfaceTexture
disediakan oleh CameraXPreviewHelper
,
previewSurfaceView
belum menggunakan outputnya dan menampilkannya di layar.
Karena kita ingin menggunakan {i>frame<i} dalam grafik MediaPipe, kita tidak akan menambahkan kode ke melihat output kamera langsung dalam tutorial ini. Sebagai gantinya, kita langsung membahas cara kita dapat mengirim {i>frame<i} kamera untuk diproses ke grafik MediaPipe dan menampilkan yang dihasilkan dari grafik di layar.
Penyiapan ExternalTextureConverter
SurfaceTexture
menangkap frame gambar dari streaming sebagai OpenGL ES
tekstur. Untuk menggunakan grafik MediaPipe, bingkai yang diambil dari kamera harus
disimpan dalam objek tekstur Open GL biasa. Framework ini menyediakan kelas,
ExternalTextureConverter
untuk mengonversi gambar yang disimpan di SurfaceTexture
ke objek tekstur OpenGL reguler.
Untuk menggunakan ExternalTextureConverter
, kita juga memerlukan EGLContext
, yang
yang dibuat dan dikelola oleh objek EglManager
. Menambahkan dependensi ke BUILD
file ini untuk menggunakan EglManager
, "//mediapipe/java/com/google/mediapipe/glutil"
.
Di MainActivity
, tambahkan deklarasi berikut:
private EglManager eglManager;
private ExternalTextureConverter converter;
Dalam fungsi onCreate(Bundle)
, tambahkan pernyataan untuk melakukan inisialisasi pada
Objek eglManager
sebelum meminta izin kamera:
eglManager = new EglManager(null);
Ingat bahwa kita telah menentukan fungsi onResume()
di MainActivity
untuk mengonfirmasi
izin kamera telah diberikan dan memanggil startCamera()
. Sebelumnya
centang, tambahkan baris berikut di onResume()
untuk melakukan inisialisasi converter
:
converter = new ExternalTextureConverter(eglManager.getContext());
converter
ini sekarang menggunakan GLContext
yang dikelola oleh eglManager
.
Kita juga perlu mengganti fungsi onPause()
di MainActivity
sehingga
jika aplikasi berstatus dijeda, kita menutup converter
dengan benar:
@Override
protected void onPause() {
super.onPause();
converter.close();
}
Untuk menyalurkan output previewFrameTexture
ke converter
, tambahkan
blok kode berikut ke 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) {}
});
Di blok kode ini, kita menambahkan SurfaceHolder.Callback
kustom untuk
previewDisplayView
dan mengimplementasikan fungsi surfaceChanged(SurfaceHolder holder, int
format, int width, int height)
untuk menghitung ukuran tampilan yang sesuai
frame kamera di layar perangkat dan untuk mengaitkan previewFrameTexture
dan mengirim frame displaySize
yang telah dihitung ke converter
.
Sekarang kita siap menggunakan frame kamera dalam grafik MediaPipe.
Menggunakan grafik MediaPipe di Android
Menambahkan dependensi yang relevan
Untuk menggunakan grafik MediaPipe, kita perlu menambahkan dependensi ke framework MediaPipe.
di Android. Pertama-tama, kita akan menambahkan aturan build untuk membangun cc_binary
menggunakan kode JNI
framework MediaPipe, lalu membuat aturan cc_library
untuk menggunakan biner ini
dalam aplikasi kita. Tambahkan blok kode berikut ke file BUILD
Anda:
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,
)
Tambahkan dependensi ":mediapipe_jni_lib"
ke aturan build mediapipe_lib
di
file BUILD
.
Selanjutnya, kita perlu menambahkan dependensi khusus untuk grafik MediaPipe yang ingin digunakan dalam aplikasi.
Pertama, tambahkan dependensi ke semua kode kalkulator di libmediapipe_jni.so
aturan build:
"//mediapipe/graphs/edge_detection:mobile_calculators",
Grafik MediaPipe adalah file .pbtxt
, tetapi untuk menggunakannya dalam aplikasi, kita perlu
untuk menggunakan aturan build mediapipe_binary_graph
untuk menghasilkan file .binarypb
.
Dalam aturan build biner Android helloworld
, tambahkan mediapipe_binary_graph
menargetkan khusus untuk grafik sebagai aset:
assets = [
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
],
assets_dir = "",
Di aturan build assets
, Anda juga dapat menambahkan aset lain seperti TensorFlowLite
yang digunakan dalam grafik Anda.
Selain itu, tambahkan manifest_values
tambahan untuk properti khusus untuk
grafik, yang nantinya dapat diambil di 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",
},
Perhatikan bahwa binaryGraphName
menunjukkan nama file dari grafik biner,
ditentukan oleh kolom output_name
di target mediapipe_binary_graph
.
inputVideoStreamName
dan outputVideoStreamName
adalah input dan output
nama streaming video yang ditentukan masing-masing dalam grafik.
Sekarang, MainActivity
perlu memuat framework MediaPipe. Selain itu,
framework menggunakan OpenCV, jadi MainActvity
juga harus memuat OpenCV
. Gunakan
kode berikut di MainActivity
(di dalam class, tetapi tidak di dalam fungsi apa pun)
untuk memuat kedua dependensi:
static {
// Load all native libraries needed by the app.
System.loadLibrary("mediapipe_jni");
System.loadLibrary("opencv_java3");
}
Menggunakan grafik dalam MainActivity
Pertama, kita perlu memuat aset yang berisi .binarypb
yang dikompilasi dari
file .pbtxt
dari grafik. Untuk melakukan ini, kita dapat
menggunakan utilitas MediaPipe,
AndroidAssetUtil
Melakukan inisialisasi pengelola aset di onCreate(Bundle)
sebelum melakukan inisialisasi
eglManager
:
// Initialize asset manager so that MediaPipe native libraries can access the app assets, e.g.,
// binary graphs.
AndroidAssetUtil.initializeNativeAssetManager(this);
Sekarang, kita perlu menyiapkan objek FrameProcessor
yang mengirim frame kamera
disiapkan oleh converter
ke grafik MediaPipe dan menjalankan grafik, menyiapkan
output, lalu mengupdate previewDisplayView
untuk menampilkan output. Tambah
kode berikut untuk mendeklarasikan FrameProcessor
:
private FrameProcessor processor;
dan lakukan inisialisasi di onCreate(Bundle)
setelah melakukan inisialisasi eglManager
:
processor =
new FrameProcessor(
this,
eglManager.getNativeContext(),
applicationInfo.metaData.getString("binaryGraphName"),
applicationInfo.metaData.getString("inputVideoStreamName"),
applicationInfo.metaData.getString("outputVideoStreamName"));
processor
perlu menggunakan frame yang dikonversi dari converter
untuk
diproses. Tambahkan baris berikut ke onResume()
setelah melakukan inisialisasi
converter
:
converter.setConsumer(processor);
processor
harus mengirimkan outputnya ke previewDisplayView
. Untuk melakukannya, tambahkan
definisi fungsi berikut ke SurfaceHolder.Callback
kustom kita:
@Override
public void surfaceCreated(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
processor.getVideoSurfaceOutput().setSurface(null);
}
Saat SurfaceHolder
dibuat, kita memiliki Surface
ke
VideoSurfaceOutput
dari processor
. Setelah dihancurkan, kita menghapusnya dari
VideoSurfaceOutput
dari processor
.
Selesai. Sekarang Anda telah berhasil membangun dan menjalankan di perangkat dan melihat deteksi tepi Sobel yang berjalan di kamera live umpan! Selamat!
Jika Anda mengalami masalah, lihat kode lengkap tutorial di sini.