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ế |
---|---|---|
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 tại thời điểm 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 tại thời điểm xây dựng. |
Tác động đến kích thước ứng dụng (bao gồm mã và các thành phần) | ~10,1 MB | ~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 thử ứ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 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 một 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 chế độ cài đặt của 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 cách phù hợp với trường hợp sử dụng của bạn.
STREAM_MODE
(mặc định)- 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 trước tiên, sau đó chạy tính năng phát hiện tư thế. Trong các khung hình tiếp theo, bước phát hiện người sẽ không được thực hiện trừ phi người dùng bị che khuất hoặc không còn được phát hiện với độ tin cậy cao. 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 lần suy luận. Điều này làm giảm độ trễ và làm mượt việc phát hiện. Hãy sử 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 và sau đó chạy tính năng phát hiện tư thế. Bước phát hiện người sẽ chạy cho 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. Bạn có thể 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 xây dựng các tuỳ chọn của trình phát hiện, bạn có thể sử dụng API setPreferredHardwareConfigs
để kiểm soát 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 khả năng sử dụng, độ ổn định, độ chính xác và độ trễ của từng cấu hình để chọn cấu hình phù hợp 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à bước chuẩn bị có 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ể là vào 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 toàn bộ quá trình chuẩn bị hoàn tất, cấu hình phù hợp nhất sẽ được dùng trong những lần chạy tiếp theo.
Ví dụ về cách sử dụng setPreferredHardwareConfigs
:
- Để Bộ công cụ học máy chọn cấu hình phù hợp nhất, đừng gọi API này.
- Nếu không muốn bật bất kỳ chế độ tăng tốc nào, bạn chỉ cần truyền
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 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 đang 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 đối tượng InputImage
từ nhiều nguồn, mỗi nguồn được giải thích ở bên dưới.
Sử dụng media.Image
Để tạo đối tượng InputImage
từ đối tượng media.Image
, chẳng hạn như khi bạn chụp ảnh từ 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, thì các lớp OnImageCapturedListener
và ImageAnalysis.Analyzer
sẽ tính giá trị 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 cho biết độ xoay của hình ảnh, bạn có thể tính độ xoay của hình ảnh 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 trong ứ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 độ xoay hình ảnh như mô tả trước đó cho phương thức nhập 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
qua đố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 một đố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 được
Nếu phát hiện thấy một người trong hình ảnh, API phát hiện tư thế sẽ trả về một đối tượng Pose
có 33 PoseLandmark
.
Nếu người này không nằm hoàn toàn bên trong hình ảnh, thì 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ị InFramePriority thấp.
Nếu không phát hiện thấy 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 của kết quả tìm kiếm 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 ảnh phải được biểu thị bằng đủ dữ liệu pixel; để có hiệu suất tốt nhất, chủ thể tối thiểu phải 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 ý 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ể.
- Tiêu điểm ảnh 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ả 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 sử 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ơ bản và
STREAM_MODE
. - Cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, bạn cũng cần 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 các lệnh gọi đến trình phát hiện. Nếu có khung hình 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 khởi động nhanh để biết ví dụ. - Nếu bạn sử dụng API
CameraX
, hãy đảm bảo rằng chiến lược áp lực ngược được đặt về giá trị mặc địnhImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Việc này giúp đảm bảo mỗi lần hệ thống chỉ gửi một hình ảnh để phân tích. Nếu trình phân tích bận, nếu có nhiều hình ảnh khác được tạo, thì 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 hình ảnh đang được phân tích được đóng 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 sử 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, sau đó kết xuất hình ảnh và lớp phủ trong một bước duy nhất. Thao tác này chỉ hiển thị trên bề mặt 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 khởi động nhanh để tham khảo 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 Máy ảnh 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 điểm mốc giúp phân loại các tư thế, hãy xem Mẹo phân loại ống.