Detectar rostos com o Kit de ML no Android

Use o Kit de ML para detectar rostos em imagens e vídeos.

Há duas maneiras de integrar a detecção facial: um modelo agrupado que faz parte do seu app e um modelo desagrupado que depende do Google Play Services. Os dois modelos são iguais. Se você selecionar o modelo desagrupado, o app será menor.

Seleção deDesagrupadoAgrupados
ImplementaçãoO download do modelo é feito dinamicamente pelo Google Play Services.O modelo é estaticamente vinculado ao seu app no momento da compilação.
Tamanho do appAumento de cerca de 800 KB.Aumento de aproximadamente 6,9 MB.
Tempo de inicializaçãoPode ser necessário aguardar o download do modelo para usá-lo pela primeira vez.O modelo está disponível imediatamente
  • Teste o app de exemplo para ver um exemplo de uso dessa API.
  • Tente o código por conta própria com o codelab.

Antes de começar

  1. No arquivo build.gradle no nível do projeto, inclua o repositório Maven do Google nas seções buildscript e allprojects.

  2. Adicionar as dependências das bibliotecas do Android do Kit de ML ao arquivo do Gradle no nível do app do seu módulo, que geralmente é app/build.gradle. Escolha uma das seguintes dependências com base nas suas necessidades:

    Para empacotar o modelo com o app, faça o seguinte:

    dependencies {
      // ...
      // Use this dependency to bundle the model with your app
      implementation 'com.google.mlkit:face-detection:16.1.5'
    }
    

    Para usar o modelo no Google Play Services:

    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. Se você quiser usar o modelo no Google Play Services, poderá configurar o app para fazer o download automático do modelo no dispositivo após a instalação do app na Play Store. Para fazer isso, adicione a seguinte declaração ao arquivo AndroidManifest.xml do app:

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="face" >
          <!-- To use multiple models: android:value="face,model2,model3" -->
    </application>
    

    Também é possível verificar explicitamente a disponibilidade do modelo e solicitar o download por meio da API ModuleInstallClient do Google Play Services.

    Se você não ativar os downloads do modelo de tempo de instalação ou solicitar o download explícito, o download do modelo será feito na primeira vez que você executar o detector. As solicitações feitas antes da conclusão do download não produzem resultados.

Diretrizes de imagem de entrada

Para reconhecimento facial, você deve usar uma imagem com dimensões de pelo menos 480x360 pixels. Para que o Kit de ML detecte rostos com precisão, as imagens de entrada devem conter rostos representados por dados de pixel suficientes. Em geral, cada rosto que você quiser detectar em uma imagem deverá ter pelo menos 100x100 pixels. Se você quiser detectar os contornos dos rostos, o Kit de ML precisará de uma entrada de resolução mais alta: cada rosto deverá ter pelo menos 200x200 pixels.

Se você detectar rostos em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Já que imagens menores podem ser processadas mais rapidamente, para reduzir a latência, capture imagens em resoluções mais baixas, mas lembre-se dos requisitos de precisão acima e certifique-se de que o rosto da pessoa ocupe o máximo possível da imagem. Veja também dicas para melhorar o desempenho em tempo real.

Uma imagem com foco inadequado também pode afetar a precisão. Se você não receber resultados aceitáveis, peça para o usuário recapturar a imagem.

A orientação de um rosto em relação à câmera também pode afetar os atributos faciais que o Kit de ML detecta. Veja Conceitos de detecção facial.

1. Configurar o detector facial

Antes de aplicar a detecção facial a uma imagem, especifique essas configurações com um objeto FaceDetectorOptions se quiser alterar qualquer uma delas. É possível alterar as seguintes configurações:

Configurações
setPerformanceMode PERFORMANCE_MODE_FAST (padrão) | PERFORMANCE_MODE_ACCURATE

Favoreça a velocidade ou a precisão durante a detecção facial.

setLandmarkMode LANDMARK_MODE_NONE (padrão) | LANDMARK_MODE_ALL

Para tentar identificar pontos turísticos "landlandês&quot: olhos, orelhas, nariz, bochechas, boca e assim por diante.

setContourMode CONTOUR_MODE_NONE (padrão) | CONTOUR_MODE_ALL

Para detectar os contornos dos atributos faciais. São detectados apenas os contornos do rosto mais proeminente de uma imagem.

setClassificationMode CLASSIFICATION_MODE_NONE (padrão) | CLASSIFICATION_MODE_ALL

Se é necessário classificar rostos em categorias como "sorrindo" e "olhos abertos"

setMinFaceSize float (padrão: 0.1f)

Define o menor tamanho de rosto desejado, expresso como a proporção entre a largura da cabeça e a largura da imagem.

enableTracking false (padrão) | true

Se é necessário atribuir um código a rostos, que pode ser usado para rastrear rostos em imagens.

Quando a detecção de contorno está ativada, apenas um rosto é detectado, portanto, o rastreamento facial não produz resultados úteis. Por esse motivo, e para melhorar a velocidade de detecção, não ative a detecção de contorno simultaneamente com o rastreamento facial.

