Reconnaissance faciale dans une vidéo avec OpenCV

Partager cet article

Vous êtes en train de regarder un film d’espionnage. Le suspence est haletant et tout à coup un policier du film regarde une caméra de surveillance dans laquelle un cadre est dessiné autour d’un visage. Le personnage continue a se mouvoir dans l’écran et le cadre suis encore son visage … Très honnêtement, n’avez-vous eu jamais envie de pouvoir faire ça ? Evidemment il y a quelques années cela relevant de la science fiction mais aujourd’hui ce type de traitement est simple comme bonjour.

Alors, suivez le guide !

OpenCV

Tout d’abord et en ce qui concerne les prérequis, nous allons utiliser Python (j’utilise la version 3.7) ainsi que OpenCV 4.

Note: Nous avons déjà utilisé cette librairie pour de la reconnaissance faciale dans l’article sur les cartes d’identités. Aussi je ne reviendrais pas sur cette librairie Open Source tant utilisée.

Ouvrons un notebook jupyter et vérifions que ces deux éléments sont bien fonctionnels:

import cv2 as cv
print(cv.getBuildInformation())

Si OpenCv est bien installé vous devez avoir un rendu tel que celui-ci, qui précise la version d’OpenCv que vous utilisez (dans mon cas la 4.1.2):

General configuration for OpenCV 4.1.2 =====================================
  Version control:               4.1.2

  Platform:
    Timestamp:                   2019-11-21T23:50:25Z
    Host:                        Linux 4.15.0-1028-gcp x86_64
    CMake:                       3.9.0
    CMake generator:             Unix Makefiles
    CMake build tool:            /usr/bin/gmake
    Configuration:               Release
...

Utiliser sa caméra

L’utilisation du périphérique caméra est d’une extrême simplicité avec OpenCV. Une seule ligne suffit en effet à l’utiliser (pour peu qu’elle soit disponible bien évidemment). En python on utilise la méthode VideoCapture() comme suit :

webcam = cv.VideoCapture(0)

Vous remarquerez que cette méthode demande un index (ici 0) en argument. Cet index correspond à l’index du périphérique auquel vous accédez. Dans mon cas je n’ai qu’une caméra disponible donc pas d’ambiguïté.

Il faut maintenant vérifier que la caméra est bien prête à renvoyer des images. Pour celà, il faut juste tester l’objet retourné webcam :

webcam.isOpened()
True

La méthode isOpened() renvoit True (vrai) si la caméra est bien prête.

Lancer la caméra

Une caméra fonctionne comme une « mitraillette » à photos. Récupérer un flux vidéo consiste donc à récupérer des images en répétition et très rapidement donc. C’est ce que l’on appelle le « frame rate » (F.P.S.) c’est à dire le nombre d’image que l’on est capable de récupérer dans une seconde. Cette fréquence peut être différente selon le type de diffusion et qualité. A titre d’exemple à l’époque des télévisions analogiques (PAL/SECAM) on avait un taux de 25 images/sec.

Pour reprendre wikipédia :

Le nombre d’images par seconde ou images à la seconde (en abrégé, IPS ou i/s) est une unité de mesure correspondant au nombre d’images affichées en une seconde par un dispositif.

Wikipédia

Dans le code ci-dessous on va afficher dans une fenêtre le flux vidéo:

if webcam.isOpened():
    while True:
        bImgReady, imageframe = webcam.read() # get frame per frame from the webcam
        if bImgReady:
            cv.imshow('My webcam', imageframe) # show the frame
        else:
            print('No image available')
        keystroke = cv.waitKey(20) # Wait for Key press
        if (keystroke == 27):
            break # if key pressed is ESC then escape the loop

    webcam.release()
    cv.destroyAllWindows()  

Remarquez la boucle infinie (ligne 2) qui ne se termine que quand l’utilisateur appuie sur la touche ECHAP (code 27). Ensuite la méthode webcam.read() renvoie l’image envoyée par la caméra à l’instant t (un bouléen bImgReady précise si une image a bien été récupérée) en ligne 3. Il suffit ensuite de récupérer et faire un traitement sur cette image. Dans notre cas nous allons simplement récupérer les images et les afficher.

Le résultat est très simple, puisque l’on doit simplement avoir l’affichage d’une fenêtre avec ce que filme la caméra dedans:

Le flux doit bien sur être assez limpide, mais nous allons maintenant calculer le « frame rate » (FPS). Cliquons sur ECHAP pour fermer la fenêtre.

