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
- Nếu bạn chưa làm như vậy, thêm Firebase vào dự án Android của bạn.
- Thêm các phần phụ thuộc của thư viện Android cho 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
):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 dùng: Định cấu hình để ứng dụng tự động tải xuống
mô hình ML vào thiết bị sau khi cài đặt ứng dụng của bạn qua 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.xml
: 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ẽ bị được tải xuống vào lần đầu tiên bạn chạy trình phát hiện. Yêu cầu bạn đưa ra trước quá trình tải xuống đã hoàn tất sẽ không có kết quả nào.<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 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 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 đang 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 (lưu ý các yêu cầu nêu trên về độ 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.
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ả được chấp nhận, 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 khuôn mặt các tính năng mà Bộ công cụ học máy phát hiện. Xem Phát hiện khuôn mặt Khái niệm.
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ượngFirebaseVisionFaceDetectorOptions
.
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 "đặc điểm" 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. Đườ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. |
Phân loại khuôn mặt |
NO_CLASSIFICATIONS (mặc định)
| ALL_CLASSIFICATIONS
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ở". |
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ộ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ụ:
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 các khuôn mặt trong hình ảnh, hãy tạo một đối tượngFirebaseVisionImage
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 FirebaseVisionImage
vào phương thức
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 nhất 480x360 pixel. Nếu bạn đang nhận dạng 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ễ.
Tạo đối tượng
FirebaseVisionImage
từ hình ảnh.-
Cách tạo đối tượng
FirebaseVisionImage
qua Đối tượngmedia.Image
, chẳng hạn như khi chụp ảnh từ camera của thiết bị, hãy truyền đối tượngmedia.Image
và xoay thànhFirebaseVisionImage.fromMediaImage()
.Nếu bạn sử dụng Thư viện CameraX,
OnImageCapturedListener
và Các lớpImageAnalysis.Analyzer
tính toán giá trị xoay cho bạn, nên bạn chỉ cần chuyển đổi chế độ xoay vòng thành một Hằng sốROTATION_
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+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 bạn xoay hình ảnh, có thể tính toán kích thước này dựa trên hướng xoay của thiết bị và hướng của máy ảnh cảm biến 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à thànhFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Để tạo đối tượng
FirebaseVisionImage
qua URI tệp, hãy truyền ngữ cảnh ứng dụng và URI tệp đểFirebaseVisionImage.fromFilePath()
Điều này rất hữu ích khi bạn sử dụng ý địnhACTION_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ọ.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() }
- Cách tạo đối tượng
FirebaseVisionImage
quaByteBuffer
hoặc một mảng byte, trước tiên, hãy tính hình ảnh như mô tả ở trên cho đầu vàomedia.Image
.Sau đó, hãy tạo một đối tượng
FirebaseVisionImageMetadata
có chứa chiều cao, chiều rộng, định dạng mã hoá màu của hình ảnh, và xoay: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 một Đố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
qua Đối tượngBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
đại diện phải thẳng đứng mà không cần xoay thêm.
-
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)
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ượngFirebaseVisionFace
sẽ được chuyển đến thành công
trình nghe. 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 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ụ:
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 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 Tổng quan về 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):
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ốcBậ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.
- Điều tiết lệnh gọi đến trình phát hiện. Nếu một khung video mới trong khi trình phát hiện đang chạy, hãy thả khung hình.
- Nếu bạn đang 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. Khi làm vậy, bạn sẽ kết xuất lên giao diện màn hình một lần cho mỗi khung đầu vào.
-
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
.