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.

Tính năngKhông nhómTheo cụm
Triển khaiMô hình được tải xuống một cách linh động thông qua Dịch vụ Google Play.Mô hình đượ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.
Kích thước ứng dụngTăng kích thước khoảng 800 KB.Tăng kích thước khoảng 6,9 MB.
Thời gian khởi chạyCó thể phải đợi mô hình tải xuống trước khi sử dụng lần đầu.Mô hình có sẵn ngay lập tức

Dùng thử

Trước khi bắt đầu

  1. Trong tệp build.gradle cấp dự án, hãy nhớ bao gồm Kho lưu trữ Maven trong cả hai phần buildscriptallprojects.

  2. Thêm các phần phụ thuộc cho thư viện Android Bộ công cụ học máy vào mô-đun của bạn tệp gradle cấp ứng dụng, thường là app/build.gradle. Chọn một trong số các phần phụ thuộc sau đây tuỳ theo nhu cầu của bạn:

    Để nhóm mô hình với ứng dụng của bạn:

    dependencies {
      // ...
      // Use this dependency to bundle the model with your app
      implementation 'com.google.mlkit:face-detection:16.1.7'
    }
    

    Cách sử dụng mô hình này trong Dịch vụ Google Play:

    dependencies {
      // ...
      // Use this dependency to use the dynamically downloaded model in Google Play Services
      implementation 'com.google.android.gms:play-services-mlkit-face-detection:17.1.0'
    }
    
  3. Nếu chọn sử dụng mô hình này trong Dịch vụ Google Play, bạn có thể định cấu hình ứng dụng của bạn để tự động tải mô hình xuống thiết bị sau khi ứng dụng đã cài đặt từ 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.mlkit.vision.DEPENDENCIES"
              android:value="face" >
          <!-- To use multiple models: android:value="face,model2,model3" -->
    </application>
    

    Bạn cũng có thể kiểm tra rõ ràng phạm vi cung cấp của mô hình và yêu cầu tải xuống thông qua ModuleInstallClient API của Dịch vụ Google Play.

    Nếu bạn không bật tính năng tải mô hình tại thời điểm cài đặt xuống hoặc không yêu cầu tải xuống một cách rõ ràng, 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. Yêu cầu bạn đưa ra không có kết quả nào trước khi quá trình tải xuống hoàn tất.

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

Để nhận dạng khuôn mặt, bạn nên sử dụng hình ảnh có kích thước tối thiểu là 480x360 pixel. Để 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 khuôn mặt được biểu thị bằng đủ dữ liệu pixel. Nhìn chung, mỗi khuôn mặt bạn muốn để phát hiện trong hình ảnh phải có kích thước tối thiểu là 100x100 pixel. Nếu bạn muốn phát hiện đường viền của khuôn mặt, Bộ công cụ học máy yêu cầu dữ liệ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ối thiểu là 200x200 pixel.

Nếu phát hiện khuôn mặt trong ứng dụng theo thời gian thực, có thể bạn cũng muốn để xem xét kích thước tổng thể của các 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 vẫn xin lưu ý các yêu cầu nêu trên về tính chính xác và đảm bảo rằng khuôn mặt của chủ thể chiếm nhiều diện tích hình ảnh nhất có thể. Xem thêm mẹo cải thiện hiệu suất theo thời gian thực.

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 được chấp nhận kết quả, hãy 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 khuôn mặt các tính năng mà Bộ công cụ học máy phát hiện. Xem Khái niệm về công nghệ 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 nào trình phát hiện khuôn mặt, hãy chỉ định các cài đặt đó bằng Đối tượng FaceDetectorOptions. Bạn có thể thay đổi các chế độ cài đặt sau:

Cài đặt
setPerformanceMode PERFORMANCE_MODE_FAST (mặc định) | PERFORMANCE_MODE_ACCURATE

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

setLandmarkMode LANDMARK_MODE_NONE (mặc định) | LANDMARK_MODE_ALL

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

setContourMode CONTOUR_MODE_NONE (mặc định) | CONTOUR_MODE_ALL

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

setClassificationMode CLASSIFICATION_MODE_NONE (mặc định) | CLASSIFICATION_MODE_ALL

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

setMinFaceSize float (mặc định: 0.1f)

Đặt kích thước khuôn mặt mong muốn nhỏ nhất, được biểu thị bằng tỷ lệ chiều rộng của phần đầu đến chiều rộng của hình ảnh.

enableTracking false (mặc định) | true

Liệu có gán một giấy tờ tuỳ thân cho khuôn mặt (có thể dùng để theo dõi) khuôn mặt trên các hình ảnh.

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

