TP2: On s'amuse en fréquences

GIF-7105: Photographie Algorithmique, Hiver 2019

Olivier Mercier

Description du projet

La manipulation des fréquences d'une image permet d'obtenir des effets particuliers comme des images qui ne présentent pas les mêmes détails de proche et de loin ou des images qui se fondent à d'autres. Pour effectuer de telles manipulations, des filtres gaussiens et laplaciens peuvent être utilisés. Dans le présent travail, ceux-ci ont été exploités pour d'abord obtenir des images accentuées et des images hybrides. Afin de mieux comprendre les principes sous-jacents, des analyses en fréquence ont été effectuées et des piles gaussienne et laplacienne ont été mises en place pour certaines images. Finalement, des mélanges d'images ont été implémentés avec des piles, des pyramides et par la manipulation des gradients.

Outre ce rapport en format html, le dossier de remise contient le code permettant d'appliquer les algorithmes ainsi que les images résultantes obtenues. Les fichiers "python" principaux pour les différentes parties sont simplement nommés "main_partie_X" où X est le chiffre correspondant à la partie. Les fichiers "align_images.py" et "crop_images.py" sont les fichiers python de support fournis pour le travail. Quelques modifications mineures leur ont été apportées, mais sans plus. Le fichier "hybrid_image" implémente la fonction pour obtenir une image hybride tandis que le fichier "stacks" contient les fonctions pour obtenir les piles gaussienne et laplacienne ainsi que les pyramides gaussienne et laplacienne. Le fichier "roi" correspond simplement à une adaptation du fichier "roi.py" disponible sur le Github du cours (dans la section 8.Melange). Pour utiliser les principaux fichiers de code source des différentes parties, il suffit de placer les images sur lesquelles on veut appliquer l'algorithme dans le dossier "photos_pX" correspondant. Il peut aussi être nécessaire d'entrer le bon nom de fichier de l'image dans le code source. Les résultats obtenus sont enregistrés dans le dossier "results" si désiré.

Partie 0: Réchauffement

Tel que spécifié, le patron des détails a d'abord été obtenu en appliquant un filtre laplacien (lui-même obtenu à partir d'un filtre gaussien) sur l'image originale. Pour obtenir l'image accentuée, il suffit simplement d'ajouter ce patron de détails à l'image originale. L'accentuation peut être ajusté grâce au facteur "alpha", qui multiplie le nombre de fois que le patron des détails est ajouté. Les deux images présentées ci-dessous ont été obtenues pour des valeurs d'alpha de 2 et de sigma (déviation standard du filtre gaussien) de 3. Dans le code source de la partie 0, ceux-ci peuvent être ajustés en temps réel à l'aide de boutons glissoires.

Image originale de Team Gurren Lagann
Lien vers la source
Image accentuée de Team Gurren Lagann

Image originale de One-Punch Man
Lien vers la source
Image accentuée de One-Punch Man

Comme je me doutais que l'accentuation allait être plus impressionnante pour des images comportant beaucoup d'arêtes et des contrastes moins graduels, j'ai tout de suite sélectionné des images de style "bande-dessinée". Pour la première image, le résultat est intéressant puisqu'il semble rajeunir l'image. En effet, l'image originale est tirée d'une série animée qui date de 2007 alors elle est un peu terne et vieillot. L'accentuation permet vraiment d'améliorer les détails et de rendre le tout plus net. La seconde image est également réussie dans la mesure où les contrastes sont plus prononcés.

Partie 1: Images hybrides

L'implémentation de l'hybridation d'images (voir le fichier "hybrid_image.py") se fait en obtenant d'abord le filtre passe-bas gaussien dont la valeur de sigma dépend de la fréquence de coupure basse selon 1/2*pi*f. Le filtre laplacien est quant à lui obtenu en soustrayant le filtre gaussien passe-haut à l'image originale. Les deux filtres sont ensuite appliqués sur leur image respective et les résultats sont additionnés pour obtenir l'hybride. La fonction permet aussi de réaliser le procédé sur une image en couleur. Dans ce cas, la procédure est appliquée sur chacun des canaux RGB séparément. Dans le fichier principal de la partie 1, les fréquences de coupure doivent être sélectionnées initialement, mais elles peuvent être ajustées avec un bouton glissoire par la suite. Les fonctions fournies pour l'alignement et le rognage sont utilisées pour obtenir de plus beaux résultats. Le code du fichier principal permet également d'afficher l'analyse fréquentielle des images originales, des images filtrées et de l'image hybride. Les résultats obtenus pour l'image obligatoire ainsi que celles au choix sont présentés ci-dessous.

