Use o Kit de ML para detectar rostos em imagens e vídeos.
Antes de começar
- Adicione o Firebase ao seu projeto para Android, caso ainda não tenha feito isso.
- Adicione as dependências das bibliotecas do Android do Kit de ML ao arquivo
Gradle do módulo (nível do aplicativo) (geralmente
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' }
- Opcional, mas recomendado: configure seu aplicativo para fazer o download automático do modelo de ML no dispositivo após a instalação do aplicativo a partir da Play Store.
Para fazer isso, adicione a seguinte declaração ao arquivo
AndroidManifest.xml
do app:<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Se você não ativar os downloads do modelo no momento da instalação, ele será transferido na primeira vez em que você executar o detector. As solicitações feitas antes da conclusão do download não produzirão resultados.
Diretrizes de imagem de entrada
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ê estiver fazendo a detecção facial em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Já que as imagens menores podem ser processadas mais rapidamente, reduza a latência capturando imagens em resoluções menores (lembrando os requisitos de precisão acima) e faça o rosto da pessoa ocupar o máximo possível da imagem. Consulte também Dicas para melhorar o desempenho em tempo real.
Uma imagem com foco inadequado pode prejudicar a precisão. Se os resultados não forem 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 objetoFirebaseVisionFaceDetectorOptions
se quiser alterar qualquer uma delas.
É possível alterar as seguintes configurações:
Configurações | |
---|---|
Modo de desempenho | FAST (padrão) | ACCURATE Favoreça a velocidade ou a precisão durante a detecção facial. |
Detectar pontos de referência | NO_LANDMARKS (padrão) | ALL_LANDMARKS Para tentar identificar "pontos de referência" faciais: olhos, orelhas, nariz, bochechas, boca e assim por diante. |
Detectar contornos | NO_CONTOURS (padrão) | ALL_CONTOURS Para detectar os contornos dos atributos faciais. São detectados apenas os contornos do rosto mais proeminente de uma imagem. |
Classificar rostos | NO_CLASSIFICATIONS (padrão)
| ALL_CLASSIFICATIONS Se é necessário classificar rostos em categorias como "sorrindo" e "olhos abertos". |
Tamanho mínimo do rosto |
float (padrão: 0.1f )
O tamanho mínimo, relativo à imagem, de rostos a serem detectados. |
Ativar rastreamento facial | false (padrão) | true Atribuir ou não IDs, que podem ser usados 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. |
Por exemplo:
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. Executar o detector facial
Para detectar rostos em uma imagem, crie um objetoFirebaseVisionImage
usando Bitmap
, media.Image
, ByteBuffer
, matriz de bytes ou um arquivo no
dispositivo. Em seguida, transmita o objeto FirebaseVisionImage
para o método detectInImage
do FirebaseVisionFaceDetector
.
Para reconhecimento facial, você deve usar uma imagem com dimensões de pelo menos 480x360 pixels. Se você estiver fazendo reconhecimento facial em tempo real, a captura de frames com essa resolução mínima poderá ajudar a reduzir a latência.
Crie um objeto
FirebaseVisionImage
a partir da imagem.-
Para criar um objeto
FirebaseVisionImage
a partir de um objetomedia.Image
, como ao capturar uma imagem da câmera de um dispositivo, transmita o objetomedia.Image
e a rotação da imagem paraFirebaseVisionImage.fromMediaImage()
.Se você usa a biblioteca CameraX, as classes
OnImageCapturedListener
eImageAnalysis.Analyzer
calculam o valor de rotação para você. Basta converter a rotação em uma das constantesROTATION_
do Kit de ML antes de chamarFirebaseVisionImage.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 // ... } } }
Se você não usa uma biblioteca de câmera que ofereça a rotação da imagem, faça o cálculo usando a rotação do dispositivo e a orientação do sensor da câmera:
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 }
Em seguida, transmita o objeto
media.Image
e o valor de rotação paraFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Para criar um objeto
FirebaseVisionImage
com base no URI de um arquivo, transmita o contexto do aplicativo e o URI do arquivo paraFirebaseVisionImage.fromFilePath()
. Isso é útil ao usar uma intentACTION_GET_CONTENT
para solicitar que o usuário selecione uma imagem no app de galeria dele.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() }
- Para criar um objeto
FirebaseVisionImage
com base em umByteBuffer
ou uma matriz de bytes, primeiro calcule a rotação da imagem conforme descrito acima para a entradamedia.Image
.Em seguida, crie um objeto
FirebaseVisionImageMetadata
que contenha a altura, a largura, o formato de codificação de cores e a rotação da imagem: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()
Use o buffer ou a matriz e o objeto de metadados para criar um objeto
FirebaseVisionImage
: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)
- Para criar um objeto
FirebaseVisionImage
com base em um objetoBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
precisa estar na posição vertical, sem a necessidade de ser girada novamente.
-
Receba uma instância de
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin+KTX
val detector = FirebaseVision.getInstance() .getVisionFaceDetector(options)
Por fim, transmita a imagem para o método
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. Receber informações sobre faces detectadas
Se a operação de reconhecimento facial for bem-sucedida, uma lista de objetos deFirebaseVisionFace
será transmitida para o listener de êxito. Cada objeto FirebaseVisionFace
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. Por exemplo:
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 } }
Exemplo de contornos faciais
Quando a detecção de contorno facial está ativada, você recebe uma lista de pontos para cada traço facial detectado. Esses pontos representam a forma do traço. Consulte a Visão geral dos 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 nela para ampliar:
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 contornos
Detecção de pontos de referência
Classificação
Detecção e classificação de pontos de referência
Detecção de contornos e detecção de pontos de referência
Detecção e classificação de contornos
Detecção de contornos, detecção de pontos de referência e classificaçãoAtive 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 da API.
- Limite as chamadas ao detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, descarte esse frame.
- Se você estiver usando a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML. Em seguida, renderize a imagem e faça a sobreposição de uma só vez. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada.
-
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
.