Une introduction à la détection de visage sur Android
() translation by (you can also view the original English article)
Introduite avec les bibliothèques Vision dans Play Services 8.1, la détection de visage vous permet, en tant que développeur, d'analyser facilement une vidéo ou une image afin de localiser des visages humains. Une fois que vous avez une liste de visages détectés sur une image, vous pouvez collecter des informations sur chaque visage, telles que l'orientation, la probabilité de sourire, si une personne a les yeux ouverts ou fermés et des repères spécifiques sur son visage.
Ces informations peuvent être utiles pour plusieurs applications, telles qu'une application appareil photo qui prend automatiquement une photo lorsque tout le monde dans le cadre sourit les yeux ouverts ou pour augmenter les images avec des effets stupides, tels que des cornes de licorne. Il est important de noter que la détection de visage n'est pas une reconnaissance faciale. Bien que des informations puissent être collectées sur un visage, ces informations ne sont pas utilisées par la bibliothèque Vision pour déterminer si deux visages proviennent de la même personne.
Ce tutoriel utilisera une image fixe pour exécuter l'API de détection de visage et collecter des informations sur les personnes figurant sur la photo, tout en illustrant ces informations avec des graphiques superposés. Tout le code de ce tutoriel est disponible sur GitHub.



1. Configuration du projet
Pour ajouter la bibliothèque Vision à votre projet, vous devez importer Play Services 8.1 ou une version ultérieure dans votre projet. Ce didacticiel importe uniquement la bibliothèque Play Services Vision. Ouvrez le fichier build.gradle de votre projet et ajoutez la ligne de compilation suivante au nœud des dépendances
.
1 |
compile 'com.google.android.gms:play-services-vision:8.1.0' |
Une fois que vous avez inclus Play Services dans votre projet, vous pouvez fermer le fichier build.gradle de votre projet et ouvrir AndroidManifest.xml. Vous devez ajouter une meta-data
définissant la dépendance de face sous le nœud application
de votre manifeste. Cela permet à la bibliothèque Vision de savoir que vous prévoyez de détecter les visages dans votre application.
1 |
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="face"/> |
Une fois que vous avez fini de configurer AndroidManifest.xml, vous pouvez y aller et le fermer. Ensuite, vous devez créer une nouvelle classe nommée FaceOverlayView.java. Cette classe étend View
et contient la logique permettant de détecter les visages dans le projet, d'afficher le bitmap analysé et de dessiner au-dessus de l'image pour illustrer des points.
Pour l'instant, commencez par ajouter les variables membres en haut de la classe et en définissant les constructeurs. L'objet Bitmap
sera utilisé pour stocker le bitmap qui sera analysé et les objets SparseArray
of Face
stockeront chaque visage trouvé dans le bitmap.
1 |
public class FaceOverlayView extends View { |
2 |
|
3 |
private Bitmap mBitmap; |
4 |
private SparseArray<Face> mFaces; |
5 |
|
6 |
public FaceOverlayView(Context context) { |
7 |
this(context, null); |
8 |
}
|
9 |
|
10 |
public FaceOverlayView(Context context, AttributeSet attrs) { |
11 |
this(context, attrs, 0); |
12 |
}
|
13 |
|
14 |
public FaceOverlayView(Context context, AttributeSet attrs, int defStyleAttr) { |
15 |
super(context, attrs, defStyleAttr); |
16 |
}
|
17 |
}
|
Ensuite, ajoutez une nouvelle méthode à l'intérieur de FaceOverlayView
appelée setBitmap (Bitmap bitmap)
. Pour l'instant, cela va simplement sauvegarder le bitmap qui lui est passé, mais vous utiliserez plus tard cette méthode pour analyser l'image.
1 |
public void setBitmap( Bitmap bitmap ) { |
2 |
mBitmap = bitmap; |
3 |
}
|
Ensuite, vous avez besoin d'un bitmap. J'en ai inclus un dans l'exemple de projet sur GitHub, mais vous pouvez utiliser l'image de votre choix pour jouer avec la détection de visage et voir ce qui fonctionne et ce qui ne fonctionne pas. Lorsque vous avez sélectionné une image, placez-la dans le répertoire res / raw. Ce tutoriel supposera que l'image s'appelle face.jpg.
Après avoir placé votre image dans le répertoire res / raw, ouvrez res / layout / activity_main.xml. Cette présentation contient une référence à FaceOverlayView
afin qu’elle soit affichée dans MainActivity
.
1 |
<?xml version="1.0" encoding="utf-8"?>
|
2 |
<com.tutsplus.facedetection.FaceOverlayView
|
3 |
xmlns:android="https://schemas.android.com/apk/res/android" |
4 |
android:id="@+id/face_overlay" |
5 |
android:layout_width="match_parent" |
6 |
android:layout_height="match_parent" /> |
La disposition étant définie, ouvrez MainActivity
et configurez FaceOverlayView
à partir de onCreate ()
. Pour ce faire, vous obtenez une référence à la vue, lisez le fichier image face.jpg du répertoire raw en tant que flux d'entrée et convertissez-le en bitmap. Une fois que vous avez la bitmap, vous pouvez appeler setBitmap
sur FaceOverlayView
pour transmettre l'image à votre vue personnalisée.
1 |
public class MainActivity extends AppCompatActivity { |
2 |
|
3 |
private FaceOverlayView mFaceOverlayView; |
4 |
|
5 |
@Override
|
6 |
protected void onCreate(Bundle savedInstanceState) { |
7 |
super.onCreate(savedInstanceState); |
8 |
setContentView(R.layout.activity_main); |
9 |
mFaceOverlayView = (FaceOverlayView) findViewById( R.id.face_overlay ); |
10 |
|
11 |
InputStream stream = getResources().openRawResource( R.raw.face ); |
12 |
Bitmap bitmap = BitmapFactory.decodeStream(stream); |
13 |
|
14 |
mFaceOverlayView.setBitmap(bitmap); |
15 |
|
16 |
}
|
17 |
}
|
2. Détecter les visages
Maintenant que votre projet est configuré, il est temps de commencer à détecter les visages. Dans setBitmap (bitmap bitmap)
, vous devez créer un FaceDetector
. Cela peut être fait en utilisant un FaceDetector.Builder
, vous permettant de définir plusieurs paramètres qui affectent la rapidité avec laquelle les visages seront détectés et les autres données que FaceDetector
générera.
Les paramètres que vous choisissez dépendent de ce que vous essayez de faire dans votre application. Si vous activez la recherche de points de repère, les visages seront détectés plus lentement. Comme avec la plupart des choses dans la programmation, tout a ses compromis. Pour en savoir plus sur les options disponibles pour FaceDetector.Builder
, vous pouvez trouver la documentation officielle sur le site Web du développeur Android.
1 |
FaceDetector detector = new FaceDetector.Builder( getContext() ) |
2 |
.setTrackingEnabled(false) |
3 |
.setLandmarkType(FaceDetector.ALL_LANDMARKS) |
4 |
.setMode(FaceDetector.FAST_MODE) |
5 |
.build(); |
Vous devez également vérifier si le FaceDetector
est opérationnel. Lorsqu'un utilisateur utilise la détection de visage pour la première fois sur son appareil, Play Services doit accéder à un ensemble de petites bibliothèques natives pour traiter la demande de votre application. Bien que cela soit presque toujours fait avant que votre application ait été lancée, il est important de gérer le risque que cela se produise.
Si FaceDetector
est opérationnel, vous pouvez convertir votre bitmap en objet Frame
et le transmettre au détecteur pour recueillir des données sur les visages dans l'image. Lorsque vous avez terminé, vous devez libérer le détecteur pour éviter toute fuite de mémoire. Lorsque vous avez terminé de détecter les visages, appelez invalidate()
pour déclencher le rafraîchissement de la vue.
1 |
if (!detector.isOperational()) { |
2 |
//Handle contingency
|
3 |
} else { |
4 |
Frame frame = new Frame.Builder().setBitmap(bitmap).build(); |
5 |
mFaces = detector.detect(frame); |
6 |
detector.release(); |
7 |
}
|
8 |
invalidate(); |
Maintenant que vous avez détecté les visages dans votre image, il est temps de les utiliser. Pour cet exemple, vous allez simplement dessiner une boîte verte autour de chaque face. Puisque invalidate ()
a été appelé après la détection des visages, vous pouvez ajouter toute la logique nécessaire à onDraw (Canvas canvas)
. Cette méthode permet de s'assurer que le bitmap et les faces sont définis, puis tracez le bitmap sur la toile, puis tracez un cadre autour de chaque face.
Étant donné que la taille d'affichage de différents périphériques est différente, vous garderez également une trace de la taille mise à l'échelle du bitmap afin que l'image entière soit toujours visible sur le périphérique et que toutes les superpositions soient dessinées correctement.
1 |
@Override
|
2 |
protected void onDraw(Canvas canvas) { |
3 |
super.onDraw(canvas); |
4 |
|
5 |
if ((mBitmap != null) && (mFaces != null)) { |
6 |
double scale = drawBitmap(canvas); |
7 |
drawFaceBox(canvas, scale); |
8 |
}
|
9 |
}
|
La méthode drawBitmap (Canvas canvas)
dessine votre bitmap sur le canevas et le redimensionne de manière appropriée, tout en renvoyant un multiplicateur permettant de redimensionner correctement vos autres dimensions.
1 |
private double drawBitmap( Canvas canvas ) { |
2 |
double viewWidth = canvas.getWidth(); |
3 |
double viewHeight = canvas.getHeight(); |
4 |
double imageWidth = mBitmap.getWidth(); |
5 |
double imageHeight = mBitmap.getHeight(); |
6 |
double scale = Math.min( viewWidth / imageWidth, viewHeight / imageHeight ); |
7 |
|
8 |
Rect destBounds = new Rect( 0, 0, (int) ( imageWidth * scale ), (int) ( imageHeight * scale ) ); |
9 |
canvas.drawBitmap( mBitmap, null, destBounds, null ); |
10 |
return scale; |
11 |
}
|
La méthode drawFaceBox (Canvas Canvas, double scale)
devient un peu plus intéressante. Chaque face qui a été détectée et enregistrée a une valeur de position au-dessus et à gauche de chaque face. Cette méthode prendra cette position et en tirera un rectangle vert pour englober chaque face en fonction de sa largeur et de sa hauteur.
Vous devez définir votre objet Paint
, puis parcourir chaque face
de votre SparseArray
pour trouver sa position, sa largeur et sa hauteur, puis tracer le rectangle sur le fond en utilisant ces informations.
1 |
private void drawFaceBox(Canvas canvas, double scale) { |
2 |
//paint should be defined as a member variable rather than
|
3 |
//being created on each onDraw request, but left here for
|
4 |
//emphasis.
|
5 |
Paint paint = new Paint(); |
6 |
paint.setColor(Color.GREEN); |
7 |
paint.setStyle(Paint.Style.STROKE); |
8 |
paint.setStrokeWidth(5); |
9 |
|
10 |
float left = 0; |
11 |
float top = 0; |
12 |
float right = 0; |
13 |
float bottom = 0; |
14 |
|
15 |
for( int i = 0; i < mFaces.size(); i++ ) { |
16 |
Face face = mFaces.valueAt(i); |
17 |
|
18 |
left = (float) ( face.getPosition().x * scale ); |
19 |
top = (float) ( face.getPosition().y * scale ); |
20 |
right = (float) scale * ( face.getPosition().x + face.getWidth() ); |
21 |
bottom = (float) scale * ( face.getPosition().y + face.getHeight() ); |
22 |
|
23 |
canvas.drawRect( left, top, right, bottom, paint ); |
24 |
}
|
25 |
}
|
À ce stade, vous devriez pouvoir exécuter votre application et voir votre image avec des rectangles autour de chaque visage détecté. Il est important de noter que l'API de détection de visage est encore relativement nouvelle au moment d'écrire ces lignes et qu'elle peut ne pas détecter chaque visage. Vous pouvez jouer avec certains paramètres de l'objet FaceDetector.Builder
afin de collecter davantage de données, même si cela n'est pas garanti.