Image originale de Marilyn Monroe Image hybride d'Albert Einstein et de Marilyn Monroe Image originale d'Albert Einstein

Image originale de la lune
Lien vers la source
Image hybride de la lune et d'une fleur Image originale de la fleur
Lien vers la source

Image originale de l'oeil
Lien vers la source
Image hybride d'un oeil et de la Terre Image originale de la Terre
Lien vers la source

Image originale du lion
Lien vers la source
Image hybride d'un homme et d'un lion Image originale de l'homme
Lien vers la source

Image originale d'un autre oeil
Lien vers la source
Image hybride d'une horloge et d'un oeil Image originale de l'horloge
Lien vers la source

L'obtention d'une bonne image hybride dépend non seulement des fréquences de coupure sélectionnées, mais également de la similitude entre les deux images originales. Effectivement, l'effet apparaît plus réussi lorsque les bordures des objets sont alignées, lorsque les détails sont similaires ou lorsque les régions teintées sont au même endroit. Par exemple, l'hybridation de la lune et de la fleur ne donne pas un bon résultat puisque les feuilles de la fleur présentent beaucoup plus d'arêtes que la surface lisse de la lune. De plus, les variations de teintes entre les taches sur la lune et l'ombrage des feuilles ne correspondent pas. À l'opposé, l'image hybride des visages de l'homme et du lion est réussie puisque non seulement les yeux et la forme du visage sont biens alignés, mais aussi parce que la barbe de l'homme se fond sur le museau du lion. Par ailleurs, les rides de l'homme semble donner de l'âge au lion. L'alignement et le choix judicieux des images apparaît ici déterminant pour obtenir un bon effet.

