Projet final: Reconnaissance faciale et modulation d'effets

Rémi Mercier (26-04-2017)

Description du projet

Ce projet consiste à créer une application amusante à partir de mon programme de reconnaissance faciale. L'objectif est d'utiliser des fichiers entrainés pour différentes émotions (sourire, triste, etc...) et de moduler l'image de la webcam en fonction de l'expression du sujet en temps réel.

Description du programme déjà implémenté

*** Ce texte est la description de mon travail de session précédant en vision numérique auquel je vais appliquer de nouvelles fonctionnalités amusantes

Reconnaissance faciale

La première étape consiste à détecter les visages dans une image donnée. Pour ce faire, OpenCV offre un algorithme déjà entraîné pour la détection de visages, soit un détecteur Haar Cascade. Ce type de détecteur est entraîné à partir d’une série d’image positive (qui contiennent un visage) et d’images négatives (qui ne contiennent pas de visage). Chaque image est ensuite caractérisé à l’aide d’une série de classificateurs simple. Ces classificateurs simples comprennent la détection de caractéristique comme des lignes, des bordures ou des sommets. Chacun de ces classificateurs est appliqué partout sur l’image et résulte à chaque fois d’un résultat binaire, caractérisant l’image. Puisque la quantité de caractéristiques peut devenir absolument énorme, l’algorithme va chercher un nombre minimal de caractéristique décrivant les images positives comme positive et les images négatives comme négative. Cela réduit drastiquement le nombre de caractéristiques sauvegardées, mais peut introduire des faux-positifs.

Cependant, le nombre de caractéristique peut s’avérer encore beaucoup trop élevé et les appliquer sur l’ensemble d’une image prendrait un temps fou. De ce fait, l’algorithme utilise un principe de cascade. Les caractéristiques sont regroupées en plusieurs étapes. Si une section d’image est détectée comme négative à la première étape, on passe à la prochaine section et ainsi de suite. Si la section d’image passe toutes les étapes, elle est considérée comme positive. Évidemment, toutes ces techniques d’optimisations introduisent encore une fois une erreur, mais cette perte de précision est nécessaire afin de pouvoir utiliser cet algorithme sur un flux vidéo en temps réel avec un minimum de puissance de processeur.

Tracking

Une fois qu’on a détecté les visages sur une image, il faut être en mesure de les suivre d’une image à l’autre sur un flux vidéo afin de pouvoir ensuite les caractériser dans le temps (voir apprentissage et caractéristiques). Pour ce faire, il faut pouvoir caractériser de façon très simple les visages détectés dans une image afin de les reconnaître (ou pas) à l’image suivante. Les caractéristique que j’ai décidé d’utiliser pour mon application sont les plus simples possibles, soit la position et la taille. De ce fait, je compare la position des visages détecté dans l’image courante avec la position des visages détecté à l’image précédente. Si la différence de position est en dessous d’un seuil, je considère que c’est le même visage. La même chose s’applique pour la taille. Si la différence entre la taille du visage à l’image courante et celle de l’image précédente est en dessous d’un seuil, je considère que c’est le même visage. Si c’est deux critères sont véridiques, je met à jour la position du visage avec celle détectée à l’image courante. Un filtre de Kalman est appliqué sur cette mise-à-jour afin de rendre le tracking plus doux et d’éviter les transitions trop brusques. Le filtre permet également de prédire la position d’un visage si il n’est pas détecté sur l’image courante. Chaque visage a une durée de vie sans mise-à-jour maximale de 20 images, après quoi le visage est perdu. Un nouveau visage détecté est considéré comme “présent” si et seulement si il est détecté et suivi durant 5 images consécutives. Cette mesure évite les faux positifs spontanés dû au bruit dans l’image.

Chaque visage peut ensuite être caractérisé d’une image à l’autre, ce qui va permettre par exemple de détecter des tendances, comme si une personne semble s’endormir, son humeur etc...

*** La section suivante est utilisé pour l'apprentissage des émotions dans le cadre du projet

Apprentissage de caractéristiques par Local Binary Patterns Histograms (LBPH)