Ví dụ:

Kotlin

// High-accuracy landmark detection and face classification
val highAccuracyOpts = FaceDetectorOptions.Builder()
        .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
        .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
        .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
        .build()

// Real-time contour detection
val realTimeOpts = FaceDetectorOptions.Builder()
        .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
        .build()

Java

// High-accuracy landmark detection and face classification
FaceDetectorOptions highAccuracyOpts =
        new FaceDetectorOptions.Builder()
                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
                .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
                .build();

// Real-time contour detection
FaceDetectorOptions realTimeOpts =
        new FaceDetectorOptions.Builder()
                .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
                .build();

2. Chuẩn bị hình ảnh đầu vào

Để phát hiện các khuôn mặt trong 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 Phương thức process của FaceDetector.

Để phát hiện khuôn mặt, bạn nên dùng hình ảnh có kích thước tối thiểu 480x360 pixel. Nếu bạn đang phát hiện khuôn mặt theo thời gian thực, hãy 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 InputImage đối tượng từ các nguồn khác nhau, 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 độ xoay của hình ảnh, bạn có thể tính tỷ lệ khung hình dựa trên độ xoay của thiết bị và hướng của máy ảnh cảm biến 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 thành InputImage.fromMediaImage():

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

Sử dụng URI tệp

Cách tạo InputImage từ mộ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 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 vùng đệm hoặc mảng, cùng với đối tượng chiều cao, chiều rộng, định dạng mã hoá màu và độ xoay:

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

Cách tạo 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. Tải một thực thể của FaceDetector (Trình phát hiện khuôn mặt)

Kotlin

val detector = FaceDetection.getClient(options)
// Or, to use the default option:
// val detector = FaceDetection.getClient();

Java

FaceDetector detector = FaceDetection.getClient(options);
// Or use the default options:
// FaceDetector detector = FaceDetection.getClient();

4. Xử lý hình ảnh

Truyền hình ảnh vào phương thức process:

Kotlin

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

Java

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

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

Nếu thao tác phát hiện khuôn mặt thành công, danh sách Các đối tượng Face được chuyển đến thành công trình nghe. Mỗi đối tượng Face đạ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ể lấy toạ độ giới hạn trong dữ liệu đầu vào hình ảnh, cũng như bất kỳ thông tin nào khác mà bạn đã định cấu hình trình phát hiện khuôn mặt tìm kiếm. Ví dụ:

Kotlin

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(FaceLandmark.LEFT_EAR)
    leftEar?.let {
        val leftEarPos = leftEar.position
    }

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

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

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

Java

for (Face 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):
    FaceLandmark leftEar = face.getLandmark(FaceLandmark.LEFT_EAR);
    if (leftEar != null) {
        PointF leftEarPos = leftEar.getPosition();
    }

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

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

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

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 phát hiện được. Những điểm này biểu thị hình dạng của của chúng tôi. Xem Khuôn mặt Khái niệm phát hiện để biết thông tin chi tiết về cách đường bao đại diệ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, nhấp vào hình ảnh để phóng to:

ví dụ phát hiện lưới đường viền khuôn mặt

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 bước 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 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 thực hiện cả hai:

    Phát hiện đường viền
    Phát hiện địa danh
    Phân loại
    Phát hiện và phân loại địa danh
    Phát hiện đường viền và phát hiện điểm mốc
    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 điểm mốc

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

  • Hãy cân nhắc chụp ảnh ở độ phân giải thấp hơn. Tuy nhiên, hãy lưu ý rằng 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 Camera hoặc API camera2, lệnh điều tiết đến trình phát hiện. Nếu một video mới khung hình sẽ xuất hiện trong khi trình phát hiện đang chạy, hãy bỏ khung đó. Xem Ví dụ về lớp VisionProcessorBase trong ứng dụng mẫu khởi động nhanh.
  • Nếu bạn sử dụng API CameraX, đảm bảo rằng chiến lược backpressure được đặt ở giá trị mặc định ImageAnalysis.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 các hình ảnh khác được tạo 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ủ đồ 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ủ lên trên trong một bước duy nhất. 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. Xem CameraSourcePreview Ví dụ về các lớp GraphicOverlay trong ứng dụng mẫu khởi động nhanh.
  • Nếu bạn sử dụng API Camera2, hãy chụp ảnh trong Định dạng ImageFormat.YUV_420_888. Nếu bạn sử dụng API Máy ảnh cũ, hãy chụp ảnh trong Định dạng ImageFormat.NV21.