TP3: Morphage de visages (Partie A)



Algorithme de morphage


Mon algorithme de morphage est très fonctionnel et s'adapte aux images de différentes dimensions même si j'ai préféré resté en 720x720 pixels pour la partie A du TP. On peut voir dans mon morphing de mon visage vers celui d'Étienne que j'ai eu un petit problème avec le coin gauche de mes lunettes. Ceci a été réparé dans mon algorithme après la remise de la partie A (petit problème de triangles qui ne voulait pas voir une transformation inverse si la matrice était singulière). Je n'ai simplement pas pris le temps de refaire le morphing au complet. On peut voir par contre que ce n'est vraiment pas le meilleur des morphing. Probablement ma faute avec tous ces cheveux. Un de mes résultats plus loin, celui de Putin vers Lenin, montre très bien le morphing.

Fonction pour le morphage : morphage.py


Résultat final de l'algorithme de morphage du visage de Simon à Étienne



Mon algorithme utilise la fonction demandée dans l'énoncé, soit: def morph(img1, img2, img1_pts, img2_pts, tri, warp_frac, dissolve_frac) . Les deux premiers arguments sont le nom des images à allez ouvrir, les deux autres arguments sont les fichiers de points à allez lire pour les correspondances entres les différents points des images. La variable tri est la structure de triangulation Delaunay de la librairie python scipy.spatial. J'ai eu un gros problème avec cette librairie. Ce n'est pas que la forme de triangulation était mauvaise, mais ce qui n'est pas écrit dans la documentation (terriblement mauvaise documentation de la fonction Delaunay de la part de scipy) c'est que la fonction Delaunay garde en mémoire les points associés à la triangulation initiale, pas possible de changer les origines des points. Donc ce que je faisais au début était de faire la triangulation toujours au début sur la forme moyenne des points et de simplement changer ensuite l'origine des points de la structure de triangulation vers les points demandées par warp_frac, donc:

(1 - warp_frac) * points_image_origine + (warp_frac) * points_image_destination

Mais puisque la fonction scipy.spatial.Delaunay ne permet pas ce changement des points "on the fly" je me retrouvais toujours avec la structure de triangulation moyenne, et non pas une triangulation changeant avec le changement de warp_frac à chaque itération. Je n'utilise donc que la triangulation Delaunay et ensuite je travaille avec la librairie python matplotlib.tri pour me permettre de changer les points à chaque itération. La fonction suivante me donne une structure de triangulation voulue avec les points voulues (pts_wanted sont les points qui changent à chaque itération vers lesquels on veut que les points des deux images fassent leur transformation affine):

triangle_struct = mtri.Triangulation(pts_wanted[:,0], pts_wanted[:,1], tri_moy.simplices)

J'utilise ensuite la fonction tri_finder pour déterminer dans quel triangle de ma structure de triangulation les points à transformer sont:

trifinder = triangle_struct.get_trifinder()
index = trifinder(coordinates[i, 0], coordinates[i, 1])
coord_apres = np.dot(transfo_moy_to_1[index], coord_avant)


Je pense que le travail avec la triangulation est vraiment la partie difficile du TP3, pour moi en tout cas. Parce que sinon le reste à été comme sur des roulettes. L'algorithme va donc comme suit. J'ouvre les deux images à morpher ensemble et je ressort leurs dimensions: hauteur, largeur ainsi que le type de points utilisés dans leur troisième dimension (RGB, etc.). Je travail avec un template représentant l'image morpher de fin de la même forme que les images ouvertes et je le remplie de valeurs bogus (1). J'ouvre ensuite les deux fichiers de points des images et je calcul les points voulues selon la variable warp_frac. Je transforme ma structure de triangulation Delaunay de scipy vers celle de matplotlib et j'initialise ma variable de triangle finder.

Je commence ensuite le travail de trouver toutes les transformations affine pour tous les triangles, en partant de l'image voulue (soit celle représentée par pts_wanted dans le template) vers les deux autres images. J'ai donc deux fois le nombres de triangles en transformation. Pour la transformation j'ai suivi un petit stackexchange très explicite pour ce genre de transformation affine (et comment trouver la transformation inverse). Soit la lien suivant:

https://math.stackexchange.com/questions/1092002/how-to-define-an-affine-transformation-using-2-triangles

Le point important que j'ai compris de cette explication est qu'il fallait simplement que je trouve la transformation inverse des points de mon triangle d'origine, ensuite le dot product avec mes points du triangle moyen ce qui me donne la transformation de l'image d'origine vers l'image moyenne. Mais pour avoir la transformation dans l'autre sens, soit de l'image moyenne vers l'image d'origine: simplement faire l'inverse de la transformation.



