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 déjà fait, Ajoutez Firebase à votre projet Android.
- Ajouter les dépendances des bibliothèques Android ML Kit à votre module
Fichier Gradle (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 Fichier
AndroidManifest.xml
: Si vous n'activez pas les téléchargements de modèles au moment de l'installation, le modèle sera téléchargée la première fois que vous exécutez le détecteur. Demandes que vous envoyez avant le terminé ne produira 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 pour 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 doivent être d'au moins 100 x 100 pixels. Si vous souhaitez détecter les contours des visages, ML Kit nécessite une résolution plus élevée: chaque visage elle doit faire au moins 200 x 200 pixels.
Si vous détectez des visages dans une application en temps réel, vous pouvez également pour prendre en compte les dimensions globales des images d'entrée. Les images plus petites peuvent être sont traitées plus rapidement. Pour réduire la latence, capturez des images à des résolutions inférieures. (en tenant compte des critères de précision ci-dessus) et assurez-vous que le visage du sujet occupe le plus d'espace possible dans l'image. Voir aussi Conseils pour améliorer les performances en temps réel
Une mise au point médiocre peut nuire à la précision. Si vous n'obtenez pas de résultats acceptables, essayez de demander à l'utilisateur de reprendre l'image.
L'orientation d'un visage par rapport à l'appareil photo peut aussi avoir une incidence sur le comportement détecte les caractéristiques détectées par ML Kit. Voir Détection de visages Concepts.
1. Configurer le détecteur de visages
Avant d'appliquer la détection des visages à une image, vous pouvez modifier les paramètres paramètres par défaut du détecteur de visages, définissez ces paramètres à l'aide d'une 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
S'il faut essayer d'identifier les « points de repère » du visage : yeux, oreilles, nez, les joues, la bouche, etc. |
Détecter les contours |
NO_CONTOURS (par défaut)
| ALL_CONTOURS
Indique s'il faut détecter les contours des traits du visage. 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 du visage |
false (par défaut) | true
Permet d'attribuer ou non aux visages un ID qui peut être utilisé pour suivre visages sur les images. Notez que lorsque la détection des contours est activée, un seul visage est détecté, le suivi des visages ne produit donc pas de résultats utiles. Pour cette et pour améliorer la vitesse de détection, n'activez pas les deux et le suivi des visages. |
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+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. Lancer le détecteur de visages
Pour détecter les visages sur une image, créez un objetFirebaseVisionImage
à partir d'un Bitmap
, d'un media.Image
, d'un ByteBuffer
, d'un tableau d'octets ou d'un fichier sur
l'appareil. Ensuite, transmettez l'objet FirebaseVisionImage
à la
La méthode detectInImage
de FirebaseVisionFaceDetector
.
Pour la reconnaissance faciale, utilisez une image dont les dimensions sont d'au moins 480 x 360 pixels Pour reconnaître des visages en temps réel, à cette résolution minimale peut aider à 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 les la bibliothèque CameraX, les
OnImageCapturedListener
et Les classesImageAnalysis.Analyzer
calculent la valeur de rotation Il vous suffit donc de convertir la rotation en une ConstantesROTATION_
avant l'appelFirebaseVisionImage.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 // ... } } }
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+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 }
Ensuite, transmettez l'objet
media.Image
et valeur de rotation surFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
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 du fichierFirebaseVisionImage.fromFilePath()
Cela est utile lorsque vous Utiliser un intentACTION_GET_CONTENT
pour inviter l'utilisateur à sélectionner une image de son application Galerie.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() }
- Pour créer un objet
FirebaseVisionImage
à partir d'unByteBuffer
ou un tableau d'octets, calculez d'abord l'image comme décrit ci-dessus pour l'entréemedia.Image
.Ensuite, créez un objet
FirebaseVisionImageMetadata
. qui contient la hauteur, la largeur, le format d'encodage des couleurs de l'image et rotation: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()
Utilisez le tampon ou le tableau ainsi que l'objet de métadonnées pour créer une Objet
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)
- Pour créer un objet
FirebaseVisionImage
à partir d'un ObjetBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
doit être à la verticale, sans effectuer de rotation supplémentaire.
-
Obtenez une instance de
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin+KTX
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+KTX
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 Les objetsFirebaseVisionFace
seront transmis à l'API
l'écouteur. Chaque objet FirebaseVisionFace
représente un visage détecté.
dans l'image. Pour chaque face, vous pouvez obtenir ses coordonnées de délimitation dans l'entrée
ainsi que toute autre information que vous avez configurée
trouver. 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+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 } }
Exemple de contours d'un 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 . Observez le visage Présentation des concepts de détection pour en savoir plus sur la façon dont les contours représentées.
L'image suivante illustre la correspondance entre ces points et une face (cliquez sur l'icône image à agrandir):
Détection des 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 visages pour utiliser la détection ou la classification du contour du visage et la détection de points de repère, mais pas les deux:
Détection de contours
Détection de points de repère
Classification
Détection et classification des points de repère
Détection de contours et de points de repère
Détection et classification de contours
Détection de contours, détection de points de repère et classificationActivez le mode
FAST
(activé par défaut).Envisagez de capturer des images à une résolution plus faible. Toutefois, gardez à l'esprit les exigences concernant les dimensions des images de cette API.
- Limiter les appels au détecteur. Si une nouvelle image vidéo devient disponible pendant l'exécution du détecteur, supprimez la trame.
- Si vous utilisez la sortie du détecteur pour superposer des images l'image d'entrée, récupérez d'abord le résultat à partir de ML Kit, puis effectuez le rendu de l'image. et les superposer en une seule étape. Cela vous permet d'afficher sur la surface d'affichage une seule fois pour chaque trame 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 les images Format
ImageFormat.NV21
.