ML Kit menyediakan dua SDK yang dioptimalkan untuk deteksi pose.
Nama SDK | deteksi-pose | deteksi-pose akurat |
---|---|---|
Penerapan | Kode dan aset tertaut secara statis ke aplikasi Anda pada waktu build. | Kode dan aset tertaut secara statis ke aplikasi Anda pada waktu build. |
Dampak ukuran aplikasi (termasuk kode dan aset) | ~10,1 MB | ~13,3 MB |
Performa | Pixel 3XL: ~30FPS | Pixel 3XL: ~23FPS dengan CPU, ~30FPS dengan GPU |
Cobalah
- Cobalah aplikasi contoh untuk melihat contoh penggunaan API ini.
Sebelum memulai
- Dalam file
build.gradle
level project, pastikan Anda menyertakan repositori Maven Google di bagianbuildscript
danallprojects
. Tambahkan dependensi untuk library Android ML Kit ke file gradle level aplikasi modul Anda, biasanya
app/build.gradle
:dependencies { // If you want to use the base sdk implementation 'com.google.mlkit:pose-detection:18.0.0-beta5' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta5' }
1. Membuat instance PoseDetector
Opsi PoseDetector
Untuk mendeteksi pose dalam gambar, pertama-tama buat instance PoseDetector
dan
secara opsional, menentukan setelan pendeteksi.
Mode deteksi
PoseDetector
beroperasi dalam dua mode deteksi. Pastikan Anda memilih salah satu yang cocok
kasus penggunaan Anda.
STREAM_MODE
(default)- Detektor pose akan mendeteksi paling banyak orang yang menonjol dalam gambar, lalu menjalankan deteksi pose. Dalam {i>frame<i} berikutnya, langkah deteksi orang tidak akan dilakukan kecuali orang tersebut menjadi dikaburkan atau tidak lagi terdeteksi dengan tingkat keyakinan tinggi. Pendeteksi pose akan cobalah untuk melacak orang yang paling menonjol dan menampilkan pose mereka di masing-masing inferensi. Tindakan ini akan mengurangi latensi dan memperlancar deteksi. Gunakan mode ini saat Anda ingin mendeteksi pose dalam streaming video.
SINGLE_IMAGE_MODE
- Detektor pose akan mendeteksi seseorang, lalu menjalankan pose deteksi. Langkah deteksi orang akan dijalankan untuk setiap gambar, sehingga latensi akan menjadi lebih tinggi, dan tidak ada pelacakan orang. Gunakan mode ini saat menggunakan pose pada gambar statis atau jika pelacakan tidak diinginkan.
Konfigurasi hardware
PoseDetector
mendukung beberapa konfigurasi hardware untuk pengoptimalan
performa:
CPU
: menjalankan detektor hanya menggunakan CPUCPU_GPU
: menjalankan detektor menggunakan CPU dan GPU
Saat membuat opsi detektor, Anda dapat menggunakan API
setPreferredHardwareConfigs
untuk mengontrol pemilihan hardware. Secara {i>default<i},
semua konfigurasi perangkat keras
diatur sesuai keinginan.
ML Kit akan mengambil ketersediaan, stabilitas, ketepatan, dan latensi setiap konfigurasi
mempertimbangkan dan memilih yang terbaik
dari konfigurasi pilihan. Jika tidak satu pun dari
konfigurasi pilihan berlaku, konfigurasi CPU
akan digunakan secara otomatis
sebagai penggantian. ML Kit akan melakukan pemeriksaan ini dan persiapan terkait
non-pemblokiran sebelum mengaktifkan akselerasi apa pun, jadi kemungkinan besar
saat pertama kali pengguna menjalankan detektor, pendeteksi akan menggunakan CPU
. Setelah semua
persiapan selesai, konfigurasi terbaik akan digunakan dalam operasi berikut.
Contoh penggunaan setPreferredHardwareConfigs
:
- Agar ML Kit memilih konfigurasi terbaik, jangan panggil API ini.
- Jika Anda tidak ingin mengaktifkan akselerasi apa pun, teruskan
CPU
saja. - Jika Anda ingin menggunakan GPU untuk menurunkan beban CPU
meskipun GPU bisa lebih lambat, teruskan
hanya dalam
CPU_GPU
.
Tentukan opsi detektor pose:
Kotlin
// Base pose detector with streaming frames, when depending on the pose-detection sdk val options = PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build() // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk val options = AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build()
Java
// Base pose detector with streaming frames, when depending on the pose-detection sdk PoseDetectorOptions options = new PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build(); // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk AccuratePoseDetectorOptions options = new AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build();
Terakhir, buat instance PoseDetector
. Teruskan opsi yang Anda tentukan:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. Menyiapkan gambar input
Untuk mendeteksi pose pada gambar, buat objek InputImage
dari Bitmap
, media.Image
, ByteBuffer
, array byte, atau file di
perangkat. Lalu, teruskan objek InputImage
ke
PoseDetector
.
Untuk deteksi pose, Anda harus menggunakan gambar dengan dimensi minimal 480x360 piksel. Jika Anda mendeteksi pose secara real time, menangkap frame pada resolusi minimum ini dapat membantu mengurangi latensi.
Anda dapat membuat InputImage
dari berbagai sumber, masing-masing akan dijelaskan di bawah ini.
Menggunakan media.Image
Untuk membuat InputImage
dari objek media.Image
, seperti saat Anda mengambil gambar dari
kamera perangkat, teruskan objek media.Image
dan objek
rotasi ke InputImage.fromMediaImage()
.
Jika Anda menggunakan
Library CameraX, OnImageCapturedListener
dan
Class ImageAnalysis.Analyzer
menghitung nilai rotasi
keamanan untuk Anda.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Jika Anda tidak menggunakan pustaka kamera yang memberi derajat rotasi gambar, Anda bisa menghitungnya dari derajat rotasi perangkat dan orientasi kamera sensor di perangkat:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Lalu, teruskan objek media.Image
dan
nilai derajat rotasi ke InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Menggunakan URI file
Untuk membuat InputImage
dari URI file, teruskan konteks aplikasi dan URI file ke
InputImage.fromFilePath()
. Hal ini berguna ketika Anda
gunakan intent ACTION_GET_CONTENT
untuk meminta pengguna memilih
gambar dari aplikasi galeri mereka.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Menggunakan ByteBuffer
atau ByteArray
Untuk membuat InputImage
dari ByteBuffer
atau ByteArray
, hitung gambar terlebih dahulu
derajat rotasi seperti yang dijelaskan sebelumnya untuk input media.Image
.
Lalu, buat objek InputImage
dengan buffer atau array, beserta elemen
tinggi, lebar, format encoding warna, dan derajat rotasi:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Menggunakan Bitmap
Untuk membuat InputImage
dari objek Bitmap
, buat deklarasi berikut:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Gambar direpresentasikan oleh objek Bitmap
bersama dengan derajat rotasi.
3. Memproses gambar
Teruskan objek InputImage
yang telah disiapkan ke metode process
PoseDetector
.
Kotlin
Task<Pose> result = poseDetector.process(image) .addOnSuccessListener { results -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
Task<Pose> result = poseDetector.process(image) .addOnSuccessListener( new OnSuccessListener<Pose>() { @Override public void onSuccess(Pose pose) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Mendapatkan informasi tentang pose yang terdeteksi
Jika seseorang terdeteksi dalam gambar, API deteksi pose akan menampilkan Pose
dengan 33 PoseLandmark
dtk.
Jika orang tersebut tidak sepenuhnya ada dalam gambar, model akan menetapkan koordinat {i>landmark<i} yang hilang di luar {i>frame<i} dan memberinya nilai rendah InFrameConfidence.
Jika tidak ada orang yang terdeteksi dalam frame, Pose
objek tidak berisi PoseLandmark
.
Kotlin
// Get all PoseLandmarks. If no person was detected, the list will be empty val allPoseLandmarks = pose.getAllPoseLandmarks() // Or get specific PoseLandmarks individually. These will all be null if no person // was detected val leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER) val rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER) val leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW) val rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW) val leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST) val rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST) val leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP) val rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP) val leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE) val rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE) val leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE) val rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE) val leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY) val rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY) val leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX) val rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX) val leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB) val rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB) val leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL) val rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL) val leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX) val rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX) val nose = pose.getPoseLandmark(PoseLandmark.NOSE) val leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER) val leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE) val leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER) val rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER) val rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE) val rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER) val leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR) val rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR) val leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH) val rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH)
Java
// Get all PoseLandmarks. If no person was detected, the list will be empty List<PoseLandmark> allPoseLandmarks = pose.getAllPoseLandmarks(); // Or get specific PoseLandmarks individually. These will all be null if no person // was detected PoseLandmark leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER); PoseLandmark rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER); PoseLandmark leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW); PoseLandmark rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW); PoseLandmark leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST); PoseLandmark rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST); PoseLandmark leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP); PoseLandmark rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP); PoseLandmark leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE); PoseLandmark rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE); PoseLandmark leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE); PoseLandmark rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE); PoseLandmark leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY); PoseLandmark rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY); PoseLandmark leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX); PoseLandmark rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX); PoseLandmark leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB); PoseLandmark rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB); PoseLandmark leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL); PoseLandmark rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL); PoseLandmark leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX); PoseLandmark rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX); PoseLandmark nose = pose.getPoseLandmark(PoseLandmark.NOSE); PoseLandmark leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER); PoseLandmark leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE); PoseLandmark leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER); PoseLandmark rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER); PoseLandmark rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE); PoseLandmark rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER); PoseLandmark leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR); PoseLandmark rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR); PoseLandmark leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH); PoseLandmark rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH);
Tips untuk meningkatkan performa
Kualitas hasil bergantung pada kualitas gambar input:
- Agar ML Kit dapat mendeteksi pose secara akurat, orang dalam gambar harus diwakili oleh data piksel yang memadai; untuk performa terbaik, subjek harus berukuran minimal 256x256 piksel.
- Jika Anda mendeteksi pose dalam aplikasi {i>real-time<i}, Anda mungkin juga ingin mempertimbangkan dimensi keseluruhan gambar input. Gambar yang lebih kecil dapat diproses lebih cepat, jadi untuk mengurangi latensi, ambil gambar pada resolusi lebih rendah, tetapi tetap mengingat persyaratan resolusi di atas dan pastikan bahwa subjek menempati sebanyak mungkin gambar.
- Fokus gambar yang buruk juga dapat memengaruhi akurasi. Jika Anda tidak mendapatkan hasil yang dapat diterima, meminta pengguna untuk mengambil ulang gambar.
Jika Anda ingin menggunakan deteksi pose dalam aplikasi real-time, ikuti panduan ini untuk mencapai kecepatan frame terbaik:
- Gunakan SDK deteksi pose dasar dan
STREAM_MODE
. - Sebaiknya ambil gambar dengan resolusi yang lebih rendah. Namun, perhatikan juga persyaratan dimensi gambar API ini.
- Jika Anda menggunakan
Camera
ataucamera2
API, men-throttle panggilan ke detektor. Jika video baru {i>frame<i} menjadi tersedia saat detektor sedang berjalan, hapus {i>frame<i} tersebut. LihatVisionProcessorBase
dalam aplikasi contoh panduan memulai untuk digunakan sebagai contoh. - Jika Anda menggunakan
CameraX
API, pastikan strategi tekanan balik ditetapkan ke nilai defaultnyaImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Hal ini menjamin hanya satu gambar yang akan dikirimkan untuk analisis pada satu waktu. Jika lebih banyak gambar yang dihasilkan ketika penganalisis sedang sibuk, mereka akan dibuang secara otomatis dan tidak diantrekan pengiriman. Setelah gambar yang dianalisis ditutup dengan memanggil ImageProxy.close(), gambar terbaru berikutnya akan dikirimkan. - Jika Anda menggunakan output detektor untuk menempatkan grafik
gambar input, pertama-tama dapatkan hasilnya dari ML Kit, lalu render gambar
dan overlay dalam satu langkah. Tindakan ini merender ke permukaan tampilan
hanya sekali untuk setiap {i>input frame<i}. Lihat
CameraSourcePreview
danGraphicOverlay
dalam aplikasi contoh panduan memulai sebagai contoh. - Jika Anda menggunakan Camera2 API, ambil gambar dengan
Format
ImageFormat.YUV_420_888
. Jika Anda menggunakan Camera API versi lama, ambil gambar dengan FormatImageFormat.NV21
.
Langkah berikutnya
- Untuk mempelajari cara menggunakan penanda pose untuk mengklasifikasikan pose, lihat Tips Klasifikasi Pose.