Bạn có thể dùng Bộ công cụ học máy để phát hiện khuôn mặt trong hình ảnh và video.
| Tính năng | Không theo cụm | Theo cụm |
|---|---|---|
| Triển khai | Mô hình được tải xuống độ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ụng | Tăng khoảng 800 KB. | Tăng khoảng 6,9 MB. |
| Thời gian khởi chạy | Có 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ử
- Thử dùng ứng dụng mẫu để xem ví dụ về cách sử dụng API này.
- Tự thử đoạn mã bằng lớp học lập trình.
Trước khi bắt đầu
Trong tệp
build.gradlecấp dự án, hãy nhớ thêm kho lưu trữ Maven của Google vào cả hai mụcbuildscriptvàallprojects.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. Chọn một trong các phần phụ thuộc sau đây dựa trên nhu cầu của bạn:Để gói 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' }Để sử dụng mô hình 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' }Nếu bạn chọn sử dụng mô hình trong Dịch vụ Google Play, bạn có thể định cấu hình ứng dụng để tự động tải mô hình xuống thiết bị sau khi ứng dụng được cài đặt từ Cửa hàng Play. Để làm như vậy, hãy thêm nội dung khai báo sau vào tệp
AndroidManifest.xmlcủ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 Dịch vụ Google Play API ModuleInstallClient.
Nếu bạn không bật tính năng tải mô hình xuống tại thời điểm cài đặt hoặc yêu cầu tải xuống rõ ràng, thì mô hình sẽ được tải xuống vào 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 quá trình tải xuống hoàn tất sẽ không tạo ra kết quả.
Nguyên tắc về hình ảnh đầu vào
Để 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 khuôn mặt một cách chính xác, hình ảnh đầu vào phải chứa khuôn mặt được biểu thị bằng dữ liệu pixel đầy đủ. Nói 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 nét của khuôn mặt, Bộ công cụ học máy yêu cầu hình ảnh đầ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, bạn cũng có thể 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 ý 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 càng nhiều hình ảnh càng tốt. Hãy xem thêm các mẹo để cải thiện hiệu suất theo thời gian thực.
Việc hình ảnh không được lấy nét đúng cách 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.
Hướng của khuôn mặt so với camera cũng có thể ảnh hưởng đến các đặc điểm trên khuôn mặt mà Bộ công cụ học máy phát hiện. Xem bài viết Khái niệm về tính năng 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 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ượngFaceDetectorOptions.
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
Có cố gắng xác định "điểm mốc" 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
Có phát hiện đường nét của các đặc điểm trên khuôn mặt hay không. Đường nét chỉ được phát hiện cho khuôn mặt nổi bật nhất trong hình ảnh. |
setClassificationMode
|
CLASSIFICATION_MODE_NONE (mặc định)
|
CLASSIFICATION_MODE_ALL
Có phân loại khuôn mặt thành các danh mục như "mỉm cười", và "mắt mở" hay không. |
setMinFaceSize
|
float (mặc định: 0.1f)
Đặt kích thước khuôn mặt nhỏ nhất mong muốn, được biểu thị bằng tỷ lệ chiều rộng của đầu so với chiều rộng của hình ảnh. |
enableTracking
|
false (mặc định) | true
Có gán mã nhận dạng cho khuôn mặt hay không. Bạn có thể dùng mã nhận dạng này để theo dõi 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 nét, hệ thống chỉ phát hiện một khuôn mặt được phát hiện, vì vậy, tính năng theo dõi khuôn mặt không mang lại 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 nét 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 khuôn mặt trong hình ảnh, hãy tạo đối tượngInputImage từ Bitmap, media.Image, ByteBuffer, mảng byte hoặc tệp trên thiết bị. Sau đó, hãy chuyển đối tượng InputImage đến phương thức process của FaceDetector.
Để phát hiện 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. Nếu bạn đang phát hiện 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ễ.
Bạn có thể tạo InputImage
đối tượng 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 bằng camera của thiết bị), hãy chuyể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 OnImageCapturedListener và
ImageAnalysis.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 dùng thư viện máy ảnh cho biết độ xoay của hình ảnh, bạn có thể tính độ xoay đó từ độ xoay của thiết bị và hướng của cảm biến camera 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 chuyể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 từ URI tệp, hãy chuyển ngữ cảnh ứng dụng và URI tệp đến InputImage.fromFilePath().InputImage Đ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
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 đối tượng InputImage
từ ByteBuffer hoặc ByteArray, trước tiên, hãy tính độ xoay của 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ộ đệ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. Tạo thực thể của FaceDetector
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
Chuyển hình ảnh đến phương thứcprocess:
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. Xem 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ượngFace sẽ được chuyển đến trình nghe thành công. Mỗi đối tượng Face biểu thị 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ể xem toạ độ giới hạn của khuôn mặt đó 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ụ:
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 nét khuôn mặt
Khi bạn bật tính năng phát hiện đường nét khuôn mặt, bạn sẽ nhận được danh sách các điểm cho từng đặc điểm trên khuôn mặt được phát hiện. Các điểm này biểu thị hình dạng của đặc điểm. Hãy xem bài viết Khái niệm về tính năng phát hiện khuôn mặt để biết thông tin chi tiết về cách biểu thị đường nét.
Hình ảnh sau đây minh hoạ cách các điểm này ánh xạ đến 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 sử 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 nguyên tắ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 tính năng phát hiện đường nét khuôn mặt hoặc phân loại và phát hiện điểm mốc, nhưng không sử dụng cả hai:
Phát hiện đường nét
Phát hiện điểm mốc
Phân loại
Phát hiện và phân loại điểm mốc
Phát hiện đường nét và điểm mốc
Phát hiện đường nét và phân loại
Phát hiện đường nét, điểm mốc và phân loạiBật chế độ
FAST(được 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 ý các yêu cầu về kích thước hình ảnh của API này.
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ó một khung video mới
trong khi trình phát hiện đang chạy, hãy thả khung đó. Hãy xem lớp
VisionProcessorBase trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ.
CameraX,
hãy đảm bảo rằng chiến lược áp lực ngược được đặt thành giá trị mặc định
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 xếp hàng đợi để
phân phối. Sau khi hình ảnh đang được phân tích bị đó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.
CameraSourcePreview và
GraphicOverlay trong ứng dụng mẫu bắt đầu nhanh để biết ví dụ.
ImageFormat.YUV_420_888. Nếu bạn sử dụng API Camera cũ, hãy chụp ảnh ở định dạng ImageFormat.NV21.