Calculons le Frame Rate (FPS)

Pour calculer ce taux, pas besoin d’afficher quoique se soit, nous allons simplement récupérer les images comme nous l’avons fait précédemment puis les décompter. Nous utiliserons la librairie time de Python:

from time import perf_counter
t1_start = perf_counter()
frame_count = 0
webcam = cv.VideoCapture(0)
NB_IMAGES = 100

if webcam.isOpened():
    while (frame_count < NB_IMAGES):
        bImgReady, imageframe = webcam.read() # get frame per frame from the webcam
        frame_count += 1
        
    t1_stop = perf_counter()
    print ("Frame per Sec.: ", NB_IMAGES / (t1_stop - t1_start))

    webcam.release()
    cv.destroyAllWindows()
Frame per Sec.:  25.694978989489766

Et voilà, nous avons un taux d’environ 25 images par secondes, ce qui comme je vous l’ai dit plus haut est tout à fait classique.

Reconnaissance faciale dans le flux vidéo

Et maintenant ajoutons une touche d’intelligence artificielle dans le traitement du flux vidéo. Bonne nouvelle, OpenCV inclut en standard un classificateur pour ce qui est de la reconnaissance de formes : c’est le classificateur en cascade de Haar. Toujours dans les bonnes nouvelles, plusieurs modèles pré-entrainés sont disponibles et surtout prêts à l’emploi. On y trouve la reconnaissance de visage, des yeux, sourire, etc.

Note: nous avons déjà utilisé ce classificateur dans l’article sur les cartes d’identités.

Créons juste une fonction qui va utiliser ce classificateur :

dirCascadeFiles = r'../opencv/haarcascades_cuda/'
# Get files from openCV : https://github.com/opencv/opencv/tree/3.4/data/haarcascades
classCascadefacial = cv.CascadeClassifier(dirCascadeFiles + "haarcascade_frontalface_default.xml")

def facialDetectionAndMark(_image, _classCascade):
    imgreturn = _image.copy()
    gray = cv.cvtColor(imgreturn, cv.COLOR_BGR2GRAY)
    faces = _classCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags = cv.CASCADE_SCALE_IMAGE
    )
    for (x, y, w, h) in faces:
        cv.rectangle(imgreturn, (x, y), (x+w, y+h), (0, 255, 0), 2)
    return imgreturn

Cette fonction utilise un classificateur (dont on doit passer le fichier modèle _classCascade en argument). Elle prend une image et va donc détecter une forme dedans (ici on sera dans un premier temps sur une reconnaissance faciale), et retourne la même image mais avec un cadre autour de la forme reconnue.

Nous allons maintenant utiliser cette fonction dans notre flux vidéo (et l’appeler donc à chaque image récupérée) :

def videoDetection(_haarclass):
    webcam = cv.VideoCapture(0)
    if webcam.isOpened():
        while True:
            bImgReady, imageframe = webcam.read() # get frame per frame from the webcam
            if bImgReady:
                face = facialDetectionAndMark(imageframe, _haarclass)
                cv.imshow('My webcam', face) # show the frame
            else:
                print('No image available')
            keystroke = cv.waitKey(20) # Wait for Key press
            if (keystroke == 27):
                break # if key pressed is ESC then escape the loop

        webcam.release()
        cv.destroyAllWindows()   
        
videoDetection(classCascadefacial)

Déplacez-vous et vous verrez la magie opérer … le cadre vert suivra votre visage. Demandez à quelqu’un de venir dans le champ et un autre cadre avec le visage de votre partenaire apparaîtra.

Autres détections

Dans le même ordre d’idée, vous pouvez détecter les yeux :

classCascadeEyes = cv.CascadeClassifier(dirCascadeFiles + "haarcascade_eye.xml")
videoDetection(classCascadeEyes)

Détecter le profil:

classCascadeSmile = cv.CascadeClassifier(dirCascadeFiles + "haarcascade_profileface.xml")
videoDetection(classCascadeSmile)

Bref, il vous suffit d’utiliser les fichiers cascades fournis par OpenCV (Cf. https://github.com/opencv/opencv/tree/master/data/haarcascades) ou par la communauté.

Conclusion

OpenCV est décidément une librairie pleine de ressources. En quelques lignes de codes il est donc possible de récupérer une flux vidéo, détecter des formes et modifier le rendu du flux vidéo en y ajoutant des cadres de couleurs !

Comme d’habitude les sources complets sont sur Github.

Partager cet article

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.