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

Bạn có thể dùng Bộ công cụ học máy để phát hiện các khuôn mặt trong hình ảnh và video.

Trước khi bắt đầu

  1. Thêm Firebase vào dự án Android của bạn nếu bạn chưa thực hiện.
  2. Thêm các phần phụ thuộc của thư viện Android Bộ công cụ học máy vào tệp Gradle của mô-đun (cấp ứng dụng) (thường là app/build.gradle):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
      // If you want to detect face contours (landmark detection and classification
      // don't require this additional model):
      implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1'
    }
    
  3. Không bắt buộc nhưng nên dùng: Định cấu hình ứng dụng để tự động tải mô hình ML xuống thiết bị sau khi cài đặt ứng dụng qua Cửa hàng Play.

    Để thực hiện việc này, hãy thêm nội dung khai báo sau vào tệp AndroidManifest.xml của ứng dụng:

    <application ...>
      ...
      <meta-data
          android:name="com.google.firebase.ml.vision.DEPENDENCIES"
          android:value="face" />
      <!-- To use multiple models: android:value="face,model2,model3" -->
    </application>
    
    Nếu bạn không cho phép tải mô hình xuống tại thời điểm cài đặt, mô hình sẽ được tải xuống trong lần đầu tiên bạn chạy trình phát hiện. Các yêu cầu bạn đưa ra trước khi tải xuống hoàn tất sẽ không nhận được kết quả nào.

Nguyên tắc nhập hình ảnh

Để Bộ công cụ học máy phát hiện chính xác khuôn mặt, hình ảnh đầu vào phải chứa các khuôn mặt được biểu thị bằng đủ dữ liệu pixel. Nói chung, mỗi khuôn mặt bạn muốn phát hiện trong một hình ảnh phải có kích thước ít nhất là 100x100 pixel. Nếu bạn muốn phát hiện đường viền của các khuôn mặt, Bộ công cụ học máy yêu cầu đầu vào có độ phân giải cao hơn: mỗi khuôn mặt phải có kích thước ít nhất là 200 x 200 pixel.

Nếu đang phát hiện khuôn mặt trong một ứ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 (lưu ý các yêu cầu về độ chính xác ở trên) và đảm bảo rằng khuôn mặt của đối tượng chiếm nhiều hình ảnh nhất có thể. Ngoài ra, hãy xem bài viết Mẹo cải thiện hiệu suất theo thời gian thực.

Hình ảnh lấy nét kém 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 thử yêu cầu người dùng chụp lại hình ảnh.

Hướng của khuôn mặt so với camera cũng có thể ảnh hưởng đến những đặc điểm khuôn mặt mà Bộ công cụ học máy phát hiện được. Hãy xem bài viết Khái niệm về phát hiện khuôn mặt.

1. Định cấu hình trình phát hiện khuôn mặt

Trước khi áp dụng tính năng phát hiện khuôn mặt cho một hình ảnh, nếu bạn muốn thay đổi bất kỳ chế độ cài đặt mặc định nào của trình phát hiện khuôn mặt, hãy chỉ định các chế độ cài đặt đó bằng đối tượng FirebaseVisionFaceDetectorOptions. Bạn có thể thay đổi các chế độ cài đặt sau:

Cài đặt
Chế độ hiệu suất cao FAST (mặc định) | ACCURATE

Ưu tiên tốc độ hoặc độ chính xác khi phát hiện khuôn mặt.

Phát hiện điểm mốc NO_LANDMARKS (mặc định) | ALL_LANDMARKS

Liệu có cố gắng xác định các "vùng đất" trên khuôn mặt hay không: mắt, tai, mũi, má, miệng, v.v.

Phát hiện đường bao NO_CONTOURS (mặc định) | ALL_CONTOURS

Liệu có phát hiện các đường nét của đặc điểm khuôn mặt hay không. Chỉ phát hiện đường viền cho khuôn mặt nổi bật nhất trong một hình ảnh.

Phân loại khuôn mặt NO_CLASSIFICATIONS (mặc định) | ALL_CLASSIFICATIONS

Liệu có phân loại khuôn mặt theo các danh mục như "cười" và "mắt mở" hay không.

Kích thước khuôn mặt tối thiểu float (mặc định: 0.1f)

Kích thước tối thiểu (so với hình ảnh) của các khuôn mặt cần phát hiện.

Bật tính năng theo dõi khuôn mặt false (mặc định) | true

Liệu có gán mã nhận dạng cho khuôn mặt hay không. Mã này có thể dùng để theo dõi các khuôn mặt trên các hình ảnh.