3. Comprendre les points de repère
Les points de repère sont des points d'intérêt sur un visage. L'API de détection de visage n'utilise pas de points de repère pour détecter un visage, mais plutôt un visage dans son intégralité avant de rechercher des points de repère. C’est pourquoi la découverte de points de repère est un paramètre facultatif qui peut être activé via FaceDetector.Builder
.
Vous pouvez utiliser ces points de repère comme source d'informations supplémentaire, telle que l'emplacement des yeux du sujet, afin de pouvoir réagir de manière appropriée au sein de votre application. Il y a douze points de repère qu'il est possible de trouver:
- oeil gauche et droit
- oreille gauche et droite
- oreille gauche et droite
- base du nez
- joue gauche et droite
- coin gauche et droit de la bouche
- base de la bouche
Les points de repère disponibles dépendent de l'angle du visage détecté. Par exemple, une personne faisant face sur le côté n'aura qu'un œil visible, ce qui signifie que l'autre œil ne sera pas détectable. Le tableau suivant indique quels points de repère devraient être détectables en fonction de l'angle d'Euler Y (direction gauche ou droite) du visage.
Euler Y | Visites Visibles |
---|---|
<-36 ° | œil gauche, bouche gauche, oreille gauche, base du nez, joue gauche |
-36 ° à -12 ° | bouche gauche, base du nez, bas de la bouche, œil droit, œil gauche, joue gauche, extrémité de l'oreille gauche |
-12 ° à 12 ° | œil droit, œil gauche, base du nez, joue gauche, joue droite, bouche gauche, bouche droite, bas de la bouche |
12 ° à 36 ° | bouche droite, base du nez, bas de la bouche, œil gauche, œil droit, joue droite, extrémité de l'oreille droite |
> 36 ° | oeil droit, bouche droite, oreille droite, base du nez, joue droite |
Les repères sont également incroyablement faciles à utiliser dans votre application, car vous les avez déjà inclus lors de la détection des visages. Vous devez simplement appeler getLandmarks()
sur un objet Face
pour obtenir une List
d'objets Landmark
avec lesquels vous pouvez travailler.
Dans ce didacticiel, vous dessinerez un petit cercle sur chaque repère détecté en appelant une nouvelle méthode, drawFaceLandmarks (toile Canvas, double échelle)
, à partir de onDraw (Canvas canvas)
au lieu de drawFaceBox (Canvas Canvas, double scale)
. Cette méthode prend la position de chaque point de repère, l’adapte à l’échelle du bitmap, puis affiche le cercle indicateur de point de repère.
1 |
private void drawFaceLandmarks( Canvas canvas, double scale ) { |
2 |
Paint paint = new Paint(); |
3 |
paint.setColor( Color.GREEN ); |
4 |
paint.setStyle( Paint.Style.STROKE ); |
5 |
paint.setStrokeWidth( 5 ); |
6 |
|
7 |
for( int i = 0; i < mFaces.size(); i++ ) { |
8 |
Face face = mFaces.valueAt(i); |
9 |
|
10 |
for ( Landmark landmark : face.getLandmarks() ) { |
11 |
int cx = (int) ( landmark.getPosition().x * scale ); |
12 |
int cy = (int) ( landmark.getPosition().y * scale ); |
13 |
canvas.drawCircle( cx, cy, 10, paint ); |
14 |
}
|
15 |
|
16 |
}
|
17 |
}
|
Après avoir appelé cette méthode, vous devriez voir de petits cercles verts recouvrant les visages détectés, comme illustré dans l'exemple ci-dessous.



