TP3: Morphage de visages

GIF-7105

Louis-Philippe Asselin

Note: Pour voir les images en taille originale, clic droit et "copy image address".

Morphage de visages Partie A

1: Algorithme de morphage

Les points de correspondances sont déterminés pour l'image source et destination. Ensuite, on détermine l'ensemble des points milieux entre les points source et destinations. Ceci nous permet de calculer une triangulation commune entre les deux ensembles de points. Pour la triangulation, la fonction scipy.spatial.Delaunay(avg_p). La triangulation calculée par cet algorithme est simplement le lien entre les points qui minimise la distance totale de toutes les arêtes. Elle est équivalente aux liens reliant les centres des régions dans un diagramme de Voronoi.

Par la suite, pour chaque triangle de la triangulation, la matrice de transformation affine est déterminée à partir des trois points destination et des trois points source. On procède à la résolution du système d'équation linéaires à l'aide de la fonction numpy.linalg.solve.

Une fois cette transformation déterminée pour les points source vers destination, on utilise la transformée inverse (matrice inverse) pour trouver le point source à partir de la destination. De cette façon, pour chaque point de l'image de destination, on détermine le point est dans quel triangle. On multiplie ensuite les coordonnées du point (ou du pixel) à la matrice de transformation associée au triangle.

On récupère le pixel de la coordonnée obtenue sur l'image source et on copie ce pixel à l'image de destination.

Afin de mieux calculer les transformations, des points sont ajoutés aux alentours des visages. Les transformations sont calculées sur des régions plus petits et donc plus près de la transformation optimale de chaque pixels du triangle. Des points sont aussi ajoutée aux côtés de l'image, pour traiter l'arrière plan.

Amélioration possible: Les résultats auraient pu être plus précis si on avait utilisé une iterpolation entre les points. Dans l'algorithme développé, les coordonnées sont simplement discrétisées afin d'obtenir la coordonnée du pixel et sa valeur. En iterpolant sur 4 pixels, on aurait eu des valeurs plus précises. Cependant, l'algorithme aurait été beaucoup plus lent.

warp_frac est simplement une pondération sur les points de l'image source (1-warp_frac pour destination). dissolve_frac est la pondération sur les valeurs des pixels (1-dissolve_frac pour destination). Deux images sont additionnées ensemble avec différentes valeurs de _frac. Pour un vidéo, la valeur des _frac passe de 0 à 1 et les images sont placées une à la suite de l'autre avec ffmpeg.

Problème majeur

Pour la remise de la Partie A, il y avait un problème majeur dans l'algorithme. Voir la partie "Bonus: Image à vidéo" à la fin de la page pour explications du problème.

Les résultats ont été recalculés avec la modification adéquate permettant d'éliminer les mauvaises transformations sur les grands déplacements. Le problème était causé par la transformation triangle lorsque le point de destination sortait de la région du triangle (mais était tout de même lié à la matrice, puisque la triangulation originale seulement était utilisée). En recalculant la triangulation pour la destination à chaque déformation de l'image, le problème est réglé.

Résultats animés:

ancienne version de l'algorithme. (Triangulation calculée une seule fois)

ancienne version de l'algorithme. (Triangulation calculée une seule fois)

nouvelle version (triangulation recalculée pour chaque image)

Discussion:

Les résultats ne sont pas parfaits. On voit avec l'animation que la triangulation est problématique. L'algorithme modifié aide à peine, malgré qu'il règle le problème pour le cas d'un visage vers les points de l'autre.

Bonus:

Coordonnées polaires

Une transformation par les coordonnnées polaires peut être effectuée simplement en remplaçant les coordonnées cartésiennes vers les coordonnées polaires. (x, y) <-> (rayon, angle)

J'ai testé plusieurs systèmes de coordonnées polaires (point milieu central ou coin gauche ou coin droit) sans succès.

Il y a probablement un problème de référence, d'axe ou de signe mais après plusieurs tentatives, je n'ai pas réussi à le régler.

Transformation sans triangulation

Comme proposé, la transformation des transformations de chaque point pondéré est utilisée.

À chaque point de l'image, on calcule la distance au carré entre le point et les points de référence. Ensuite, un softmax est appliqué sur les distances**2 de chaque point de référence. Ceci donne les poids pour un point.

Les poids du point sont multipliés aux translations requises pour déplacer le point vers chaque point de référence associé au poid. Puisque la somme du résultat de softmax est 1, l'addition des translations pondérées par les poids softmax(distances**2) donne une transformation qui s'approche de la transformation optimale.

transformation_i = translations_i * softmax(distances_i **2)

Le résultat est intéressant pour les points distants. Cette transformation n'est pas très utile lorsque la densité de points est grande. On peut remarquer ceci par la bouche des visages sur les images suivantes.

Régulière pondération des translations

Morphage de visages Partie B

1: Calcul du "Visage moyen"

Comme on voit dans les triangulations, des points ont été ajoutés autour du visage et sur les côtés de l'image. Avec ces points on assure que la matrice de transformation calculée applique une transformation sur les points locaux. Idéalement, on aurait ajouté beaucoup plus de points pour avoir beaucoup plus de matrices qui seraient plus précises, pour moins de points à la fois. Par contre ceci ajoute la difficulté supplémentaire de bien associer chaque point au point de l'autre image.

Selon les photos et les points de mes camarades

Points Forme moyenne Visage moyen
manuels
dlib