Xin lưu ý rằng khi bạn bật tính năng phát hiện đường viền, thì hệ thống chỉ phát hiện được một khuôn mặt, vì vậy, tính năng theo dõi khuôn mặt sẽ không tạo ra kết quả hữu ích. Vì lý do này, và để cải thiện tốc độ phát hiện, đừng bật cả tính năng phát hiện đường viền và theo dõi khuôn mặt.

Ví dụ:

Java

// High-accuracy landmark detection and face classification
FirebaseVisionFaceDetectorOptions highAccuracyOpts =
        new FirebaseVisionFaceDetectorOptions.Builder()
                .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
                .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
                .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
                .build();

// Real-time contour detection of multiple faces
FirebaseVisionFaceDetectorOptions realTimeOpts =
        new FirebaseVisionFaceDetectorOptions.Builder()
                .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS)
                .build();

Kotlin+KTX

// High-accuracy landmark detection and face classification
val highAccuracyOpts = FirebaseVisionFaceDetectorOptions.Builder()
        .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
        .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
        .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
        .build()

// Real-time contour detection of multiple faces
val realTimeOpts = FirebaseVisionFaceDetectorOptions.Builder()
        .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS)
        .build()

2. Chạy trình phát hiện khuôn mặt

Để phát hiện khuôn mặt trong hình ảnh, hãy tạo một đối tượng FirebaseVisionImage 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 FirebaseVisionImage vào phương thức detectInImage của FirebaseVisionFaceDetector.

