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 |
Wydajność | w czasie rzeczywistym na większości urządzeń. |
Wypróbuj
- Zapoznaj się z przykładową aplikacją, aby zobaczyć przykład użycia tego interfejsu API.
Zanim zaczniesz
W pliku
build.gradle
na poziomie projektu dodaj repozytorium Maven Google zarówno w sekcji buildscript, jak i w sekcji allprojects.Dodaj zależność z biblioteką wykrywania siatki twarzy ML Kit do pliku Gradle na poziomie aplikacji modułu. Jest to 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 około 2 metrów od kamery urządzenia, aby twarze były wystarczająco duże, aby umożliwić optymalne rozpoznawanie twarzy. Ogólnie rzecz biorąc, im większa twarz, tym lepsze rozpoznawanie siatki.
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 zmniejszyć dokładność.
Jeśli chcesz wykrywać twarze w aplikacjach działających w czasie rzeczywistym, musisz też wziąć pod uwagę ogólne wymiary obrazu wejściowego. Mniejsze obrazy można przetwarzać szybciej, więc robienie obrazów w niższej rozdzielczości skraca czas oczekiwania. Trzeba jednak pamiętać o powyższych wymaganiach dotyczących dokładności i zadbać o to, aby twarz obiektu zajmowała jak najwięcej miejsca na zdjęciu.
Skonfiguruj detektor siatki twarzy
Jeśli chcesz zmienić domyślne ustawienia detektora siatki twarzy, określ te ustawienia za pomocą obiektu FaceMeshDetectorOptions. Można zmienić następujące ustawienia:
setUseCase
BOUNDING_BOX_ONLY
: udostępnia ramkę ograniczającą dla wykrytej siatki twarzy. To najszybszy wykrywacz twarzy, który ma jednak ograniczony zasięg(twarze muszą znajdować się w odległości maksymalnie 2 metrów od kamery).FACE_MESH
(opcja domyślna): zawiera ramkę ograniczającą i dodatkowe informacje o siatce twarzy (468 punktów 3D i informacje o trójkącie). W porównaniu z przypadkiem użycia funkcjiBOUNDING_BOX_ONLY
czas oczekiwania wzrasta o około 15% (zgodnie z pomiarem na Pixelu 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
, tablicy bajtów lub pliku na urządzeniu.
Następnie przekaż obiekt InputImage
do metody process
interfejsu FaceDetector
.
Do wykrywania siatki twarzy należy używać obrazu o wymiarach co najmniej 480 x 360 pikseli. Jeśli wykrywasz twarze w czasie rzeczywistym, rejestrowanie klatek przy tej minimalnej rozdzielczości może zmniejszyć opóźnienie.
Obiekt InputImage
możesz utworzyć z różnych źródeł. Poniżej znajdziesz opis każdego z nich.
Korzystanie z: media.Image
Aby utworzyć obiekt InputImage
na podstawie obiektu media.Image
, na przykład podczas rejestrowania obrazu aparatem urządzenia, przekaż obiekt media.Image
i obrót obrazu do InputImage.fromMediaImage()
.
Jeśli używasz biblioteki
AparatuX, klasy OnImageCapturedListener
i ImageAnalysis.Analyzer
obliczają wartość obrotu za 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 stopień obrotu obrazu, możesz go obliczyć na podstawie stopnia obrotu urządzenia i orientacji czujnika aparatu 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
i wartość stopnia obrotu do InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Za pomocą identyfikatora URI pliku
Aby utworzyć obiekt InputImage
na podstawie identyfikatora URI pliku, przekaż do InputImage.fromFilePath()
kontekst aplikacji i identyfikator URI pliku. Jest to przydatne, gdy używasz intencji ACTION_GET_CONTENT
, aby zachęcić użytkownika do wybrania obrazu z aplikacji galerii.
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ć obiekt InputImage
na podstawie ByteBuffer
lub ByteArray
, najpierw oblicz stopień obrotu obrazu zgodnie z wcześniejszym opisem dla danych wejściowych media.Image
.
Następnie utwórz obiekt InputImage
z buforem lub tablicą i podaj 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ć obiekt 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 obrazu
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 twarz, do detektora powodzenia zostanie przekazana lista obiektów FaceMesh
. Każdy obiekt FaceMesh
oznacza twarz wykrytej na zdjęciu. W przypadku każdej siatki twarzy możesz uzyskać współrzędne ograniczające z obrazu wejściowego, a także inne informacje, które skonfigurowałeś do wykrywania siatki twarzy.
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(); } }