On remarque que le visage moyen obtenu par dlib est plus clair, moins diffus puisque les points obtenus par dlib sont plus précis. L'image moyenne l'est donc aussi.

Selon les photos et les points de la base de données d'Utrecht (points obtenus avec dlib)

Forme moyenne Visage moyen
hommes et femmes
hommes et femmes souriants
hommes et femmes non souriants
hommes non souriants
femmes non souriantes
hommes souriants
femmes souriantes

La femme souriante moyenne est plus bruitée que les autres. C'est normal puisque nous avons moins d'images de femmes souriantes que pour les autres groupes. Les résultats sont bons et sont différents, malgré les formes moyennes similaires.

Bonus

La plupart des combinaisons d'hommes et de femmes souriants ou non souriants sont dans le tableau précédent.

2: Masculinisation et féminisation de votre visage

De façon similaire à la partie précédente, la forme du visage masculin moyen est calculée. Ensuite, mon visage est déformé vers les points du fisage masculin moyen (même algorithme que précédemment).

Visage masculin et féminin moyen sont affichés dans le tableau précédent.

J'ai choisi d'utiliser le visage souriant moyen, puisque mon visage est souriant sur ma photo.

Pour le visage moité masculin et moitié masculin moyen, chaque visage est déformé vers le point moyen entre mes points et les points du masculin moyen. Ensuite, l'image déformé au point milieu du masculin moyen et mon visage déformé au point milieu sont additionnés et le résultat est divisé par deux. Le résultat est "masculinisé moitié" (dissolve_frac=0.5, warp_frac=0.5) :

M Masculin/Féminin souriant moyen Masculinisé/Féminisé Masculinisé/Féminisé moitié

Les lunettes sont trop déformées, il aurait été mieux de prendre la photo sans les lunettes. Les données Utrech n'ont pas de lunettes. Mes yeux sont aussi plus petits que l'humain moyen de Utrech.

Aussi, la photo de mon visage est déformée à gauche, ce qui n'aide pas pour le résultat final. L'algorithme ne compense pas pour la déformation du visage.

Bonus 1

PCA sur Utrecht

Chaque image est d'abord transformée vers la forme moyenne de la base de données. L'algorithme PCA est entraîné sur l'ensemble des images. Le résultat est utilisé pour transformer les image de la base de données vers le domaine des composantes principales.

Ensuite, pour générer des nouveaux visages, les min et max de chacune des composantes sont obtenues (sur les images transformées). À partir des chaques min et max, on échantillone de façon uniforme. On obtient une nouvelle image, encodée dans le domaine des composantes. La transformée inverse du PCA calculé est appliqué sur cette image.

Pour l'affichage, l'image est coupée entre 0 et 1 avec la fonction numpy.clip(img,0,1) (équivalent à couper de 0 à 255). Sans cette opération finale, certaines composantes dominent les valeurs visibles (similaire aux images des eigenfaces).

On peut remarquer que plus le nombre de composantes est grand, plus le visage s'éloigne du visage moyen. Puisque PCA encode avec plus de données, le décodage produit des images avec plus de détails.

Généralement, la valeur échantillonnée liée aux premières composantes sont celles qui contribuent le plus à l'image finale.

Voici les résultats pour les hommes non souriants d'Utrecht, pour différentes valeurs de nombre de composantes principales:

n images
04
08
16
32

Résultats pour les femmes non souriantes d'Utrecht, pour différentes valeurs de nombre de composantes principales:

n images
04
08
16

Voici les résultats pour les hommes non souriants d'Utrecht, sans la transformation vers la forme moyenne:

n images
04
08
16
32

Ces derniers résultats ne sont pas très réalistes puisque les visages ne sont pas enlignés. Le style est intéressant pour les images à 32 composantes!

L'encodage PCA à n_composantes est simplement n valeurs qui représentent la distance de l'image à encoder vers les images suivantes:

Ces images sont les trois premières "eigenfaces" (valeurs propres) obtenues par l'algorithme PCA à 4 composantes, pour les hommes non souriants. Ces valeurs sont celles utilisée pour encoder les images. En ordre de contribution pour l'image principale.

Image à vidéo

Avec le détecteur de features de visage dlib, les points pour chaque image d'un vidéo sont produits. Ces points sont ensuite appliqués comme pour la partie 1 à un visage différent. Les images produites sont ensuite assemblées dans un vidéo, comme lors de la Partie A.

En générant ce vidéo, j'ai initialement eu un résultat très mauvais...

Pour trouver la source du problème, une fonction de débug a été créée. Elle transfère la triangulation initiale vers des couleurs. Cette image est utilisée dans la transformation et permet de confirmer le fonctionnement de l'algorithme.

Dans ce vidéo, les lignes correspondent à la triangulation des points courants. Ceci explique leurs variations. C'est normal. Les triangles de couleurs sont produits avec la triangulation initiale. Le vidéo est une preuve que l'algorithme fonctionne bien puisque les triangles sont déformés de façon régulière.

Pour bien déterminer le pixel de destination appartient à quel triangle (et ainsi déterminer la matrice de transformation correspondante), il faut utilise la triangulation des points de destination. Le problème était que la triangulation moyenne était utilisée pour cette étape. Utiliser la triangulation moyenne est valide seulement si le point de destination est la point moyen.

À cet effet, certains vidéos pour la Partie A utilisent l'algorithme mauvais et produisent des résultats très mauvais.

Merci!