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.
*** 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
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.
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...
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.
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.
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.
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.
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.