Za pomocą pakietu ML Kit możesz wykrywać twarze na obrazach i w filmach.
Zanim zaczniesz
- Jeśli jeszcze nie masz tego za sobą, dodaj Firebase do swojego projektu na Androida.
- Dodaj do modułu zależności między bibliotekami ML Kit na Androida
Plik Gradle (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' }
-
Opcjonalne, ale zalecane: skonfiguruj automatyczne pobieranie aplikacji.
po zainstalowaniu aplikacji ze Sklepu Play na urządzeniu.
Aby to zrobić, dodaj tę deklarację do Plik
AndroidManifest.xml
: Jeśli nie włączysz pobierania modelu w czasie instalacji, model zostanie pobrane przy pierwszym uruchomieniu wzorca. Żądania przesyłane przed pobieranie nie da ż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>
Wytyczne dotyczące obrazu wejściowego
Aby ML Kit mógł precyzyjnie wykrywać twarze, obrazy wejściowe muszą zawierać twarze które są reprezentowane przez wystarczającą ilość danych pikseli. Ogólnie rzecz biorąc, każda twarz, którą chcesz pokazać, powinna mieć rozmiar co najmniej 100 x 100 pikseli. Jeśli chcesz wykrywać aby określić kontury twarzy, ML Kit wymaga wyższej rozdzielczości: powinien wynosić co najmniej 200 x 200 pikseli.
Jeśli wykrywasz twarze w aplikacji działającej w czasie rzeczywistym, możesz też aby wziąć pod uwagę ogólne wymiary obrazów wejściowych. Mniejsze obrazy szybsze przetwarzanie, a więc aby zmniejszyć opóźnienie, można robić zdjęcia w niższej rozdzielczości. (pamiętając o powyższych wymaganiach dotyczących dokładności) i upewnij się, że twarz obiektu zajmuje jak najwięcej miejsca na obrazie. Zobacz też Wskazówki, jak zwiększyć skuteczność w czasie rzeczywistym.
Słaba ostrość obrazu może negatywnie wpływać na dokładność. Jeśli nie uzyskujesz akceptowalnych wyników, poproś użytkownika o ponowne zrobienie zdjęcia.
Położenie twarzy w odniesieniu do aparatu może też wpływać na jej wygląd. wykrywanych przez ML Kit. Zobacz Wykrywanie twarzy Pojęcia.
1. Konfigurowanie wykrywania twarzy
Zanim zastosujesz wykrywanie twarzy na zdjęciu, możesz zmienić domyślnych ustawień wykrywania twarzy, określ je za pomocąFirebaseVisionFaceDetectorOptions
.
Można zmienić następujące ustawienia:
Ustawienia | |
---|---|
Tryb wydajności |
FAST (domyślna)
| ACCURATE
Większa szybkość lub dokładność podczas wykrywania twarzy. |
Wykrywanie punktów orientacyjnych |
NO_LANDMARKS (domyślna)
| ALL_LANDMARKS
Określa, czy rozpoznać „punkty orientacyjne” twarzy: oczy, uszy, nos, policzki, usta itd. |
Rozpoznaj kontury |
NO_CONTOURS (domyślna)
| ALL_CONTOURS
Określa, czy wykrywać kontury rysów twarzy. Kontury są tylko dla najbardziej widocznej twarzy na zdjęciu. |
Klasyfikuj twarze |
NO_CLASSIFICATIONS (domyślna)
| ALL_CLASSIFICATIONS
Możliwość sklasyfikowania twarzy w kategoriach, takich jak „uśmiech”, i „oczy otwarte”. |
Minimalny rozmiar twarzy |
float (domyślnie: 0.1f )
Minimalny rozmiar twarzy do wykrycia w odniesieniu do obrazu. |
Włącz śledzenie twarzy |
false (domyślna) | true
Określa, czy przypisywać twarzom identyfikator, który może służyć do śledzenia i twarze na zdjęciach. Pamiętaj, że przy włączonym wykrywaniu kontur tylko jedna twarz więc śledzenie twarzy nie da żadnych przydatnych wyników. Do tego celu i aby zwiększyć szybkość wykrywania, nie włączaj obu konturów wykrywaniem oraz śledzeniem 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+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. Włącz wykrywanie twarzy
Aby wykrywać twarze na obrazie, utwórz obiektFirebaseVisionImage
z obiektu Bitmap
, media.Image
, ByteBuffer
, tablicy bajtów lub pliku w
urządzenia. Następnie przekaż obiekt FirebaseVisionImage
do funkcji
Metoda detectInImage
użytkownika FirebaseVisionFaceDetector
.
Do rozpoznawania twarzy należy użyć zdjęcia o wymiarach co najmniej 480 x 360 pikseli. Jeśli rozpoznajesz twarze w czasie rzeczywistym i rejestrujesz klatki, przy minimalnej rozdzielczości może pomóc zmniejszyć opóźnienie.
Utwórz obiekt
FirebaseVisionImage
na podstawie .-
Aby utworzyć obiekt
FirebaseVisionImage
na podstawiemedia.Image
, np. podczas przechwytywania obrazu z z aparatu urządzenia, przekazać obiektmedia.Image
oraz w kierunkuFirebaseVisionImage.fromMediaImage()
.Jeśli używasz tagu CameraX,
OnImageCapturedListener
orazImageAnalysis.Analyzer
klasy obliczają wartość rotacji więc wystarczy zmienić rotację na jeden z zestawów ML Kit StałyROTATION_
przed nawiązaniem połączeniaFirebaseVisionImage.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 // ... } } }
Jeśli nie korzystasz z biblioteki aparatu zapewniającej obrót obrazu, może go obliczyć na podstawie obrotu urządzenia i orientacji aparatu czujnik w 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+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 }
Następnie przekaż obiekt
media.Image
oraz wartość rotacji doFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Aby utworzyć obiekt
FirebaseVisionImage
na podstawie identyfikatora URI pliku, przekaż kontekst aplikacji i identyfikator URI plikuFirebaseVisionImage.fromFilePath()
Jest to przydatne, gdy użyj intencjiACTION_GET_CONTENT
, aby zachęcić użytkownika do wyboru obraz z aplikacji Galeria.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() }
- Aby utworzyć obiekt
FirebaseVisionImage
na podstawieByteBuffer
lub tablicy bajtów, najpierw oblicz wartość obrazu w sposób opisany powyżej dla danych wejściowychmedia.Image
.Następnie utwórz obiekt
FirebaseVisionImageMetadata
określającą wysokość, szerokość i format kodowania kolorów obrazu i rotacja: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()
Za pomocą bufora lub tablicy oraz obiektu metadanych utwórz
FirebaseVisionImage
obiekt: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)
- Aby utworzyć obiekt
FirebaseVisionImage
na podstawie ObiektBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
musi być pionowo bez konieczności dodatkowego obracania.
-
Pobierz instancję
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin+KTX
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+KTX
val result = detector.detectInImage(image) .addOnSuccessListener { faces -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
3. Uzyskiwanie informacji o wykrytych twarzy
Jeśli operacja rozpoznawania twarzy zakończy się powodzeniem, listaFirebaseVisionFace
obiektów zostanie przekazanych do powodzenia
słuchacz. Każdy obiekt FirebaseVisionFace
reprezentuje twarz, która została wykryta
zdjęcia. W przypadku każdej płaszczyzny można uzyskać współrzędne ograniczające dla każdej płaszczyzny
oraz wszelkie inne informacje, na które skonfigurowano wykrywanie twarzy
znaleźć. 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+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 } }
Przykład konturu twarzy
Gdy włączysz wykrywanie konturu twarzy, zobaczysz listę punktów za wszystkie wykryte cechy twarzy. Te punkty odpowiadają kształtowi funkcji. Zobacz twarz Omówienie pojęć związanych z wykrywaniem, aby uzyskać szczegółowe informacje o konturach reprezentowanych.
Na poniższym obrazie przedstawiono mapowanie tych punktów na twarz (kliknij obraz do powiększenia):
Wykrywanie twarzy w czasie rzeczywistym
Jeśli chcesz używać wykrywania twarzy w aplikacjach działających w czasie rzeczywistym, postępuj zgodnie z tymi instrukcjami wytycznych dotyczących uzyskiwania najlepszej liczby klatek na sekundę:
Skonfiguruj wykrywacz twarzy, aby używał jednej wykrywanie kontur lub klasyfikacja twarzy i wykrywanie punktów orientacyjnych, ale nie oba te rodzaje naraz:
Wykrywanie konturów
Wykrywanie punktów orientacyjnych
Klasyfikacja
Wykrywanie i klasyfikacja punktów orientacyjnych
Wykrywanie konturów i wykrywanie punktów orientacyjnych
Wykrywanie i klasyfikacja kontur
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, wymagania dotyczące wymiarów obrazów w tym interfejsie API.
- Ogranicz wywołania do detektora. Jeśli nowa klatka wideo dostępnych, gdy detektor jest uruchomiony, upuść ramkę.
- Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obrazu wejściowego, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładanie nakładek w jednym kroku. W ten sposób renderowanie na powierzchni tylko raz na każdą ramkę wejściową.
-
Jeśli korzystasz z interfejsu API Camera2, rób zdjęcia w Format:
ImageFormat.YUV_420_888
.Jeśli używasz starszej wersji interfejsu Camera API, rób zdjęcia w Format:
ImageFormat.NV21
.