Vous pouvez utiliser ML Kit pour détecter des visages dans des images et des vidéos.
Avant de commencer
- Si ce n'est pas encore fait, ajoutez Firebase à votre projet Android.
- Ajoutez les dépendances des bibliothèques Android ML Kit au fichier Gradle de votre module (au niveau de l'application) (généralement
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' }
-
Facultatif, mais recommandé: configurez votre application pour qu'elle télécharge automatiquement le modèle de ML sur l'appareil une fois qu'elle est installée depuis le Play Store.
Pour ce faire, ajoutez la déclaration suivante au fichier
AndroidManifest.xml
de votre application: Si vous n'activez pas les téléchargements de modèles au moment de l'installation, le modèle sera téléchargé la première fois que vous exécuterez le détecteur. Les requêtes que vous effectuez avant la fin du téléchargement ne produiront aucun résultat.<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Consignes concernant les images d'entrée
Pour que ML Kit détecte précisément les visages, les images d'entrée doivent contenir des visages représentés par suffisamment de données de pixel. En général, chaque visage que vous souhaitez détecter dans une image doit faire au moins 100 x 100 pixels. Si vous souhaitez détecter les contours des visages, ML Kit nécessite une entrée de résolution supérieure: chaque visage doit mesurer au moins 200 x 200 pixels.
Si vous détectez des visages dans une application en temps réel, vous pouvez également tenir compte des dimensions globales des images d'entrée. Les images de petite taille peuvent être traitées plus rapidement. Pour réduire la latence, capturez des images à des résolutions inférieures (en gardant à l'esprit les exigences de précision ci-dessus) et assurez-vous que le visage du sujet occupe autant que possible l'image. Consultez également Conseils pour améliorer les performances en temps réel.
Un mauvais cadrage peut nuire à la précision. Si vous n'obtenez pas de résultats acceptables, essayez de demander à l'utilisateur de reprendre la photo.
L'orientation d'un visage par rapport à la caméra peut également affecter les caractéristiques faciales détectées par ML Kit. Consultez la section Concepts de la détection des visages.
1. Configurer le détecteur de visage
Avant d'appliquer la détection de visage à une image, si vous souhaitez modifier l'un des paramètres par défaut du détecteur de visage, spécifiez-le avec un objetFirebaseVisionFaceDetectorOptions
.
Vous pouvez modifier les paramètres suivants:
Paramètres | |
---|---|
Mode Performances |
FAST (par défaut)
| ACCURATE
Privilégiez la vitesse ou la précision lors de la détection des visages. |
Détecter les points de repère |
NO_LANDMARKS (par défaut)
| ALL_LANDMARKS
Indique si vous devez tenter d'identifier des "points de repère" faciaux : yeux, oreilles, nez, joues, bouche, etc. |
Détecter les contours |
NO_CONTOURS (par défaut)
| ALL_CONTOURS
Indique si les contours des traits du visage doivent être détectés. Les contours ne sont détectés que pour le visage le plus visible d'une image. |
Classer des visages |
NO_CLASSIFICATIONS (par défaut)
| ALL_CLASSIFICATIONS
Indique si les visages doivent être classés ou non en catégories telles que "sourire" et "yeux ouverts". |
Taille minimale du visage |
float (par défaut: 0.1f )
Taille minimale, par rapport à l'image, des visages à détecter. |
Activer le suivi des visages |
false (par défaut) | true
Indique si vous devez attribuer un ID aux visages, qui peut être utilisé pour suivre les visages dans les images. Notez que lorsque la détection des contours est activée, un seul visage est détecté. Le suivi du visage ne produit donc pas de résultats utiles. Pour cette raison et pour améliorer la vitesse de détection, n'activez pas à la fois la détection des contours et le suivi du visage. |
Exemple :
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. Exécuter le détecteur de visages
Pour détecter des visages dans une image, créez un objetFirebaseVisionImage
à partir d'un Bitmap
, media.Image
, ByteBuffer
, d'un tableau d'octets ou d'un fichier sur l'appareil. Transmettez ensuite l'objet FirebaseVisionImage
à la méthode detectInImage
de FirebaseVisionFaceDetector
.
Pour la reconnaissance faciale, vous devez utiliser une image dont les dimensions sont d'au moins 480 x 360 pixels. Si vous reconnaissez des visages en temps réel, la capture de frames à cette résolution minimale peut contribuer à réduire la latence.
Créez un objet
FirebaseVisionImage
à partir de votre image.-
Pour créer un objet
FirebaseVisionImage
à partir d'un objetmedia.Image
, par exemple lorsque vous capturez une image à partir de l'appareil photo d'un appareil, transmettez l'objetmedia.Image
et la rotation de l'image àFirebaseVisionImage.fromMediaImage()
.Si vous utilisez la bibliothèque CameraX, les classes
OnImageCapturedListener
etImageAnalysis.Analyzer
calculent la valeur de rotation à votre place. Il vous suffit donc de convertir la rotation en l'une des constantesROTATION_
de ML Kit avant d'appelerFirebaseVisionImage.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
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 // ... } } }
Si vous n'utilisez pas de bibliothèque d'appareil photo qui vous indique la rotation de l'image, vous pouvez la calculer à partir de la rotation de l'appareil et de l'orientation du capteur de l'appareil photo dans l'appareil:
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 }
Transmettez ensuite l'objet
media.Image
et la valeur de rotation àFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Pour créer un objet
FirebaseVisionImage
à partir d'un URI de fichier, transmettez le contexte de l'application et l'URI de fichier àFirebaseVisionImage.fromFilePath()
. Cela est utile lorsque vous utilisez un intentACTION_GET_CONTENT
pour inviter l'utilisateur à sélectionner une image dans son application Galerie.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() }
- Pour créer un objet
FirebaseVisionImage
à partir d'unByteBuffer
ou d'un tableau d'octets, commencez par calculer la rotation de l'image comme décrit ci-dessus pour l'entréemedia.Image
.Créez ensuite un objet
FirebaseVisionImageMetadata
contenant la hauteur, la largeur, le format d'encodage des couleurs et la rotation de l'image: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()
Utilisez le tampon ou le tableau, ainsi que l'objet de métadonnées, pour créer un objet
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)
- Pour créer un objet
FirebaseVisionImage
à partir d'un objetBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
doit être à l'endroit, sans rotation supplémentaire requise.
-
Obtenez une instance de
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin
val detector = FirebaseVision.getInstance() .getVisionFaceDetector(options)
Enfin, transmettez l'image à la méthode
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. Obtenir des informations sur les visages détectés
Si l'opération de reconnaissance faciale aboutit, une liste d'objetsFirebaseVisionFace
est transmise à l'écouteur de succès. Chaque objet FirebaseVisionFace
représente un visage détecté dans l'image. Pour chaque visage, vous pouvez obtenir ses coordonnées de délimitation dans l'image d'entrée, ainsi que toute autre information que vous avez configurée pour que le détecteur de visage la trouve. Exemple :
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 } }
Exemple de contours du visage
Lorsque la détection des contours du visage est activée, vous obtenez une liste de points pour chaque caractéristique du visage détectée. Ces points représentent la forme de l'élément géographique. Pour en savoir plus sur la représentation des contours, consultez la présentation des concepts de détection des visages.
L'image suivante montre comment ces points sont mappés sur un visage (cliquez sur l'image pour l'agrandir):
Détection de visages en temps réel
Si vous souhaitez utiliser la détection de visage dans une application en temps réel, suivez ces consignes pour obtenir les meilleurs fréquences d'images:
Configurez le détecteur de visage pour qu'il utilise la détection des contours du visage ou la classification et la détection des points de repère, mais pas les deux:
Détection de contours
Détection de points de repère
Classification
Détection et classification de points de repère
Détection et détection de points de repère
Détection et classification de contours
Détection, détection de points de repère et classification de contoursActivez le mode
FAST
(activé par défaut).Envisagez de prendre des images en basse résolution. Toutefois, gardez à l'esprit les exigences concernant les dimensions des images de cette API.
- Limitez les appels au détecteur. Si un nouveau frame vidéo devient disponible pendant l'exécution du détecteur, supprimez-le.
- Si vous utilisez la sortie du détecteur pour superposer des éléments graphiques à l'image d'entrée, obtenez d'abord le résultat de ML Kit, puis affichez l'image et superposez-la en une seule étape. Vous ne procédez ainsi qu'une seule fois pour chaque frame d'entrée.
-
Si vous utilisez l'API Camera2, capturez des images au format
ImageFormat.YUV_420_888
.Si vous utilisez l'ancienne API Camera, capturez des images au format
ImageFormat.NV21
.