Để nhận dạng khuôn mặt, bạn nên dùng hình ảnh có kích thước tối thiểu là 480 x 360 pixel. Nếu bạn nhận dạng khuôn mặt theo thời gian thực, việc chụp khung hình ở độ phân giải tối thiểu này có thể giúp giảm độ trễ.

  1. Tạo đối tượng FirebaseVisionImage từ hình ảnh của bạn.

    • Để tạo đối tượng FirebaseVisionImage từ đối tượng media.Image, chẳng hạn như khi 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 ảnh đến FirebaseVisionImage.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, vì vậy, bạn chỉ cần chuyển đổi chế độ xoay sang một trong các hằng số ROTATION_ của Bộ công cụ học máy trước khi gọi FirebaseVisionImage.fromMediaImage():

      Java

      private class YourAnalyzer implements ImageAnalysis.Analyzer {
      
          private int degreesToFirebaseRotation(int degrees) {
              switch (degrees) {
                  case 0:
                      return FirebaseVisionImageMetadata.ROTATION_0;
                  case 90:
                      return FirebaseVisionImageMetadata.ROTATION_90;
                  case 180:
                      return FirebaseVisionImageMetadata.ROTATION_180;
                  case 270:
                      return FirebaseVisionImageMetadata.ROTATION_270;
                  default:
                      throw new IllegalArgumentException(
                              "Rotation must be 0, 90, 180, or 270.");
              }
          }
      
          @Override
          public void analyze(ImageProxy imageProxy, int degrees) {
              if (imageProxy == null || imageProxy.getImage() == null) {
                  return;
              }
              Image mediaImage = imageProxy.getImage();
              int rotation = degreesToFirebaseRotation(degrees);
              FirebaseVisionImage image =
                      FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
              // Pass image to an ML Kit Vision API
              // ...
          }
      }
      

      Kotlin+KTX

      private class YourImageAnalyzer : ImageAnalysis.Analyzer {
          private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
              0 -> FirebaseVisionImageMetadata.ROTATION_0
              90 -> FirebaseVisionImageMetadata.ROTATION_90
              180 -> FirebaseVisionImageMetadata.ROTATION_180
              270 -> FirebaseVisionImageMetadata.ROTATION_270
              else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
          }
      
          override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
              val mediaImage = imageProxy?.image
              val imageRotation = degreesToFirebaseRotation(degrees)
              if (mediaImage != null) {
                  val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                  // Pass image to an ML Kit Vision API
                  // ...
              }
          }
      }
      

      Nếu không sử dụng thư viện máy ảnh cho phép xoay hình ảnh, bạn có thể tính toán độ xoay này bằng cách xoay thiết bị và hướng của cảm biến máy ảnh trong thiết bị:

      Java

      private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
      static {
          ORIENTATIONS.append(Surface.ROTATION_0, 90);
          ORIENTATIONS.append(Surface.ROTATION_90, 0);
          ORIENTATIONS.append(Surface.ROTATION_180, 270);
          ORIENTATIONS.append(Surface.ROTATION_270, 180);
      }
      
      /**
       * 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, Context context)
              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);
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
          int sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION);
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          int result;
          switch (rotationCompensation) {
              case 0:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  break;
              case 90:
                  result = FirebaseVisionImageMetadata.ROTATION_90;
                  break;
              case 180:
                  result = FirebaseVisionImageMetadata.ROTATION_180;
                  break;
              case 270:
                  result = FirebaseVisionImageMetadata.ROTATION_270;
                  break;
              default:
                  result = FirebaseVisionImageMetadata.ROTATION_0;
                  Log.e(TAG, "Bad rotation value: " + rotationCompensation);
          }
          return result;
      }

      Kotlin+KTX

      private val ORIENTATIONS = SparseIntArray()
      
      init {
          ORIENTATIONS.append(Surface.ROTATION_0, 90)
          ORIENTATIONS.append(Surface.ROTATION_90, 0)
          ORIENTATIONS.append(Surface.ROTATION_180, 270)
          ORIENTATIONS.append(Surface.ROTATION_270, 180)
      }
      /**
       * 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, context: Context): 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)
      
          // On most devices, the sensor orientation is 90 degrees, but for some
          // devices it is 270 degrees. For devices with a sensor orientation of
          // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
          val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
          val sensorOrientation = cameraManager
                  .getCameraCharacteristics(cameraId)
                  .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
          rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
      
          // Return the corresponding FirebaseVisionImageMetadata rotation value.
          val result: Int
          when (rotationCompensation) {
              0 -> result = FirebaseVisionImageMetadata.ROTATION_0
              90 -> result = FirebaseVisionImageMetadata.ROTATION_90
              180 -> result = FirebaseVisionImageMetadata.ROTATION_180
              270 -> result = FirebaseVisionImageMetadata.ROTATION_270
              else -> {
                  result = FirebaseVisionImageMetadata.ROTATION_0
                  Log.e(TAG, "Bad rotation value: $rotationCompensation")
              }
          }
          return result
      }

      Sau đó, hãy truyền đối tượng media.Image và giá trị xoay đến FirebaseVisionImage.fromMediaImage():

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    • Để tạo đối tượng FirebaseVisionImage từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp đến FirebaseVisionImage.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ọ.

      Java

      FirebaseVisionImage image;
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e.printStackTrace();
      }

      Kotlin+KTX

      val image: FirebaseVisionImage
      try {
          image = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          e.printStackTrace()
      }
    • Để tạo một đối tượng FirebaseVisionImage từ một ByteBuffer hoặc một mảng byte, trước tiên, hãy tính toán độ xoay hình ảnh như mô tả ở trên cho đầu vào media.Image.

      Sau đó, hãy tạo một đối tượng FirebaseVisionImageMetadata chứa chiều cao, chiều rộng, định dạng mã hoá màu và chế độ xoay của hình ảnh:

      Java

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
              .setWidth(480)   // 480x360 is typically sufficient for
              .setHeight(360)  // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build();

      Kotlin+KTX

      val metadata = FirebaseVisionImageMetadata.Builder()
              .setWidth(480) // 480x360 is typically sufficient for
              .setHeight(360) // image recognition
              .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
              .setRotation(rotation)
              .build()

      Sử dụng vùng đệm hoặc mảng và đối tượng siêu dữ liệu để tạo đối tượng FirebaseVisionImage:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Cách tạo đối tượng FirebaseVisionImage từ đối tượng Bitmap:

      Java

      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

      Kotlin+KTX

      val image = FirebaseVisionImage.fromBitmap(bitmap)
      Hình ảnh mà đối tượng Bitmap biểu thị phải thẳng đứng và không cần xoay thêm.
  2. Nhận một thực thể của FirebaseVisionFaceDetector:

    Java

    FirebaseVisionFaceDetector detector = FirebaseVision.getInstance()
            .getVisionFaceDetector(options);

    Kotlin+KTX

    val detector = FirebaseVision.getInstance()
            .getVisionFaceDetector(options)
  3. Cuối cùng, hãy truyền hình ảnh vào phương thức detectInImage:

    Java

    Task<List<FirebaseVisionFace>> result =
            detector.detectInImage(image)
                    .addOnSuccessListener(
                            new OnSuccessListener<List<FirebaseVisionFace>>() {
                                @Override
                                public void onSuccess(List<FirebaseVisionFace> faces) {
                                    // Task completed successfully
                                    // ...
                                }
                            })
                    .addOnFailureListener(
                            new OnFailureListener() {
                                @Override
                                public void onFailure(@NonNull Exception e) {
                                    // Task failed with an exception
                                    // ...
                                }
                            });

    Kotlin+KTX

    val result = detector.detectInImage(image)
            .addOnSuccessListener { faces ->
                // Task completed successfully
                // ...
            }
            .addOnFailureListener { e ->
                // Task failed with an exception
                // ...
            }

3. Nhận thông tin về các khuôn mặt được phát hiện

Nếu thao tác nhận dạng khuôn mặt thành công, danh sách các đối tượng FirebaseVisionFace sẽ được chuyển đến trình nghe thành công. Mỗi đối tượng FirebaseVisionFace đại diện cho một khuôn mặt đã được phát hiện trong hình ảnh. Đối với mỗi khuôn mặt, bạn có thể nhận các toạ độ giới hạn trong hình ảnh đầu vào, cũng như mọi thông tin khác mà bạn đã định cấu hình trình phát hiện khuôn mặt để tìm. Ví dụ:

Java

for (FirebaseVisionFace face : faces) {
    Rect bounds = face.getBoundingBox();
    float rotY = face.getHeadEulerAngleY();  // Head is rotated to the right rotY degrees
    float rotZ = face.getHeadEulerAngleZ();  // Head is tilted sideways rotZ degrees

    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
    // nose available):
    FirebaseVisionFaceLandmark leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR);
    if (leftEar != null) {
        FirebaseVisionPoint leftEarPos = leftEar.getPosition();
    }

    // If contour detection was enabled:
    List<FirebaseVisionPoint> leftEyeContour =
            face.getContour(FirebaseVisionFaceContour.LEFT_EYE).getPoints();
    List<FirebaseVisionPoint> upperLipBottomContour =
            face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).getPoints();

    // If classification was enabled:
    if (face.getSmilingProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        float smileProb = face.getSmilingProbability();
    }
    if (face.getRightEyeOpenProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        float rightEyeOpenProb = face.getRightEyeOpenProbability();
    }

    // If face tracking was enabled:
    if (face.getTrackingId() != FirebaseVisionFace.INVALID_ID) {
        int id = face.getTrackingId();
    }
}

Kotlin+KTX

for (face in faces) {
    val bounds = face.boundingBox
    val rotY = face.headEulerAngleY // Head is rotated to the right rotY degrees
    val rotZ = face.headEulerAngleZ // Head is tilted sideways rotZ degrees

    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
    // nose available):
    val leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR)
    leftEar?.let {
        val leftEarPos = leftEar.position
    }

    // If contour detection was enabled:
    val leftEyeContour = face.getContour(FirebaseVisionFaceContour.LEFT_EYE).points
    val upperLipBottomContour = face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).points

    // If classification was enabled:
    if (face.smilingProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        val smileProb = face.smilingProbability
    }
    if (face.rightEyeOpenProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) {
        val rightEyeOpenProb = face.rightEyeOpenProbability
    }

    // If face tracking was enabled:
    if (face.trackingId != FirebaseVisionFace.INVALID_ID) {
        val id = face.trackingId
    }
}

Ví dụ về đường viền khuôn mặt

Khi bật tính năng phát hiện đường viền khuôn mặt, bạn sẽ nhận được danh sách các điểm cho từng đặc điểm khuôn mặt đã được phát hiện. Những điểm này biểu thị hình dạng của đối tượng. Hãy xem bài viết Tổng quan về các khái niệm phát hiện khuôn mặt để biết thông tin chi tiết về cách biểu thị đường viền.

Hình ảnh sau đây minh hoạ cách các điểm này ánh xạ với một khuôn mặt (hãy nhấp vào hình ảnh để phóng to):

Phát hiện khuôn mặt theo thời gian thực

Nếu bạn muốn dùng tính năng phát hiện khuôn mặt trong ứng dụng theo thời gian thực, hãy làm theo các hướng dẫn sau để đạt được tốc độ khung hình tốt nhất:

  • Định cấu hình trình phát hiện khuôn mặt để sử dụng tính năng phát hiện hoặc phân loại đường viền khuôn mặt cũng như phát hiện mốc, nhưng không được sử dụng cả hai:

    Phát hiện đường viền
    Phát hiện đường viền
    Phân loại
    Phát hiện và phân loại địa danh
    Phát hiện và phân loại đường viền
    Phát hiện và phân loại đường viền
    Phát hiện đường viền, phát hiện và phân loại mốc

  • Bật chế độ FAST (bật theo mặc định).

  • 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.

  • Điều tiết lệnh gọi đến trình phát hiện. Nếu có một khung hình video mới trong khi trình phát hiện đang chạy, hãy bỏ khung hình đó.
  • Nếu bạn đang 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. Nhờ vậy, bạn chỉ kết xuất lên giao diện hiển thị một lần cho mỗi khung nhập.
  • 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.