Phát hiện tư thế bằng Bộ công cụ học máy trên Android

Bộ công cụ học máy cung cấp hai SDK được tối ưu hoá để phát hiện tư thế.

Tên SDKphát hiện tư thếpose-detection-accurate
Triển khaiMã 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 tạo bản 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 tạo bản dựng.
Mức tác động của kích thước ứng dụng (bao gồm cả mã và tài sản)~10,1 MB~13,3 MB
Hiệu suấtPixel 3XL: ~30 khung hình/giâyPixel 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ử nghiệm với ứng dụng mẫu để xem ví dụ về cách sử dụng API này.

Trước khi bắt đầu

  1. Trong tệp build.gradle cấp dự án, hãy nhớ thêm kho lưu trữ Maven của Google vào cả mục buildscriptallprojects.
  2. 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-beta5'
      // If you want to use the accurate sdk
      implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta5'
    }
    

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à tùy ý chỉ định chế độ cài đặt của trình phát hiện.

Chế độ phát hiện

PoseDetector hoạt động ở hai chế độ phát hiện. Hãy nhớ chọn một loại 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 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 đó 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 người đó trong mỗi lần suy luận. Điều này giúp giảm độ trễ và giúp quá trình phát hiện diễn ra suôn sẻ. 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 rồi 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ó tính năng theo dõi người. Sử dụng chế độ này khi sử dụng tính năng phát hiện tư thế trên hình ả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ỉ chạy trình phát hiện bằng CPU
  • CPU_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 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ấu hình phần cứng đều được đặt là ư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 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 áp dụng được, cấu hình CPU sẽ tự động được dùng làm 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à 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ể trong 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 tất cả các bước chuẩn bị hoàn tất, 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, đừ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 vào 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. Truyề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 tư thế trong hình ảnh, hãy tạo đối tượng InputImage từ Bitmap, media.Image, ByteBuffer, mảng byte hoặc 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 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à độ xoay của hình ảnh đến InputImage.fromMediaImage().

Nếu bạn sử dụng thư viện CameraX, các lớp OnImageCapturedListenerImageAnalysis.Analyzer sẽ tính toán 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 cung cấp cho bạn độ xoay của hình ảnh, bạn có thể tính toán độ xoay đó từ độ 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 đó, truyền đối tượng media.Image và giá trị độ xoay vào 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 truyền ngữ cảnh ứng dụng và URI tệp đến InputImage.fromFilePath(). Điều này 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.

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 đố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 dữ liệu đầu vào media.Image. Sau đó, hãy tạo đối tượng InputImage bằng bộ nhớ đệ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 như 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. Nhận thông tin về tư thế được phát hiện

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 đó không hoàn toàn nằm trong hình ảnh, thì mô hình sẽ gán các toạ độ điểm đánh dấu bị thiếu bên ngoài khung hình và cung cấp cho các toạ độ đó giá trị InFrameConfidence 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ả 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 đầy đủ; để đạt được hiệu suất tốt nhất, đối tượng phải có kích thước tối thiểu là 256x256 pixel.
  • Nếu phát hiện tư thế trong một ứng dụng theo thời gian thực, bạn cũng nên cân nhắc 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 không gian hình ảnh nhất có thể.
  • Độ nét của hình ảnh 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 một ứ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, hãy lưu ý đến 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ặc camera2, 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 thả khung hình đó. Hãy xem lớp VisionProcessorBase 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 áp lực ngược thành giá trị mặc định là ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Điều này đảm bảo rằng mỗi lần chỉ có một hình ảnh được phân phối để phân tích. Nếu có nhiều hình ảnh được tạo khi trình phân tích đang bận, 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 kết quả của trình phát hiện để phủ hình ảnh đồ 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à phủ trong một bước. Việc này chỉ kết xuất một lần cho mỗi khung đầu vào trên bề mặt hiển thị. Hãy xem các lớp CameraSourcePreview 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 Máy ảnh cũ, hãy chụp ảnh ở định dạng ImageFormat.NV21.

Các bước tiếp theo

  • Để tìm hiểu cách sử dụng các điểm đánh dấu tư thế để phân loại tư thế, hãy xem bài viết Mẹo phân loại tư thế.