OpenCV offre plusieurs algorithmes d’apprentissage. Celui utilisé pour ce projet a été implémenté pour la reconnaissance faciale, soit le Local Binary Patterns Histograms (LBPH). Celui-ci se base sur l’analyse de petites région dans une image et de les caractériser en établissant la relation entre les pixels voisins, un peu comme SIFT. Ce type d’analyse est indifférent à l’illumination globale de l’image, ce qui le rend robuste aux différents types d’éclairage. Chaque petite section analysé résulte en un histogramme différent. Tous ces histogrammes vont résulter en une signature unique pour l’image.

L’apprentissage est un processus simple et complexe à la fois. Il consiste à fournir à l'algorithme un certains nombre d’échantillons positifs et négatifs. Cependant, la qualité de ces échantillons fais toute la différence dans la performance finale du système. Églalement, le nombre d’images fournie peut être un avantage ou un inconvénient. Par exemple, un grand nombre d’image de qualité permet de rendre l’algorithme plus robuste et améliorer ses performance. Cependant, il est également possible de le “noyer” dans un trop grand nombre d’image ou la caractéristique recherchée peut ne pas être bien mise en valeur dans l’ensemble des images. De ce fait, un petit échantillon d’images de qualité est de loin préférable à un grand nombre d’images de mauvaises qualités. Qu’est-ce qu’une image de mauvaise qualité? Il y a plusieurs facteurs à prendre en compte. Premièrement, l’éclairage général du sujet fais une énorme différence. De ce fait, dans un monde idéal, toutes les images n’ont pas d’éclairage directionnel, éclairant le sujet de façon non-uniforme. Puisque l’algorithme est sensible aux variations d’intensité lumineuse d’un pixel envers ses voisin, un éclairage non-uniforme va biaiser le système à l’environnement lumineux du sujet d’évaluation.

Par la suite, une fois l’apprentissage fait avec de bonnes images, on lance le détecteur LBPH avec de nouvelles photos ou un flux vidéo et le détecteur LBPH va sortir une prédiction ainsi qu’un niveau de confiance envers cette prédiction. Ce niveau de confiance permet d'interpréter les prédictions avec un modèle statistique, ce qui permet d’améliorer le taux de réussite malgré l’incertitude.

Première application: Le sourire

Comme première expérimentation, j'ai implémenté un détecteur de sourire qui servira à superposer un dessin d'un "emoji" avec 7 niveaux d'intensité de sourire que voici:

Niveau 1

Niveau 2

Niveau 3

Niveau 4

Niveau 5

Niveau 6

Niveau 7

Niveau 8

Le détecteur de sourire entrainé à cette tâche a 3 niveau, soit:

Une fois que le programme a détecté un visage et qu'il a commencé à le persister, le détecteur de sourire va attribuer statistiquement lequel parmis les 3 états précédant est le plus probable sur le visage détecté, soit le label 1,2 ou 3. Afin de créer des états intermédiaire, la prédiction est filtrée avec un moyenneur avec l'image précédante, ce qui donne donc une prédiction entre 1 et 3 avec décimales. Cette prédiction est ensuite projetée sur le nombre d'images à superposer via une règle de 3 (ici 8 images). Par exemple, si la prédiction est de 2.75, l'image choisie sera (int(8*2.75/3) = 7) soit l'image de niveau 6. Le moyenneur permet donc de faire de belles transition entre les différents états de l'utilisateur devant la caméra pour un nombre minimal d'état détectable.

Résultat

Dans le vidéo précédant, on peut constater que le filtre moyenneur introduit un petit retard entre le sourire et la mise à jour de l'image. Ce retard est tout de même négligeable et il est inévitable afin d'éviter les détections aberantes et pour créer un effet de transition "smooth". On peut remarquer dans le clip que j'essaie de faire un sourire correspondant à un état intermédaire. Ma première tentative échoue puisque je ne faisaie qu'ouvrir la bouche sans créer la réelle forme d'un sourire, ce à quoi mon détecteur est entrainé. Pour la deuxième tentative, on peut voir que le smiley se stabilise dans un état intermédiaire comme voulu.

Deuxième application: On ajoute une autre émotion (Ooooohhhhh!)

