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 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à thành phầ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ớ đưa kho lưu trữ Maven của Google vào cả hai phần 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 ở 2 chế độ phát hiện. Hãy nhớ chọn phương thức phù hợp 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 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 làm giảm độ trễ và làm mượt việc phát hiện. 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 nên độ trễ sẽ cao hơn và không có hoạt động theo dõi người dùng. Sử dụng chế độ này khi tập tư thế phát hiện trên hình ảnh tĩnh hoặc nơi 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 được đặt ở chế độ ưu tiên.

Bộ công cụ học máy sẽ lấy thông tin về khả năng sử dụng, độ ổn định, độ chính xác và độ trễ của từng cấu hình cân nhắc và 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ác cấu hình ưu tiên có thể áp dụng, thì 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:

  • Để 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 truyền chỉ trong 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 một hình ảnh, hãy tạo một đối tượng InputImage từ một mảng Bitmap, media.Image, ByteBuffer, byte hoặc một tệp trên thiết bị. Sau đó, hãy truyền đối tượng InputImage vào phương thức 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

Cách tạo InputImage từ đối tượng media.Image, chẳng hạn như khi bạn chụp ảnh từ một camera của thiết bị, hãy truyền đối tượng media.Image và xoay thành InputImage.fromMediaImage().

Nếu bạn sử dụng Thư viện CameraX, OnImageCapturedListener và Các lớp ImageAnalysis.Analyzer 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 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 bức ả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

Cách tạo InputImage đối tượng từ ByteBuffer hoặc ByteArray, trước tiên, hãy tính hình ảnh độ xoay như mô tả trước đây cho đầ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 ứng dụng theo thời gian thực, có thể bạn cũng muốn cân nhắc kích thước tổng thể của hình ảnh đầu vào. Có thể xử lý hình ảnh nhỏ hơn nhanh hơn, vì vậy để giảm độ trễ, hãy chụp ảnh ở độ phân giải thấp hơn, nhưng vẫn chú ý đến các yêu cầu giải quyết ở trên và đảm bảo rằng đối tượng chiếm phần hình ảnh nhiều 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 ứ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ác hình ảnh khác được tạo ra khi trình phân tích bận, chúng sẽ tự động bị loại bỏ và không được đưa vào hàng đợi của bạn. 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 gửi.
  • Nếu bạn sử dụng đầu ra 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à lớp phủ trong một bước. Kết xuất này 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 CameraSourcePreviewGraphicOverlay 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 điểm mốc giúp phân loại các tư thế, hãy xem Mẹo phân loại ống.