Możesz używać ML Kit do wykrywania twarzy na zdjęciach i filmach.
Funkcja | Niegrupowane | Łączenie w pakiety |
---|---|---|
Wdrażanie | Model jest pobierany dynamicznie przez Usługi Google Play. | Model jest statycznie połączony z aplikacją podczas kompilacji. |
Rozmiar aplikacji | Zwiększenie rozmiaru o około 800 KB. | Zwiększenie rozmiaru o około 6,9 MB. |
Czas inicjowania | Może minąć trochę czasu, zanim model zostanie pobrany. | Model jest dostępny od razu |
Wypróbuj
- Przetestuj przykładową aplikację, aby zobaczyć przykładowe użycie tego interfejsu API.
- Wypróbuj kod za pomocą laboratorium.
Zanim zaczniesz
W pliku
build.gradle
na poziomie projektu umieść repozytorium Google Maven w sekcjachbuildscript
iallprojects
.Dodaj zależności bibliotek ML Kit na Androida do pliku Gradle na poziomie aplikacji, który zwykle wynosi
app/build.gradle
. W zależności od potrzeb wybierz jedną z zależności:Aby połączyć model z aplikacją:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:face-detection:16.1.5' }
Do używania modelu w Usługach 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' }
Jeśli zdecydujesz się używać modelu w Usługach Google Play, możesz skonfigurować aplikację tak, aby automatycznie pobierała ją na urządzenie po zainstalowaniu jej ze Sklepu Play. Aby to zrobić, dodaj do pliku
AndroidManifest.xml
aplikacji tę deklarację:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="face" > <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Możesz też bezpośrednio sprawdzić dostępność modelu i poprosić o jego pobranie, korzystając z interfejsu ModuleInstallClient API w Usługach Google Play.
Jeśli nie włączysz pobierania modelu w czasie instalacji lub nie poprosisz o jego pobieranie, zostanie on pobrany po pierwszym uruchomieniu wzorca. Wysyłanie żądań, które zakończyły się, zanim wyniki zostały ukończone, nie zwraca żadnych wyników.
Wskazówki dotyczące obrazu wejściowego
Do rozpoznawania twarzy używaj zdjęć o wymiarach co najmniej 480 x 360 pikseli. Aby system ML Kit dokładnie wykrywał twarze, muszą one zawierać twarze reprezentowane przez wystarczającą ilość pikseli. Ogólnie każda twarz, która ma być wykrywana na zdjęciu, powinna mieć co najmniej 100 × 100 pikseli. Aby wykryć kontury twarzy, zestaw ML Kit musi mieć wyższą rozdzielczość: każda ściana powinna mieć rozmiar co najmniej 200 x 200 pikseli.
Jeśli wykryjemy twarze w aplikacji w czasie rzeczywistym, warto wziąć pod uwagę ogólne wymiary obrazów wejściowych. Mniejsze obrazy można przetwarzać szybciej, dzięki czemu aby skrócić czas oczekiwania, można robić zdjęcia w niższej rozdzielczości. Pamiętaj jednak o powyższych wymaganiach dotyczących dokładności, tak aby twarz osoby zajęła jak najwięcej miejsca. Zapoznaj się też ze wskazówkami, jak poprawić wyniki w czasie rzeczywistym.
Niska ostrość obrazów również może wpływać na dokładność. Jeśli nie uzyskasz zadowalających wyników, poproś użytkownika o ponowne zrobienie zdjęcia.
Orientacja twarzy względem aparatu może też mieć wpływ na to, jakie cechy twarzy wykrywa ML Kit. Zobacz Pojęcia związane z wykrywaniem twarzy.
1. Skonfiguruj wykrywanie twarzy
Jeśli chcesz zmienić domyślne ustawienia funkcji wykrywania twarzy, zanim zastosujesz rozpoznawanie twarzy do obiektu, określ je za pomocą obiektuFaceDetectorOptions
.
Możesz zmienić te ustawienia:
Ustawienia | |
---|---|
setPerformanceMode
|
PERFORMANCE_MODE_FAST (domyślnie)
|
PERFORMANCE_MODE_ACCURATE
Zadbaj o szybkość i dokładność wykrywania twarzy. |
setLandmarkMode
|
LANDMARK_MODE_NONE (domyślnie)
|
LANDMARK_MODE_ALL
Określa, czy chcesz rozpoznać „charakterystyczne oznaczenia” twarzy: oczy, uszy, nos, policzki, usta itd. |
setContourMode
|
CONTOUR_MODE_NONE (domyślnie)
|
CONTOUR_MODE_ALL
Określa, czy kontur jest konturowy. Kontury są wykrywane tylko w przypadku najbardziej widocznej twarzy na zdjęciu. |
setClassificationMode
|
CLASSIFICATION_MODE_NONE (domyślnie)
|
CLASSIFICATION_MODE_ALL
Określa, czy twarze mają być klasyfikowane, czy są „uśmiechnięte”, czy „otwarte”. |
setMinFaceSize
|
float (domyślnie: 0.1f )
Ustawia najmniejszy żądany rozmiar twarzy, wyrażony jako stosunek szerokości głowy do szerokości obrazu. |
enableTracking
|
false (domyślnie) | true
Określa, czy chcesz przypisać twarze, aby można było śledzić twarze na obrazach. Pamiętaj, że przy włączonym wykrywaniu konturów wykrywana jest tylko jedna twarz, więc śledzenie twarzy nie daje przydatnych wyników. Z tego powodu, aby zwiększyć szybkość wykrywania, nie włączaj jednocześnie wykrywania konturu i śledzenia twarzy. |
Na przykład:
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. Przygotuj obraz wejściowy
Aby wykrywać twarze na zdjęciu, utwórz obiektInputImage
na podstawie Bitmap
, media.Image
, ByteBuffer
, tablicy bajtów lub pliku na urządzeniu. Następnie przekaż obiekt InputImage
do metody process
obiektu FaceDetector
.
Aby wykrywać twarze, należy użyć obrazu o wymiarach co najmniej 480 x 360 pikseli. Jeśli wykrywasz twarze w czasie rzeczywistym, rejestrowanie klatek przy minimalnej rozdzielczości może zmniejszyć opóźnienia.
Obiekt InputImage
możesz utworzyć z różnych źródeł. Poniżej objaśniamy poszczególne z nich.
Korzystanie z: media.Image
Aby utworzyć obiekt InputImage
z obiektu media.Image
, na przykład podczas robienia zdjęcia aparatem urządzenia, przekaż obiekt media.Image
i obrót obrazu do InputImage.fromMediaImage()
.
Jeśli używasz biblioteki
Aparat X, klasy OnImageCapturedListener
i ImageAnalysis.Analyzer
obliczają wartość rotacji.
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 używasz biblioteki zdjęć, która zapewnia kąt obrotu zdjęcia, możesz obliczyć ją 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ść stopni obrotu do InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Używanie identyfikatora URI pliku
Aby utworzyć obiekt InputImage
z identyfikatora URI pliku, przekaż kontekst aplikacji i identyfikator URI pliku do InputImage.fromFilePath()
. Jest to przydatne, gdy użyjesz intencji ACTION_GET_CONTENT
, aby poprosić użytkownika o wybranie obrazu z 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(); }
Korzystanie z ByteBuffer
lub ByteArray
Aby utworzyć obiekt InputImage
z ByteBuffer
lub ByteArray
, najpierw oblicz stopień obrotu obrazu, jak opisano wcześniej w przypadku danych wejściowych media.Image
.
Następnie utwórz obiekt InputImage
z buforem lub tablicą wraz z wysokością, szerokością, formatem kolorów i stopniem rotacji:
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
, złóż tę deklarację:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Obraz jest reprezentowany przez obiekt Bitmap
wraz ze stopniami obrotu.
3. Pobieranie instancji 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. Przetwórz obraz
Przekaż obraz za pomocą metodyprocess
:
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. Uzyskiwanie informacji o wykrytych twarzach
Jeśli operacja wykrywania twarzy się powiedzie, lista obiektówFace
zostanie przekazana do odbiornika. Każdy obiekt Face
reprezentuje twarz wykryte na zdjęciu. W przypadku każdej twarzy możesz sprawdzić jej współrzędne geograficzne na obrazie wejściowym, a także wszystkie inne informacje, które pozwalają skonfigurować wykrywacz twarzy. Na przykład:
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(); } }
Przykład konturów twarzy
Gdy włączysz wykrywanie konturów twarzy, otrzymasz listę punktów dla każdej wykrytej cechy twarzy. Te punkty reprezentują kształt obiektu. Zobacz Pojęcia dotyczące wykrywania twarzy, aby dowiedzieć się, jak są reprezentowane kontury.
Ten obraz pokazuje, jak te punkty mapują na twarz. Kliknij obraz, aby go powiększyć:
Wykrywanie twarzy w czasie rzeczywistym
Jeśli chcesz używać wykrywania twarzy w aplikacji w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, by uzyskać największą liczbę klatek na sekundę:
Skonfiguruj wykrywacz twarzy do wykrywania konturów lub klasyfikacji i rozpoznawania punktów orientacyjnych, ale nie obu naraz:
Wykrywanie konturów
Wykrywanie punktów orientacyjnych
Klasyfikacja
Wykrywanie i klasyfikacja punktów orientacyjnych
Wykrywanie i konturowanie punktów orientacyjnych
Wykrywanie i klasyfikacja konturów
Wykrywanie konturów, wykrywanie punktów orientacyjnych i klasyfikacjaWłącz tryb
FAST
(domyślnie włączony).Rozważ robienie zdjęć w niższej rozdzielczości. Pamiętaj jednak o wymaganiach dotyczących wymiarów obrazu w tym interfejsie API.
Camera
lub camera2
, ograniczaj wywołania do wzorca do wykrywania treści. Jeśli po uruchomieniu wzorca do wykrywania filmów dostępna będzie nowa ramka, puść ją. Przykład znajdziesz w klasie
VisionProcessorBase
w przykładowej aplikacji krótkiego wprowadzenia.
CameraX
, sprawdź, czy strategia pushpress ma ustawioną wartość domyślną
ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
.
Gwarantuje to, że w danym momencie do analizy zostanie wysłany tylko 1 obraz. Jeśli analizator jest zajęty, więcej obrazów jest usuwanych, a następnie usuwanych automatycznie i nie znajdujących się w kolejce w celu dostarczenia. Gdy analizowany obraz zostanie zamknięty, wywołany jest obiekt ImageProxy.close(). Pojawi się następny obraz.
CameraSourcePreview
i
GraphicOverlay
w przykładowej aplikacji krótkiego wprowadzenia.
ImageFormat.YUV_420_888
. Jeśli używasz starszego interfejsu API aparatu, zrób zdjęcia w formacie ImageFormat.NV21
.