Pour cette deuxième partie, on ajoute une deuxième émotion détectable au programme. De façon indépendante, cette deuxième émotion va fonctionner exactement de la même façon que la première. La particularité ici c'est d'être en mesure de faire la distinction entre les deux émotions avant de moduler l'émotion dominante avec la série d'image. Pour ce faire, il est important que les deux entraînements pour les émotions soient fait dans les même conditions. En effet, je me sers ici de l'indice de confiance retourné par les deux détecteur. Celui avec le niveau de confiance le plus élevé sera jugé "dominant" et c'est son émotion qui sera choisie. Si les entrainements ne sont pas faits dans les mêmes conditions, la comparaison entre les deux détecteur sera biaisée en fonction de l'environnement du sujet. J'ai fonc réentrainé ma deuxième émotion avec la première pour éviter ce biais. Il est également important que les deux émotions aient des caractéristiques bien distinctes, sinon le détecteur sera tout mélangé et ne saura pas quelle émotions choisir. Le résultat est démontré dans le vidéo suivant:

Niveau 1

Niveau 2

Niveau 3

Niveau 4

Niveau 5

Niveau 6

Niveau 7

Niveau 8

Résultat

On peut remarquer une certaine instabilité dans les transitions entre les deux émotions. Celle-ci peut être atténuée en augmentant le nombre d'image de suite sur lequel le filtre moyenneur est appliqué. Cependant, comme je désire garder le maximum de "responsiveness", j'ai décidé de garder le filtre moyenneur à seulement deux images de suite. Une solution est introduit à la section suivante afin d'assurer de belles transitions entre les émotions.

Troisième application: On ajoute une dernière émotion (sniff, sniff...)


Effet de pluie
Qu'est-ce qui est plus triste qu'une image d'une scène pluvieuse en noir et blanc? RIEN! Pour notre dernière application, on ajoute une autre émotion, celle de la tristesse. Cette fois-ci, plutôt que d'ajouter un smiley face, l'image est transformée en noir et blanc et un GIF animé de pluie est ajouté par dessus. Plus le visage est triste, plus la pluie est visible et plus l'image perd ses couleurs. Afin de créer l'animation de pluie, le GIF animé est décomposé en une liste circulaire qui répète l'animation en boucle. Le même principe que pour les deux émotions précédantes est utilisé afin de moduler l'effet. Le résultat est le suivant avec un mélange des 3 émotions:

Résultat (Pour un effet maximal, mettre du volume...)

Comme on peut le constater, avec les 3 détecteurs et les effets un peu plus lourd, le framerate diminue considérablement sur mon pauvre ordinateur portable qui a peine à suivre en temps réel. On note que la transition entre les effets des différentes émotions repasse toujours par le point neutre, ce qui évite de passer d'un extrème à l'autre comme c'état le cas à la section précédante. En effet, quand le détecteur change d'émotion, il va d'abord soustraire à l'intensité de l'émotion courante, repassant par le point neutre, avant d'augmenter l'intensité de la nouvelle émotion. Lors du vidéo, quand je commence à faire ma grimace triste, on remarque que le détecteur hésite un peu puisque je ne reproduisais pas bien le visage que j'avais entrainé pour l'émotion de tristesse maximale. Vers 0:28, je me souviens que je devais plisser un peu les yeux et on peut alors voir alors l'effet maximal de ma dépression.

Conclusion, discussion et améliorations possibles

En conclusion, j'ai démontré une application possible de mon détecteur de visage afin de moduler un effet en fonction de l'émotion détecté. Le détecteur est capable de faire adéquatement la distinction entre 3 émotions et de faire la transition entre les différents effets, soit le smiley content, le smiley surpris et la pluie.

Cependant, cette application a de nombreuses limitations. En effet, la base du détecteur de visage elle-même est limité par l'orientation du visage. Pour être détecté, le visage doit être orienté correctement, ce qui limite la robustesse de la détection dans un flux vidéo en temps réel. Également, les barbes fortes, les lunettes étranges et les coupes de cheveux emo empêchent le détecteur de voir les visages correctement. Aussi, la phase d'apprentissage des émotions est biaisé à l'environnement dans lequel l'apprentissage s'est fait. Par exemple, si le sourire est appris seulement sur une personne et que le "oh" est appris seulement sur une autre, le détecteur sera biaisé en fonction de la personne et non de l'émotion. Il faut donc s'assurer que l'apprentissage de chaque émotions ait exactement les même conditions pour le nombre limité d'images fournises. Évidemment, pour un plus large échantillon d'images variées, ce biais s'efface progressivement.

Mes améliorations suggérées pour mon projet sont donc les suivantes: Fin!