Za pomocą pakietu ML Kit możesz wykrywać twarze na zdjęciach i filmach przypominających selfie.
Face Mesh Detection API | |
---|---|
Nazwa pakietu SDK | face-mesh-detection |
Implementacja | W momencie kompilacji kod i zasoby są statycznie połączone z aplikacją. |
Wpływ rozmiaru aplikacji | Ok.6,4 MB |
Wyniki | w czasie rzeczywistym na większości urządzeń. |
Wypróbuj
- Wypróbuj przykładową aplikację, aby: zobaczysz przykład użycia tego interfejsu API.
Zanim zaczniesz
W pliku
build.gradle
na poziomie projektu uwzględnij parametr Google Repozytorium Maven w sekcjach buildscript i allprojects.Dodaj zależność z biblioteką wykrywania siatki twarzy ML Kit do pliku Gradle modułu na poziomie aplikacji, czyli zwykle
app/build.gradle
:dependencies { // ... implementation 'com.google.mlkit:face-mesh-detection:16.0.0-beta1' }
Wytyczne dotyczące obrazu wejściowego
Zdjęcia powinny być wykonywane w odległości do ok. 2 metrów od aparatu urządzenia, więc że twarze są wystarczająco duże, by można było rozpoznawać siatkę twarzy. W ogólne, im większa twarz, tym lepsze rozpoznawanie twarzy w siatce.
Twarz powinna być skierowana w stronę aparatu, a co najmniej połowa twarzy powinna być widoczna. Duży obiekt znajdujący się między twarzą a aparatem może obniżyć dokładności.
Jeśli chcesz wykrywać twarze w aplikacjach działających w czasie rzeczywistym, weź pod uwagę ogólne wymiary obrazu wejściowego. Mniejsze obrazy więc zdjęcia są przetwarzane szybciej, więc robienie zdjęć w niższej rozdzielczości skraca czas oczekiwania. Pamiętaj jednak o powyższych wymaganiach dotyczących dokładności i upewnij się, twarz obiektu zajmuje jak najwięcej miejsca na obrazie.
Skonfiguruj detektor siatki twarzy
Jeśli chcesz zmienić domyślne ustawienia detektora siatki twarzy, wpisz te ustawienia za pomocą FaceMeshDetectorOptions obiektu. Można zmienić następujące ustawienia:
setUseCase
BOUNDING_BOX_ONLY
: udostępnia ramkę ograniczającą dla wykrytej siatki twarzy. To najszybszy wykrywacz twarzy, ale ma ograniczenie zasięgu(twarze musi znajdować się w odległości maksymalnie 2 metrów od kamery).FACE_MESH
(opcja domyślna): określa ramkę ograniczającą i dodatkową ścianę. informacje o siatce (468 punktów 3D i informacje o trójkącie). W porównaniu z Przypadek użycia funkcjiBOUNDING_BOX_ONLY
; czas oczekiwania wydłuża się o około 15%, zgodnie z pomiarem Pixel 3.
Na przykład:
Kotlin
val defaultDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.DEFAULT_OPTIONS) val boundingBoxDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.Builder() .setUseCase(UseCase.BOUNDING_BOX_ONLY) .build() )
Java
FaceMeshDetector defaultDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.DEFAULT_OPTIONS); FaceMeshDetector boundingBoxDetector = FaceMeshDetection.getClient( new FaceMeshDetectorOptions.Builder() .setUseCase(UseCase.BOUNDING_BOX_ONLY) .build() );
Przygotowywanie obrazu wejściowego
Aby wykrywać twarze na obrazie, utwórz obiekt InputImage
na podstawie
Bitmap
, media.Image
, ByteBuffer
, tablica bajtów lub plik na urządzeniu.
Następnie przekaż obiekt InputImage
do metody process
interfejsu FaceDetector
.
Do wykrywania siatki twarzy należy używać zdjęcia o wymiarach co najmniej 480 x 360 pikseli. Jeśli wykrywasz twarze w czasie rzeczywistym i rejestrujesz klatki, przy minimalnej rozdzielczości może pomóc zmniejszyć opóźnienie.
Możesz utworzyć InputImage
z różnych źródeł, każdy z nich objaśniamy poniżej.
Korzystanie z: media.Image
Aby utworzyć InputImage
z obiektu media.Image
, np. podczas przechwytywania obrazu z
z aparatu urządzenia, przekaż obiekt media.Image
i obiekt obrazu
w kierunku InputImage.fromMediaImage()
.
Jeśli używasz tagu
CameraX, OnImageCapturedListener
oraz
ImageAnalysis.Analyzer
klasy obliczają wartość rotacji
dla Ciebie.
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 // ... } } }
Jeśli nie korzystasz z biblioteki aparatu, która określa kąt obrotu obrazu, może go obliczyć na podstawie stopnia obrotu urządzenia i orientacji aparatu czujnik w urządzeniu:
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; }
Następnie przekaż obiekt media.Image
oraz
wartość stopnia obrotu na InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Za pomocą identyfikatora URI pliku
Aby utworzyć InputImage
obiektu z identyfikatora URI pliku, przekaż kontekst aplikacji oraz identyfikator URI pliku do
InputImage.fromFilePath()
Jest to przydatne, gdy
użyj intencji ACTION_GET_CONTENT
, aby zachęcić użytkownika do wyboru
obraz z aplikacji Galeria.
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(); }
Przy użyciu: ByteBuffer
lub ByteArray
Aby utworzyć InputImage
obiektu z ByteBuffer
lub ByteArray
, najpierw oblicz wartość obrazu
stopień obrotu zgodnie z wcześniejszym opisem dla danych wejściowych media.Image
.
Następnie utwórz obiekt InputImage
z buforem lub tablicą oraz
wysokość, szerokość, format kodowania kolorów i stopień obrotu:
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 );
Korzystanie z: Bitmap
Aby utworzyć InputImage
z obiektu Bitmap
, wypełnij tę deklarację:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Obraz jest reprezentowany przez obiekt Bitmap
wraz z informacją o obróceniu w stopniach.
Przetwarzanie zdjęcia
Przekaż obraz do metody process
:
Kotlin
val result = detector.process(image) .addOnSuccessListener { result -> // Task completed successfully // … } .addOnFailureListener { e -> // Task failed with an exception // … }
Java
Task<List<FaceMesh>> result = detector.process(image) .addOnSuccessListener( new OnSuccessListener<List<FaceMesh>>() { @Override public void onSuccess(List<FaceMesh> result) { // Task completed successfully // … } }) .addOnFailureListener( new OnFailureListener() { @Override Public void onFailure(Exception e) { // Task failed with an exception // … } });
Uzyskiwanie informacji o wykrytej siatce twarzy
Jeśli na obrazie zostanie wykryta jakakolwiek twarz, do FaceMesh
zostanie przekazana lista obiektów,
słuchaczem sukcesu. Każdy element FaceMesh
reprezentuje twarz wykrytej w
. Dla każdej siatki płaszczyzn można uzyskać współrzędne ograniczające dla każdej siatki płaszczyzn
a także wszelkie inne informacje skonfigurowane przez Ciebie siatkę twarzy
do wykrywania treści.
Kotlin
for (faceMesh in faceMeshs) { val bounds: Rect = faceMesh.boundingBox() // Gets all points val faceMeshpoints = faceMesh.allPoints for (faceMeshpoint in faceMeshpoints) { val index: Int = faceMeshpoints.index() val position = faceMeshpoint.position } // Gets triangle info val triangles: List<Triangle<FaceMeshPoint>> = faceMesh.allTriangles for (triangle in triangles) { // 3 Points connecting to each other and representing a triangle area. val connectedPoints = triangle.allPoints() } }
Java
for (FaceMesh faceMesh : faceMeshs) { Rect bounds = faceMesh.getBoundingBox(); // Gets all points List<FaceMeshPoint> faceMeshpoints = faceMesh.getAllPoints(); for (FaceMeshPoint faceMeshpoint : faceMeshpoints) { int index = faceMeshpoints.getIndex(); PointF3D position = faceMeshpoint.getPosition(); } // Gets triangle info List<Triangle<FaceMeshPoint>> triangles = faceMesh.getAllTriangles(); for (Triangle<FaceMeshPoint> triangle : triangles) { // 3 Points connecting to each other and representing a triangle area. List<FaceMeshPoint> connectedPoints = triangle.getAllPoints(); } }