Personnellement, mon image hybride favorite est la dernière. Bien que l'homme et le lion soit globalement plus réussie, on dirait que l'oeil horloge correspond à la réflexion d'une horloge dans l'oeil de quelqu'un. De plus, il est très facile de faire disparaître l'horloge (en rapetissant l'image ou en plissant les yeux) malgré le fait que celle-ci soit très nette à taille normale. Ceci semble être dû au fait que les nombres et les écritures sur l'horloge correspondent à une plage restreinte de hautes fréquences. À ce titre, l'analyse fréquentielle de cette image hybride est présentée ci-bas.

Analyse fréquentielle de la paire d'images de l'oeil et de l'horloge.
L'image 1 correspond à l'image de l'oeil tandis que l'image 2 correspond à celle de l'horloge.

À la base, l'image de l'oeil contient beaucoup de hautes fréquences, mais de faibles intensités. La transformée de Fourier après son filtrage passe-bas correspond exactement à ce que l'on s'attend, c'est-à-dire à une forte prédominance des basses fréquences. Tel qu'on l'aperçoit dans l'image hybride, cela se traduit par une perte des détails fins de l'oeil. Même si on reconnaît toujours qu'il s'agit d'un oeil, les motifs de l'iris sont perdus. Pour la seconde image, l'analyse fréquentielle révèle que les hautes fréquences sont amplifiées alors que les basses sont coupées (comme en témoigne le point plus sombre au centre de la transformée de Fourier de l'image filtrée). L'amplitude absolue des hautes fréquences est cependant moindre qu'à l'origine. Dans l'image hybride, cela se traduit par une horloge plus pâle. Tel qu'attendu, la transformée de Fourier de l'image hybride correspond à la somme de celles des images filtrées.

Image hybride en couleur

Tel que mentionné précédemment, l'implémentation du code permet de créer des images hybrides en couleur. Toutefois, les fonctions d'alignement et de rognage fournies ne supportent pas les images en couleur. Pour contourner le problème, des images de même taille et déjà alignées devaient être sélectionnées. C'est pourquoi la pomme et l'orange de la partie 3 ont été sélectionnées pour tester l'hybridation en couleur. Voici le résultat obtenu:

Image originale de la pomme Image hybride d'une pomme et d'une orange Image originale de l'orange

Il est un peu difficile de voir l'orange, mais, en rétrospective, c'est simplement parce que les fréquences de coupure n'ont pas été ajustées assez finement. Bien qu'un artefact de contour de l'orange soit présent, le résultat, d'un point de vue des textures et des formes, est malgré tout assez bon. Le problème vient plutôt des couleurs. En effet, l'image hybride correspond davantage aux couleurs de la pomme qu'à celles de l'orange. L'utilisation de la couleur semble donc plus améliorer le filtre passe-bas. Ceci est d'ailleurs très visible lorsque l'on regarde le crayon dans la partie droite de l'image hybride. Bien qu'il corresponde à celui de l'image originale de l'orange, sa couleur est proche de celle du crayon dans l'image de la pomme.

Partie 2: Piles gaussienne et laplacienne

L'objectif de cette partie était d'implémenter des piles gaussienne et laplacienne afin d'analyser des images hybrides. Pour ce faire, des filtres gaussien et laplacien sont créés itérativement à partir de valeurs ascendantes en puissances de deux du paramètre "sigma" (voir la fonction "stacks" du fichier "stacks.py"). De cette manière, le filtre gaussien devient de plus en plus flou à chaque empilage, mais la taille de l'image est préservée. Le filtre Laplacien est quant à lui obtenu en soustrayant le filtre gaussien du même niveau à celui du précédent. Dans le même fichier de code, les pyramides gaussienne et laplacienne ont aussi été implémentées (voir la fonction "pyramids"). Dans ce cas, la valeur de sigma est fixée à 2 et c'est le facteur d'échelle qui augmente selon des puissances de deux. Pour les besoins de l'affichage, les filtres sont redimensionnés à la taille de l'image originale. Il est à noter que pour les deux méthodes, la transformation gaussienne est appliquée à chaque fois sur l'image originale. Les résultats obtenus sur l'image obligatoire ainsi que sur mon image favorite de la partie précédente sont présentés ci-dessous.

Piles gaussienne et laplacienne pour "Lincoln et Gala" de Dali.
La ligne du haut est la pile gaussienne alors que celle du bas est la pile laplacienne.

Pyramides gaussienne et laplacienne pour "Lincoln et Gala" de Dali.
La ligne du haut est la pyramide gaussienne alors que celle du bas est la pyramide laplacienne.

Piles gaussienne et laplacienne pour "L'oeil horloge".
La ligne du haut est la pile gaussienne alors que celle du bas est la pile laplacienne.

Pyramides gaussienne et laplacienne pour "L'oeil horloge".
La ligne du haut est la pyramide gaussienne alors que celle du bas est la pyramide laplacienne.

Tel qu'attendu, dans les piles et pyramides gaussiennes, on voit que la Gala et l'horloge diminue avec l'augmentation de sigma ou de l'échelle. Autrement dit, on perd les hautes fréquences dans les deux cas. Dans les piles et pyramides laplaciennes, la Gala et l'horloge ressortent graduellement pour les premières valeurs de sigma ou du facteur d'échelle, mais re-disparaissent ensuite pour des valeurs plus élevées. Ceci est dû au fait que les filtres laplaciens se diluent vers les basses fréquences puisque les filtres gaussiens desquels ils sont issus contiennent de moins en moins de hautes fréquences. Par ailleurs, une différence notable entre les piles et les pyramides est la rapidité à laquelle l'effet des filtres est appliqué. Effectivement, pour obtenir le même résultat, le paramètre de la pyramide est deux fois moins grand que celui pour la pile. Par exemple, le filtre laplacien avec sigma à 4 de la pile de l'oeil horloge correspond au filtre laplacien avec le facteur d'échelle à 1:2 dans sa pyramide. Ainsi, l'utilisation d'une pyramide plutôt que d'une pile demande moins d'étapes, donc moins de calculs et de temps. Toutefois, le seuil idéal peut être manqué plus facilement. Un autre avantage de la pyramide consiste à adoucir un peu plus les joints entre les deux images.

Partie 3: Mélange multirésolution

Le but de cette partie était d'obtenir des mélanges multirésolution d'images en s'inspirant de l'article de Burt et Adelson. L'algorithme de la page 230 a été implémenté dans la fonction "mix_images" du fichier code principal de la partie 3. En résumé, les piles laplacienne pour les deux images sont d'abord obtenues. La pile gaussienne du masque est également obtenue puisque c'est elle qui sert de poids pour la combinaison des deux images. Ensuite, pour chaque niveau des piles, la somme pondérée (par le filtre gaussien du masque) des filtres laplaciens des deux images est calculée. Celle-ci est additionnée de manière cumulative sur tous les niveaux afin d'obtenir l'image finale. Outre cette fonction, le code inclut une correction de l'intensité (fonction "adjust_image") qui ajoute à l'image résultante un biais qui correspond à la moyenne des intensités des deux images initiales. Le tout a aussi été implémenté en utilisant des pyramides plutôt que des piles et avec de la couleur.

Pour utiliser le fichier "main_partie_3.py", il faut premièrement placer les deux images initiales dans le dossier "photos_p3". Deuxièmement, au début de la section principale du code, il faut spécifier les noms des images ainsi que les préférences de sauvegarde (au besoin). Dernièrement, il faut choisir la forme du masque pour la fonction "new_ROI" (ligne 160). Cette fonction correspond à l'implémentation locale du fichier "roi.py" fourni sur le Github du cours. Elle permet de sélectionner un masque de forme irrégulière. Les résultats pour la paire d'images obligatoire ainsi que les paires d'images au choix sont présentés ci-dessous. L'analyse et des observations sont faites au fur et à mesure.

Paire d'images obligatoire

Pommange

La première constatation est que les résultats apparaissent similaires pour la pile et la pyramide. En tons de gris, ils sont plutôt réussis tandis que les couleurs n'apparaissent pas tout à fait correctement. Ceci semble davantage lié à l'ajustement des couleurs lors de la reconstruction. Il est à noter que les couleurs apparaissaient d'ailleurs mieux avec les "plt.show()".

Paire d'images au choix

Grumpy Trump
Source de l'image 1
Source de l'image 2

Cette image n'est pas très réussie pour toutes les méthodes, mais c'est principalement dû au fait que l'alignement n'est pas très bon et que le contraste entre la face du chat et celle du président Trump est trop grand. Par ailleurs, pour les images en couleur, on voit l'effet d'utiliser un biais d'ajustement d'intensité tiré des deux images initiales. Puisque le fond de l'image du chat est complètement blanc, les images résultantes ressortent beaucoup trop claires.

Sauron galaxique
Source de l'image 1
Source de l'image 2

Ici, l'image en tons de gris obtenue avec une pile est très réussie. Celle en couleur le serait également si ce n'était du fait que l'image de l'oeil de Sauron n'ait autant affecté l'ajustement des couleurs (tout tend vers le rouge). C'est par ailleurs la première fois que les résultats des pyramides sont significativement différents de ceux des piles. Ici, ce fut un échec pour les pyramides. Il semblerait que la région du masque de l'image originale 1 n'ait pas été accentuée suffisamment.

Mars attaque... Québec!
Source de l'image 1
Source de l'image 2

Ce résultat est mon favori de tous (du moins ceux pour la pile). Avec l'utilisation de la pile, l'image en tons de gris est pratiquement parfaite. Malheureusement, on détecte encore le masque autour du vaisseau. Pour celle en couleur, encore une fois, l'ajustement n'est pas parfait, mais si on ne connaissait pas les images originales, le résultat apparaîtrait assez normal. Les pyramides ont une seconde fois échouées à la tâche (à moins de considérer que le vaisseau est invisible). À l'aide de piles laplaciennes, regardons maintenant le procédé de création pour le résultat en tons de gris obtenu avec une pile.

Illustration détaillé de la création de "Mars attaque... Québec!"
La première rangée correspond à la pile laplacienne de l'image 1 dans le masque.
La seconde rangée correspond à la pile laplacienne de l'image 2 sans la région du masque.
La troisième rangée correspond à la pile laplacienne de l'image résultante en tons de gris.

Paire d'images personnelles

Requin F-18
Source de l'image 1

Ce premier cas avec une image personnelle était surtout un test pour un masque de forme très particulière. Comme je ne m'attendais pas à un résultat faramineux, je suis assez satisfait de ceux obtenus avec la pile. De la même manière que pour les images hybrides, on constate à quel point il est important de choisir des images qui correspondent bien d'un point de vue des arêtes, de l'alignement et des couleurs.

Dentition de pilote
Source de l'image 1

Si ce n'était du brouilard gris présent dans tous les résultats, ce mélange aurait obtenu la première place à mon palmarès. Il est intéressant de constater que la pyramide a ici donné des résultats similaires à la pile. Un ajustement manuel de l'intensité aurait probablement permis d'obtenir un excellent résultat, mais comme ce n'était pas le but de l'exercice, cela n'a pas été fait. Passons maintenant à la dernière expérimentation effectuée dans le cadre de ce travail.

Mélanges avec l'utilisation des gradients

Cette expérimentation consistait à appliquer les principes vus dans la démonstration sur les gradients. Trois approches différentes ont été tentées (veuillez vous référez à la section "Gradients" du fichier "main_partie_3.py" pour le code source). La première consistait à calculer les gradients de chaque image originale, à appliquer une pyramide sur ces gradients (pour chacun des canaux de couleur) et à reconstruire le mélange à partir de celle-ci. Avant d'obtenir la pyramide, une astuce importante était d'égaler les gradients de l'image 1 à l'extérieur de la région du masque à ceux de l'image 2 contenus dans le masque. Ceci permet d'obtenir un bien meilleur dégradé entre les deux images versus la mise à zéro des gradients dans la région du masque.

La seconde méthode consistait à faire la moyenne de deux images reconstruites à partir des gradients. L'une était obtenue avec les gradients de l'image 1 tandis que l'autre l'était avec les gradients de l'image 2. Ici encore, l'astuce d'égaler les gradients dans la région du masque a été utilisée. L'idée était que la moyenne des deux images permettrait d'adoucir les joints et de mieux ajuster les couleurs. La troisième et dernière méthode correspondait au même principe que la seconde à la différence qu'elle était appliquée dans les canaux LAB et non RGB. Pour toutes les méthodes, la fonction de reconstruction à partir des gradients ("reconstruction" dans le code) correspond à l'implémentation de M. Lalonde telle que fournie sur le Github du cours (sous le nom "reconstruction.py" dans la section 8.Melange). Regardons à présent les résultats obtenus pour la pommange et mon mélange favori.

Gradients sur la pommange

On constate tout d'abord que les couleurs sont mieux ajustées qu'avec l'utilisation des piles et des pyramides, à l'exception de la méthode utilisant une pyramide de gradients. Pour les méthodes par moyenne, les joints sont cependant très apparents.

Gradients sur "Mars attaque... Québec!"

Ici encore, les méthodes par moyenne donnent de beaux résultats du point de vue des couleurs, mais le joint est très laid. Puisque les mélanges issus des piles directement sur les canaux RGB permettaient d'obtenir de beaux joints, mais de moins bonnes couleurs, l'idéal serait de combiner les avantages des deux. J'ignore toutefois comment. Il est par ailleurs intéressant de constater que la pyramide de gradients donne un résultat similaire à l'utilisation d'une pyramide directement dans l'espace RGB. Dans les deux cas, le vaisseau apparaît fantomatique.

Conclusion

Au final, ce travail a permis de se familiariser avec l'utilisation de piles gaussiennes et laplaciennes pour obtenir des images hybrides et des mélanges. L'application des transformées de Fourier a aussi permis de réaliser une analyse fréquentielle. Des résultats ont été obtenus pour des images en tons de gris, en couleur, avec des masques irréguliers, avec l'utilisation de piles, de pyramides et même avec différentes méthodes exploitant les gradients. Pour les images hybrides, les résultats sont satisfaisants. Toutefois, la majorité des résultats pour les mélanges présentent des défauts dans les joints ou dans les couleurs. Le premier défaut implique que la technique utilisée n'est soit pas la mieux adaptée ou soit pas suffisamment optimisée. Le second implique que l'ajustement de l'intensité de l'image est plus compliqué pour la technique utilisée. Dans cette dernière partie, les meilleurs résultats ont globalement été atteints avec l'utilisation d'une pile sur les images directement (et non sur leurs gradients). Bref, ce travail aura été intéressant, mais très long.