Bộ công cụ học máy cung cấp 2 SDK được tối ưu hoá để phát hiện tư thế.
Tên SDK | phát hiện tư thế | phát hiện tư thế-chính xác |
---|---|---|
Triển khai | Mã và tài sản được liên kết tĩnh với ứng dụng của bạn trong thời gian xây dựng. | Mã và tài sản được liên kết tĩnh với ứng dụng của bạn trong thời gian xây dựng. |
Mức độ ảnh hưởng đến kích thước ứng dụng (bao gồm cả mã và thành phần) | Khoảng 10,1 MB | Khoảng 13,3 MB |
Hiệu suất | Pixel 3XL: ~30 khung hình/giây | Pixel 3XL: ~23 khung hình/giây với CPU, ~30 khung hình/giây với GPU |
Dùng thử
- Hãy khám phá ứng dụng mẫu để xem ví dụ về cách sử dụng API này.
Trước khi bắt đầu
- Trong tệp
build.gradle
cấp dự án, hãy nhớ đưa kho lưu trữ Maven của Google vào cả hai phầnbuildscript
vàallprojects
. Thêm các phần phụ thuộc cho thư viện Android của Bộ công cụ học máy vào tệp gradle cấp ứng dụng của mô-đun, thường là
app/build.gradle
:dependencies { // If you want to use the base sdk implementation 'com.google.mlkit:pose-detection:18.0.0-beta4' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta4' }
1. Tạo một thực thể của PoseDetector
PoseDetector
lựa chọn
Để phát hiện tư thế trong hình ảnh, trước tiên, hãy tạo một thực thể của PoseDetector
và tuỳ ý chỉ định các chế độ cài đặt trình phát hiện.
Chế độ phát hiện
PoseDetector
hoạt động ở 2 chế độ phát hiện. Hãy nhớ chọn một cách phù hợp với trường hợp sử dụng của bạn.
STREAM_MODE
(mặc định)- Trước tiên, trình phát hiện tư thế sẽ phát hiện người nổi bật nhất trong hình ảnh, sau đó chạy tính năng phát hiện tư thế. Trong các khung tiếp theo, bước phát hiện người sẽ không được thực hiện trừ phi người đó bị che khuất hoặc không còn có độ tin cậy cao được phát hiện. Trình phát hiện tư thế sẽ cố gắng theo dõi người nổi bật nhất và trả về tư thế của họ trong mỗi dự đoán. Điều này làm giảm độ trễ và tạo mượt mà việc phát hiện. Hãy dùng chế độ này khi bạn muốn phát hiện tư thế trong luồng video.
SINGLE_IMAGE_MODE
- Trình phát hiện tư thế sẽ phát hiện một người, sau đó chạy tính năng phát hiện tư thế. Bước phát hiện người sẽ chạy trên mọi hình ảnh, vì vậy, độ trễ sẽ cao hơn và không có hoạt động theo dõi người. Hãy dùng chế độ này khi sử dụng tính năng phát hiện tư thế trên ảnh tĩnh hoặc khi không muốn theo dõi.
Cấu hình phần cứng
PoseDetector
hỗ trợ nhiều cấu hình phần cứng để tối ưu hoá hiệu suất:
CPU
: chạy trình phát hiện bằng cách chỉ sử dụng CPUCPU_GPU
: chạy trình phát hiện bằng cách sử dụng cả CPU và GPU
Khi tạo các tuỳ chọn cho trình phát hiện, bạn có thể sử dụng API setPreferredHardwareConfigs
để kiểm soát việc lựa chọn phần cứng. Theo mặc định, tất cả các cấu hình phần cứng đều được đặt ở chế độ ưu tiên.
Bộ công cụ học máy sẽ xem xét tình trạng sẵn có, độ ổn định, độ chính xác và độ trễ của từng cấu hình, rồi chọn cấu hình tốt nhất trong số các cấu hình ưu tiên. Nếu không có cấu hình ưu tiên nào được áp dụng, thì cấu hình CPU
sẽ tự động được dùng làm cấu hình dự phòng. Bộ công cụ học máy sẽ thực hiện các bước kiểm tra này và các bước chuẩn bị liên quan theo cách không chặn trước khi bật bất kỳ tính năng tăng tốc nào, vì vậy, rất có thể đây là lần đầu tiên người dùng chạy trình phát hiện, trình phát hiện sẽ sử dụng CPU
. Sau khi hoàn tất mọi bước chuẩn bị, cấu hình tốt nhất sẽ được sử dụng trong các lần chạy sau.
Ví dụ về cách sử dụng setPreferredHardwareConfigs
:
- Để cho phép Bộ công cụ học máy chọn cấu hình tốt nhất, vui lòng không gọi API này.
- Nếu bạn không muốn bật bất kỳ tính năng tăng tốc nào, hãy chỉ truyền vào
CPU
. - Nếu bạn muốn sử dụng GPU để giảm tải CPU ngay cả khi GPU có thể chậm hơn, hãy chỉ truyền
CPU_GPU
.
Chỉ định các tuỳ chọn của trình phát hiện tư thế:
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();
Cuối cùng, hãy tạo một thực thể của PoseDetector
. Chuyển các tuỳ chọn mà bạn đã chỉ định:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. Chuẩn bị hình ảnh đầu vào
Để phát hiện các tư thế trong hình ảnh, hãy tạo một đối tượng InputImage
từ Bitmap
, media.Image
, ByteBuffer
, mảng byte hoặc một tệp trên thiết bị. Sau đó, hãy truyền đối tượng InputImage
đến PoseDetector
.
Để phát hiện tư thế, bạn nên sử dụng hình ảnh có kích thước tối thiểu là 480x360 pixel. Nếu bạn phát hiện các tư thế theo thời gian thực, thì việc chụp khung hình ở độ phân giải tối thiểu này có thể giúp giảm độ trễ.
Bạn có thể tạo một đối tượng InputImage
từ nhiều nguồn. Mỗi nguồn sẽ được giải thích ở bên dưới.
Sử dụng media.Image
Để tạo một đối tượng InputImage
từ đối tượng media.Image
, chẳng hạn như khi bạn chụp ảnh bằng máy ảnh của thiết bị, hãy truyền đối tượng media.Image
và chế độ xoay của hình ảnh đến InputImage.fromMediaImage()
.
Nếu bạn sử dụng thư viện
CameraX, các lớp OnImageCapturedListener
và ImageAnalysis.Analyzer
sẽ tính giá trị chế độ xoay cho bạn.
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 // ... } } }
Nếu không sử dụng thư viện máy ảnh để cung cấp độ xoay của hình ảnh, bạn có thể tính độ xoay này dựa trên độ xoay của thiết bị và hướng của cảm biến máy ảnh trong thiết bị:
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; }
Sau đó, hãy truyền đối tượng media.Image
và giá trị độ xoay đến InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Sử dụng URI tệp
Để tạo đối tượng InputImage
từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp đến InputImage.fromFilePath()
. Điều này rất hữu ích khi bạn sử dụng ý định ACTION_GET_CONTENT
để nhắc người dùng chọn một hình ảnh từ ứng dụng thư viện của họ.
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(); }
Sử dụng ByteBuffer
hoặc ByteArray
Để tạo một đối tượng InputImage
từ ByteBuffer
hoặc ByteArray
, trước tiên, hãy tính toán độ xoay hình ảnh như mô tả trước đó cho dữ liệu đầu vào media.Image
.
Sau đó, hãy tạo đối tượng InputImage
bằng vùng đệm hoặc mảng, cùng với chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay của hình ảnh:
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 );
Sử dụng Bitmap
Để tạo đối tượng InputImage
từ đối tượng Bitmap
, hãy khai báo sau:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Hình ảnh được biểu thị bằng đối tượng Bitmap
cùng với độ xoay.
3. Xử lý hình ảnh
Truyền đối tượng InputImage
đã chuẩn bị vào phương thức process
của 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. Xem thông tin về tư thế đã phát hiện
Nếu phát hiện một người trong hình ảnh, API phát hiện tư thế sẽ trả về đối tượng Pose
có 33 PoseLandmark
.
Nếu người đó không nằm hoàn toàn bên trong hình ảnh, mô hình sẽ chỉ định toạ độ của các điểm mốc bị thiếu bên ngoài khung và cung cấp cho họ các giá trị InFrameDuration thấp.
Nếu không phát hiện người nào trong khung hình thì đối tượng Pose
sẽ không chứa 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);
Mẹo cải thiện hiệu suất
Chất lượng kết quả phụ thuộc vào chất lượng của hình ảnh đầu vào:
- Để Bộ công cụ học máy phát hiện chính xác tư thế, người trong hình ảnh phải được biểu thị bằng đủ dữ liệu pixel; để có hiệu suất tốt nhất, chủ thể phải có kích thước ít nhất là 256x256 pixel.
- Nếu phát hiện tư thế trong ứng dụng theo thời gian thực, bạn cũng nên xem xét kích thước tổng thể của hình ảnh đầu vào. Hình ảnh nhỏ hơn có thể được xử lý nhanh hơn. Vì vậy, để giảm độ trễ, hãy chụp ảnh ở độ phân giải thấp hơn, nhưng hãy lưu ý đến các yêu cầu về độ phân giải trên và đảm bảo rằng đối tượng chiếm nhiều hình ảnh nhất có thể.
- Hình ảnh lấy nét kém cũng có thể ảnh hưởng đến độ chính xác. Nếu bạn không nhận được kết quả có thể chấp nhận được, hãy yêu cầu người dùng chụp lại hình ảnh.
Nếu bạn muốn dùng tính năng phát hiện tư thế trong ứng dụng theo thời gian thực, hãy làm theo các nguyên tắc sau để đạt được tốc độ khung hình tốt nhất:
- Sử dụng sdk phát hiện tư thế cơ sở và
STREAM_MODE
. - Hãy cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, hãy lưu ý các yêu cầu về kích thước hình ảnh của API này.
- Nếu bạn sử dụng API
Camera
hoặccamera2
, hãy điều tiết lệnh gọi đến trình phát hiện. Nếu có khung video mới trong khi trình phát hiện đang chạy, hãy bỏ khung hình đó. Hãy xem lớpVisionProcessorBase
trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ. - Nếu bạn sử dụng API
CameraX
, hãy nhớ đặt chiến lược backpressure về giá trị mặc địnhImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Điều này đảm bảo mỗi lần chỉ gửi một hình ảnh để phân tích. Nếu thêm hình ảnh được tạo khi trình phân tích bận, các hình ảnh đó sẽ tự động bị loại bỏ và không được đưa vào hàng đợi phân phối. Sau khi bạn đóng hình ảnh đang được phân tích bằng cách gọi ImageProxy.close(), hình ảnh mới nhất tiếp theo sẽ được phân phối. - Nếu bạn dùng đầu ra của trình phát hiện để phủ đồ hoạ lên hình ảnh đầu vào, trước tiên, hãy lấy kết quả từ Bộ công cụ học máy rồi kết xuất hình ảnh và lớp phủ chỉ trong một bước. API này chỉ kết xuất trên nền tảng màn hình một lần cho mỗi khung đầu vào. Hãy xem các lớp
CameraSourcePreview
vàGraphicOverlay
trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ. - Nếu bạn sử dụng API Camera2, hãy chụp ảnh ở định dạng
ImageFormat.YUV_420_888
. Nếu bạn sử dụng API Camera cũ, hãy chụp ảnh ở định dạngImageFormat.NV21
.
Các bước tiếp theo
- Để tìm hiểu cách sử dụng các điểm mốc tạo tư thế để phân loại các tư thế, hãy xem Mẹo phân loại tư thế.