Je rajoute ensuite simplement un 1 avec le x et y de mes coordonnées pour la troisième dimension de ma transformation. Quand j'ai trouvé toutes mes transformations et que j'ai placé celles-ci dans deux structure de même forme que ma structure de triangulation (transfo_moy_to_1 et transfo_moy_to_2). Je peux ainsi facilement retrouver ma transformation associée à mon triangle quand, en itérant sur les points de l'image, je cherche les triangles associées aux points voulues. C'est justement la dernière étape de mon algorithme ou je trouve grâce à toutes les transformations les points des images d'origines vers les points de l'image template et que je donne la valeur de ces pixels selon la variable dissolve_frac :

template[int(coord_avant[1]), int(coord_avant[0])] =
image_1[int(coord_apres[1]), int(coord_apres[0])] * (1 - dissolve_frac) +
image_2[int(coord_derniere[1]), int(coord_derniere[0])] * (dissolve_frac)


Mon algorithme ce termine ainsi et retourne l'image transformée.



Résultats de l'algorithme de morphage


Bien que les deux visages ne soient vraiment pas du même type (face vers casque) le résultat est quand même très correct. C'est surtout la bouche et les yeux d'Anakin quand ils se transforment vers les yeux du casque qui donne un effet "uncanny valley".

Morphage de Anakin (aime pas le sable) vers Anakin (aime pas les rebels)



Le morphing des deux photos de mes chats fonctionne très bien. C'est juste très difficile de faire en sorte que les deux bêtes restent tranquille pour avoir le même angle de tête. Malgré tout on peut presque voir le chat tourner la tête avec le morphing.

Morphage de mon chat Asimov vers mon chat Bradbury



Je trouve ce morping très fort, de passer d'un homme qui crie vers un homme en détresse ça cogne. On voit vraiment bien la forme du visage passer d'une émotion à l'autre.

Morphage de Alex Jones vers un jeune homme sous l'effet du PTSD au vietnam



De loin le meilleur de mes morphages, c'est à se demander si les deux personnes sont la même.

Morphage de Putin vers Lenin



Les images sont assez différentes, vêtements différents, posture et prise de vue un peu différentes, mais l'effet est quand même très bon. Surtout la barbe qui passe de longue à courte mais avec un bon morphing.

Morphage de moi en fin de session à Jésus après 40 jours dans le désert






TP3: Morphage de visages (Partie B)



Calcul du visage moyen


Pour le calcul des visages moyens j'ai un peu changé mon algorithme pour ne prendre qu'une seule image en arguments et simplement morpher le visage en entrée vers la structure de triangulation. Je fais ensuite une moyenne de tous les visages moyens et sort le résultat. Les résultats sont très bons et correspondent avec les résultats d'autres étudiants dans la classe pour le visage moyen.

Fonction pour le visage moyen : visage_moyen.py


Bien que le visage moyen de la classe avec tous les points pris par la classe soit très bon, on peu remarquer un léger flou dans les détails comme les yeux, la bouche, les dents et les narines. Malgré tout on comprend très bien qu'une certaine portion de la classe porte des lunettes mais que la majorité des personnes sont male aux cheveux bruns.

Triangulation du visage moyen de la classe avec les points de la classe

Visage moyen de la classe avec les points de la classe



Avec les points de la librairie dlib on voit vraiment que le visage est moins flou pour les détails du visage, mais que les cheveux et les épaules sont par contre très flous. C'est certainement à cause des points situés beaucoup plus sur les détails du visages que sur ceux de la tête en général que les traits sont aussi nets.

Triangulation du visage moyen de la classe avec les points de dlib

Visage moyen de la classe avec les points de dlib



Pour utrecht aussi les points de la librairie dlib donne une bonne définition des détails du visage comme les yeux, le contoure de la face et la bouche.

Triangulation du visage moyen d'utrecht avec les points de dlib

Visage moyen d'utrecht avec les points de dlib



Masculinisation et féminisation du visage


Visage moyen masculin d'utrecht avec les points de dlib

Visage moyen féminin d'utrecht avec les points de dlib



On peut avoir assez bien que mon visage est un peu trop sur le côté ce qui cause une déformation importante de mes lunettes et du coté droit de mon visage pour compenser. Sinon si on regarde seulement le côté gauche de mon visage qui est le moins touché par ce problème, on remarque que le visage a beaucoup plus de sens et qu'il ressemble quand même beaucoup à celui du visage moyens masculin.

Moi

Warp 75, Dissolve 15

Warp 85, Dissolve 15

Warp 88, Dissolve 15



Bon c'est un peu autre chose pour le visage féminin. Encore le problème du côté droit du visage qui était trop de côté à la prise de la photo, mais sinon mon visage est quand même similaire au visage moyen féminin. Petit problème étant ma barbe qui est vraiment dans le chemin pour nous convaincre que mon visage est "féminin" après le morphage. Une belle femme à barbe, presque.

Moi

Warp 75, Dissolve 15

Warp 85, Dissolve 15

Warp 100, Dissolve 15