Możesz używać ML Kit do wykrywania twarzy na zdjęciach i w filmach.
Zanim zaczniesz
- Jeśli jeszcze tego nie zrobiono, dodaj Firebase do projektu na Androida.
- Dodaj zależności do bibliotek ML Kit na Androida do pliku Gradle modułu (na poziomie aplikacji) (zwykle
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' }
-
Opcjonalnie, ale zalecane: skonfiguruj aplikację tak, aby po zainstalowaniu jej ze Sklepu Play automatycznie pobierała model ML na urządzenie.
Aby to zrobić, dodaj do pliku
AndroidManifest.xml
aplikacji następującą deklarację: Jeśli nie włączysz pobierania modeli w czasie instalacji, model zostanie pobrany przy pierwszym uruchomieniu funkcji wykrywania. Żądania wysłane przed zakończeniem pobierania nie przyniosą żadnych wyników.<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Wskazówki dotyczące obrazów wejściowych
Aby ML Kit mógł dokładnie wykrywać twarze, obrazy wejściowe muszą zawierać twarze reprezentowane przez wystarczającą ilość danych pikseli. Ogólnie rzecz biorąc, każda twarz, którą chcesz wykryć na zdjęciu, powinna mieć co najmniej 100 × 100 pikseli. Jeśli chcesz wykrywać kontury twarzy, ML Kit wymaga podania danych o wyższej rozdzielczości: każda twarz powinna mieć co najmniej 200 x 200 pikseli.
Jeśli wykrywanie twarzy odbywa się w aplikacji w czasie rzeczywistym, warto też wziąć pod uwagę ogólne wymiary obrazów wejściowych. Mniejsze obrazy można przetwarzać szybciej, więc aby zmniejszyć opóźnienie, rób zdjęcia w niższej rozdzielczości (z uwzględnieniem powyższych wymagań dotyczących dokładności) i upewnij się, że twarz osoby na zdjęciu zajmuje jak największą część obrazu. Zobacz też wskazówki dotyczące zwiększania skuteczności w czasie rzeczywistym.
Nieostre zdjęcie może obniżyć dokładność. Jeśli nie uzyskujesz zadowalających wyników, poproś użytkownika o ponowne zrobienie zdjęcia.
Na to, jakie cechy twarzy wykryje ML Kit, może mieć wpływ orientacja twarzy względem aparatu. Zobacz pojęcia związane z wykrywaniem twarzy.
1. Konfigurowanie modułu wykrywania twarzy
Jeśli przed zastosowaniem wykrywania twarzy na obrazie chcesz zmienić jakieś ustawienia domyślne wykrywacza twarzy, określ te ustawienia za pomocą obiektuFirebaseVisionFaceDetectorOptions
.
Możesz zmienić te ustawienia:
Ustawienia | |
---|---|
Tryb wydajności |
FAST (domyślnie)
| ACCURATE
preferować szybkość lub dokładność wykrywania twarzy; |
Wykrywanie punktów orientacyjnych |
NO_LANDMARKS (domyślnie)
| ALL_LANDMARKS
Czy należy próbować zidentyfikować „punkty orientacyjne” twarzy: oczy, uszy, nos, policzki, usta itp. |
Wykrywanie kontur |
NO_CONTOURS (domyślnie)
| ALL_CONTOURS
Określa, czy mają być wykrywane kontury elementów twarzy. Kontury są wykrywane tylko w przypadku najbardziej widocznej twarzy na zdjęciu. |
Klasyfikowanie twarzy |
NO_CLASSIFICATIONS (domyślnie)
| ALL_CLASSIFICATIONS
Określa, czy twarze mają być klasyfikowane według kategorii takich jak „uśmiech” czy „otwarte oczy”. |
Minimalny rozmiar twarzy |
float (domyślnie: 0.1f )
Minimalny rozmiar twarzy do wykrycia w stosunku do obrazu. |
Włączanie śledzenia twarzy |
false (domyślnie) | true
czy chcesz przypisywać twarzom identyfikatory, które mogą służyć do śledzenia twarzy na zdjęciach. Pamiętaj, że po włączeniu wykrywania kontur wykrywane jest tylko jedno oblicze, więc śledzenie twarzy nie przynosi przydatnych rezultatów. Z tego powodu oraz aby zwiększyć szybkość wykrywania, nie włączaj jednocześnie wykrywania kontur i śledzenia twarzy. |
Przykład:
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. Uruchom wykrywanie twarzy
Aby wykrywać twarze na obrazie, utwórz obiektFirebaseVisionImage
z elementu Bitmap
, media.Image
, ByteBuffer
, tablicy bajtów lub pliku na urządzeniu. Następnie prześlij obiekt FirebaseVisionImage
do metody detectInImage
obiektu FirebaseVisionFaceDetector
.
Do rozpoznawania twarzy należy użyć obrazu o wymiarach co najmniej 480 x 360 pikseli. Jeśli rozpoznawanie twarzy odbywa się w czasie rzeczywistym, rejestrowanie klatek w tym minimalnym rozmiarze może pomóc w zmniejszeniu opóźnienia.
Utwórz obiekt
FirebaseVisionImage
na podstawie swojego obrazu.-
Aby utworzyć obiekt
FirebaseVisionImage
na podstawie obiektumedia.Image
, na przykład podczas robienia zdjęcia aparatem urządzenia, przekaż obiektmedia.Image
i obrót obrazu do obiektuFirebaseVisionImage.fromMediaImage()
.Jeśli używasz biblioteki CameraX, klasy
OnImageCapturedListener
iImageAnalysis.Analyzer
obliczają wartość rotacji za Ciebie, więc przed wywołaniem funkcjiFirebaseVisionImage.fromMediaImage()
musisz tylko przekonwertować rotację na jedną z konstantROTATION_
w ML Kit: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 // ... } } }
Jeśli nie używasz biblioteki aparatu, która zapewnia obrócenie obrazu, możesz obliczyć je na podstawie obrotu urządzenia i orientacji czujnika aparatu na urządzeniu:
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 }
Następnie prześlij obiekt
media.Image
i wartość obrotu do funkcjiFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Aby utworzyć obiekt
FirebaseVisionImage
na podstawie identyfikatora URI pliku, prześlij kontekst aplikacji i identyfikator URI pliku do funkcjiFirebaseVisionImage.fromFilePath()
. Jest to przydatne, gdy używasz intencjiACTION_GET_CONTENT
, aby poprosić użytkownika o wybranie obrazu z aplikacji Galeria.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() }
- Aby utworzyć obiekt
FirebaseVisionImage
zByteBuffer
lub tablicy bajtów, najpierw oblicz obrót obrazu w sposób opisany powyżej w przypadku wejściamedia.Image
.Następnie utwórz obiekt
FirebaseVisionImageMetadata
, który zawiera wysokość, szerokość, format kodowania kolorów oraz obrót obrazu: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()
Użyj bufora lub tablicy oraz obiektu metadanych, aby utworzyć obiekt
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)
- Aby utworzyć obiekt
FirebaseVisionImage
z obiektuBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
musi być pionowy i nie wymagać dodatkowego obracania.
-
Pobieranie instancji
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin
val detector = FirebaseVision.getInstance() .getVisionFaceDetector(options)
Na koniec przekaż obraz do metody
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. Uzyskiwanie informacji o wykryć twarze
Jeśli operacja rozpoznawania twarzy się powiedzie, do listenera success zostanie przekazana lista obiektówFirebaseVisionFace
. Każdy obiekt FirebaseVisionFace
reprezentuje twarz wykrytą na zdjęciu. W przypadku każdej twarzy możesz uzyskać współrzędne ograniczające w podawanym obrazie, a także wszelkie inne informacje, które skonfigurowałeś/skonfigurowałaś w detektorze twarzy. Przykład:
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 } }
Przykład kontur twarzy
Gdy włączysz wykrywanie konturu twarzy, otrzymasz listę punktów dla każdego wykrytego elementu twarzy. Te punkty reprezentują kształt funkcji. Szczegółowe informacje o reprezentowaniu kontur znajdziesz w artykule Face Detection Concepts Overview (omówienie koncepcji wykrywania twarzy).
Na poniższym obrazie widać, jak te punkty są mapowane na twarz (kliknij obraz, aby go powiększyć):
Wykrywanie twarzy w czasie rzeczywistym
Jeśli chcesz używać funkcji wykrywania twarzy w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:
Skonfiguruj wykrywacz twarzy, aby używać wykrywania kontur twarzy lub klasyfikacji i wykrywania punktów orientacyjnych, ale nie obu tych funkcji jednocześnie:
Wykrywanie kontur
Wykrywanie punktów orientacyjnych
Klasyfikacja
Wykrywanie i klasyfikacja punktów orientacyjnych
Wykrywanie kontur i punktów orientacyjnych
Wykrywanie kontur i klasyfikacja
Wykrywanie kontur, 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.
- ograniczać wywołania do tego urządzenia. Jeśli podczas działania detektora pojawi się nowa klatka wideo, odrzuć ją.
- Jeśli używasz danych wyjściowych z detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a potem wyrenderuj obraz i nałóż go w jednym kroku. W ten sposób renderujesz na powierzchni wyświetlacza tylko raz w przypadku każdej ramki wejściowej.
-
Jeśli używasz interfejsu Camera2 API, rób zdjęcia w formacie
ImageFormat.YUV_420_888
.Jeśli używasz starszej wersji interfejsu Camera API, rób zdjęcia w formacie
ImageFormat.NV21
.