Wykrywanie twarzy przy użyciu ML Kit na Androidzie

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Możesz używać ML Kit do wykrywania twarzy na zdjęciach i filmach.

FunkcjaNiegrupowaneŁączenie w pakiety
WdrażanieModel jest pobierany dynamicznie przez Usługi Google Play.Model jest statycznie połączony z aplikacją podczas kompilacji.
Rozmiar aplikacjiZwiększenie rozmiaru o około 800 KB.Zwiększenie rozmiaru o około 6,9 MB.
Czas inicjowaniaMoże minąć trochę czasu, zanim model zostanie pobrany.Model jest dostępny od razu

Wypróbuj

Zanim zaczniesz

  1. W pliku build.gradle na poziomie projektu umieść repozytorium Google Maven w sekcjach buildscript i allprojects.

  2. 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'
    }
    
  3. 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ą obiektu FaceDetectorOptions. 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 obiekt InputImage 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ą metody process:

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ów Face 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ć:

przykład wykrytej siatki konturowej twarzy

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 klasyfikacja

  • Włą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.

  • Jeśli używasz interfejsu 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.
  • Jeśli korzystasz z interfejsu API 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.
  • Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obrazie wejściowym, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Renderuje się na wyświetlaczu tylko raz dla każdej klatki wejściowej. Przykład znajdziesz w klasach CameraSourcePreview i GraphicOverlay w przykładowej aplikacji krótkiego wprowadzenia.
  • Jeśli używasz interfejsu Camera2 API, zrób zdjęcia w formacie ImageFormat.YUV_420_888. Jeśli używasz starszego interfejsu API aparatu, zrób zdjęcia w formacie ImageFormat.NV21.