4. Données de visage supplémentaires
Bien que la position d'un visage et ses repères soient utiles, vous pouvez également obtenir plus d'informations sur chaque visage détecté dans votre application grâce à des méthodes intégrées à partir de l'objet Face
. Les méthodes getIsSmilingProbability ()
, getIsLeftEyeOpenProbability ()
et getIsRightEyeOpenProbability ()
tentent de déterminer si les yeux sont ouverts ou si la personne détectée sourit en renvoyant un float compris entre 0.0 et 1.0. Plus la valeur 1.0 est proche, plus cette personne sourit ou a l'œil gauche ou droit ouvert.
Vous pouvez également trouver l'angle du visage sur les axes Y et Z d'une image en vérifiant ses valeurs d'Euler. La valeur de Z Euler sera toujours signalée. Toutefois, vous devez utiliser un mode précis pour détecter les visages afin de recevoir la valeur X. Vous pouvez voir un exemple d'obtention de ces valeurs dans l'extrait de code suivant.
1 |
private void logFaceData() { |
2 |
float smilingProbability; |
3 |
float leftEyeOpenProbability; |
4 |
float rightEyeOpenProbability; |
5 |
float eulerY; |
6 |
float eulerZ; |
7 |
for( int i = 0; i < mFaces.size(); i++ ) { |
8 |
Face face = mFaces.valueAt(i); |
9 |
|
10 |
smilingProbability = face.getIsSmilingProbability(); |
11 |
leftEyeOpenProbability = face.getIsLeftEyeOpenProbability(); |
12 |
rightEyeOpenProbability = face.getIsRightEyeOpenProbability(); |
13 |
eulerY = face.getEulerY(); |
14 |
eulerZ = face.getEulerZ(); |
15 |
|
16 |
Log.e( "Tuts+ Face Detection", "Smiling: " + smilingProbability ); |
17 |
Log.e( "Tuts+ Face Detection", "Left eye open: " + leftEyeOpenProbability ); |
18 |
Log.e( "Tuts+ Face Detection", "Right eye open: " + rightEyeOpenProbability ); |
19 |
Log.e( "Tuts+ Face Detection", "Euler Y: " + eulerY ); |
20 |
Log.e( "Tuts+ Face Detection", "Euler Z: " + eulerZ ); |
21 |
}
|
22 |
}
|
Conclusion
Dans ce didacticiel, vous avez découvert l'un des composants principaux de la bibliothèque Play Services Vision, la détection de visage. Vous savez maintenant comment détecter des visages dans une image fixe, comment rassembler des informations et trouver des repères importants pour chaque visage.
En utilisant ce que vous avez appris, vous devriez pouvoir ajouter des fonctionnalités intéressantes à vos propres applications pour augmenter les images fixes, suivre les visages dans un flux vidéo ou toute autre chose que vous pouvez imaginer.