Exemplo:

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. Preparar a imagem de entrada

Para detectar rostos em uma imagem, crie um objeto InputImage a partir de um Bitmap, media.Image, ByteBuffer, matriz de bytes ou um arquivo no dispositivo. Em seguida, transmita o objeto InputImage para o método process do FaceDetector.

Para detecção facial, você deve usar uma imagem com dimensões de pelo menos 480x360 pixels. Se você estiver fazendo a detecção facial em tempo real, a captura de frames com essa resolução mínima poderá ajudar a reduzir a latência.

É possível criar um objeto InputImage usando diferentes origens, cada uma explicada abaixo.

Como usar um media.Image

Para criar um objeto InputImage usando um objeto media.Image, como quando você captura uma imagem de uma câmera de um dispositivo, transmita o objeto media.Image e a rotação da imagem para InputImage.fromMediaImage().

Se você usar a biblioteca CameraX, as classes OnImageCapturedListener e ImageAnalysis.Analyzer vão calcular o valor de rotação automaticamente.

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
          // ...
        }
    }
}

Se você não usar uma biblioteca de câmera que ofereça o grau de rotação da imagem, poderá calculá-lo usando o grau de rotação do dispositivo e a orientação do sensor da câmera:

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;
}

Em seguida, transmita o objeto media.Image e o valor do grau de rotação para InputImage.fromMediaImage():

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

Como usar um URI de arquivo

Para criar um objeto InputImage a partir de um URI de arquivo, transmita o contexto do app e o URI do arquivo para InputImage.fromFilePath(). Isso é útil ao usar uma intent ACTION_GET_CONTENT para solicitar que o usuário selecione uma imagem do aplicativo de galeria dele.

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();
}

Como usar ByteBuffer ou ByteArray

Para criar um objeto InputImage usando um ByteBuffer ou ByteArray, primeiro calcule o grau de rotação da imagem conforme descrito anteriormente para a entrada media.Image. Em seguida, crie o objeto InputImage com o buffer ou a matriz, com a altura, a largura, o formato de codificação de cores e o grau de rotação da imagem:

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
);

Como usar um Bitmap

Para criar um objeto InputImage usando um objeto Bitmap, faça a seguinte declaração:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

A imagem é representada por um objeto Bitmap com os graus de rotação.

3. Receber uma instância do 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. Processar a imagem

Transmita a imagem para o método 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. Receber informações sobre rostos detectados

Se a operação de detecção facial for bem-sucedida, uma lista de objetos de Face será transmitida para o listener de êxito. Cada objeto Face representa um rosto detectado na imagem. Para cada rosto, é possível receber as coordenadas delimitadoras na imagem de entrada, além de qualquer outra informação que você tenha configurado para detecção. Exemplo:

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();
    }
}

Exemplo de contornos faciais

Quando a detecção de contorno facial está ativada, você recebe uma lista de pontos para cada atributo facial que foi detectado. Esses pontos representam a forma do elemento. Consulte Conceitos de detecção facial para ver detalhes sobre como os contornos são representados.

A imagem a seguir ilustra como esses pontos mapeiam um rosto. Clique para ampliá-la:

exemplo de malha de contorno facial detectada

Detecção facial em tempo real

Se você quiser usar a detecção facial em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames.

  • Configure o detector facial para usar a detecção de contorno facial ou a classificação e a detecção de pontos de referência, mas não os dois:

    Detecção de contorno
    Detecção de pontos de referência
    Classificação
    Detecção e classificação de pontos de referência
    Detecção de contorno e detecção de pontos de referência
    Detecção de contorno e classificação
    Detecção de contorno, detecção e classificação de pontos de referência

  • Ative o modo FAST (ativado por padrão).

  • Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem desta API.

  • Se você usa a API Camera ou camera2, o limite de chamadas para o detector é limitado. Se um novo frame de vídeo for disponibilizado enquanto o detector estiver em execução, descarte o frame. Consulte a classe VisionProcessorBase no app de amostra do guia de início rápido para ver um exemplo.
  • Se você usar a API CameraX, verifique se a estratégia de contrapressão está definida com o valor padrão ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Isso garante que apenas uma imagem seja enviada por vez para análise. Se mais imagens forem produzidas quando o analisador estiver ocupado, elas serão descartadas automaticamente e não serão colocadas na fila para entrega. Quando a imagem que está sendo analisada é fechada, chamando ImageProxy.close(), a próxima imagem mais recente é enviada.
  • Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ela será renderizada na superfície de exibição apenas uma vez para cada frame de entrada. Consulte as classes CameraSourcePreview e GraphicOverlay no app de amostra do guia de início rápido para ver um exemplo.
  • Se você usar a API Camera2, capture imagens no formato ImageFormat.YUV_420_888. Se você usar a API Camera mais antiga, capture imagens no formato ImageFormat.NV21.