Bạn có thể sử dụng Bộ công cụ học máy để phát hiện khuôn mặt trong hình ảnh và video.
Trước khi bắt đầu
- Nếu bạn chưa thực hiện, hãy thêm Firebase vào dự án Android.
- 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
):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' }
-
Không bắt buộc nhưng nên làm: Định cấu hình ứng dụng để tự động tải mô hình học máy xuống thiết bị sau khi ứng dụng được cài đặt 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: 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, thì 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 quá trình tải xuống hoàn tất sẽ không có kết quả.<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Nguyên tắc về hình ảnh đầu vào
Để 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. Nhìn chung, mỗi khuôn mặt mà 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 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ối thiểu là 200x200 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 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 hình ả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 Mẹo cải thiện hiệu suất theo thời gian thực.
Độ nét của hình ảnh kém có thể làm giảm độ 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 máy ảnh cũng có thể ảnh hưởng đến những tính năng trên khuôn mặt mà Bộ công cụ học máy phát hiện được. Xem phần 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 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ượngFirebaseVisionFaceDetectorOptions
.
Bạn có thể thay đổi các chế độ cài đặt sau:
Cài đặt | |
---|---|
Chế độ hiệu suất |
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 địa danh |
NO_LANDMARKS (mặc định)
| ALL_LANDMARKS
Liệu có nên cố gắng xác định "điểm đánh dấu" trên khuôn mặt: mắt, tai, mũi, má, miệng, v.v. |
Phát hiện đường viền |
NO_CONTOURS (mặc định)
| ALL_CONTOURS
Liệu có phát hiện đường nét của các đặc điểm trên 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 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 thành các danh mục như "mặt 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 của khuôn mặt cần phát hiện, so với hình ảnh. |
Bật tính năng theo dõi khuôn mặt |
false (mặc định) | true
Liệu có chỉ định mã nhận dạng cho khuôn mặt hay không. Mã nhận dạng này có thể dùng để theo dõi các khuôn mặt trên hình ảnh. Xin lưu ý rằng khi bật tính năng phát hiện đường viền, 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 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 viền và tính năng 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
// 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 đối tượngFirebaseVisionImage
từ Bitmap
, media.Image
, ByteBuffer
, mảng byte hoặc tệp trên thiết bị. Sau đó, 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 sử dụng hình ảnh có kích thước tối thiểu là 480x360 pixel. Nếu bạn đang nhận dạng khuôn mặt 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ễ.
Tạo đối tượng
FirebaseVisionImage
từ hình ảnh.-
Để tạo đối tượng
FirebaseVisionImage
từ đối tượngmedia.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ượngmedia.Image
và độ xoay của hình ảnh đếnFirebaseVisionImage.fromMediaImage()
.Nếu 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, vì vậy, bạn chỉ cần chuyển đổi giá trị xoay thành một trong các hằng sốROTATION_
của Bộ công cụ học máy trước khi gọiFirebaseVisionImage.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
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 cung cấp độ 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ị:
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
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 đó, truyền đối tượng
media.Image
và giá trị xoay vàoFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
từ URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đếnFirebaseVisionImage.fromFilePath()
. Điều này hữu ích khi bạn sử dụng ý địnhACTION_GET_CONTENT
để nhắc người dùng chọn một hình ảnh trong ứng dụng thư viện.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Để tạo đối tượng
FirebaseVisionImage
từByteBuffer
hoặc mảng byte, trước tiên, hãy tính toán độ xoay hình ảnh như mô tả ở trên cho dữ liệu đầu vàomedia.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à độ 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
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 bộ đệ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
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Cách tạo đối tượng
FirebaseVisionImage
từ đối tượngBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
biểu thị phải thẳng đứng, không cần xoay thêm.
-
Tạo một thực thể của
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin
val detector = FirebaseVision.getInstance() .getVisionFaceDetector(options)
Cuối cùng, hãy truyền hình ảnh đến 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
val result = detector.detectInImage(image) .addOnSuccessListener { faces -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
3. Nhận thông tin về 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ượngFirebaseVisionFace
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ể lấy 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ụ:
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
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 trên khuôn mặt được phát hiện. Các điểm này thể hiện hình dạng của đối tượng. Hãy xem phần Tổng quan về 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 liên kết với một khuôn mặt (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 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:
Đị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 viền khuôn mặt hoặc tính năng phân loại và phát hiện điểm đánh dấu, nhưng không được sử dụng cả hai:
Phát hiện đường viền
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 viền và phát hiện điểm mốc
Phát hiện đường viền và phân loại
Phát hiện đường viền, phát hiện đ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 ý đến các yêu cầu về kích thước hình ảnh của API này.
- Điều tiết các lệnh gọi đến trình phát hiện. Nếu có khung video mới trong khi trình phát hiện đang chạy, hãy thả khung đó.
- Nếu bạn đang 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à phủ trong một bước. Bằng cách này, bạn chỉ kết xuất một lần cho mỗi khung đầu vào trên bề mặt hiển thị.
-
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
.