Avant l'avènement de la photographie couleur, Sergei Mikhailovich Prokudin-Gorskii avait pris des photos en teintes de gris avec des filtres rouge, vert et bleu. Le but du présent projet est de combiner ces trois images filtrées pour obtenir une seule image couleur. Pour ce faire, le programme aligne d'abord les trois canaux de couleur avant d'appliquer des correctifs pour retirer les contours de l'image résultante, pour tenter de retirer les artefacts et pour améliorer le contraste. Les résultats obtenus sont présentés pour plusieurs images à basse et haute résolution tirées de la collection de Prokudin-Gorskii. Finalement, le programme est appliqué sur quelques images prises avec un téléphone moderne préalablement décomposées en canaux RGB.
Outre ce rapport en format html, le fichier de remise contient le code permettant d'appliquer les algorithmes ainsi que les images résultantes après alignement (mais avant l'application des correctifs) et après l'application des correctifs. Le code est séparé en plusieurs fichiers python. En résumé, il y a trois fichiers pour l'alignement (pour une échelle, plusieurs échelles et pour les photos avec cellulaire), deux fichiers pour l'ajustement des images (un pour les images jpg et l'autre pour les tif) et un fichier contenant les fonctions utilitaires utilisées dans les autres fichiers de code. Pour utiliser le programme au complet, il suffit par exemple de placer des images basses résolutions en teinte de gris dans le dossier "jpg_img" et d'appliquer d'abord l'alignement à une échelle. L'image en couleur est alors enregistrée dans le dossier "jpg_out" et le code pour l'ajustement d'une image jpg peut être exécuté. L'image résultante avec les correctifs se retrouve au final dans le dossier "jpg_wow". Le principe est le même pour les images à haute résolution à la différence qu'il faut utiliser l'alignement à plusieurs échelles et l'ajustement pour les images tif.
Pour faire l'alignement des images à basse résolution, l'image en teinte de gris originale est d'abord séparée en trois pour obtenir les canaux B, G et R en la coupant simplement au 1/3 et 2/3 de la hauteur de l'image. L'alignement se fait ensuite en appelant la fonction "align_images". En utilisant le canal B comme référence, la fonction est appelée deux fois: la première pour aligner le canal G et la seconde pour le canal R.
À partir du canal de référence et du canal à aligner, l'idée de la fonction "align_images" (voir le fichier "__init__.py" dans le dossier "tp1_functions" pour les détails d'implémentation) est de trouver le déplacement en x et y du canal à aligner qui minimise la somme des différences au carré (SDC) des pixels avec le canal de référence. Le déplacement considéré va de -15 à +15 en x et en y. La corrélation croisée normalisée a été tentée également, mais elle n'a pas donné d'aussi bons résultats. Plutôt que de faire la SDC de l'image entière, celle-ci est seulement appliquée sur quatre fenêtres correspondant chacune à un carré de 20% de la largeur et 20% de la hauteur. De plus, ces fenêtres sont réparties différemment dans l'image. Ceci permet de diminuer les chances d'être malchanceux et de faire l'alignement sur une région peu informative de l'image (comme un plan d'eau par exemple). Étant donné que les bords des images contiennent plusieurs défauts et artefacts, les fenêtres considérées n'ont pas été placées trop proches de ceux-ci. Pour toutes les images jpg et même pour certaines images tif, cette façon de procéder n'est pas nécessaire puisqu'une seule fenêtre de 20% suffisait pour obtenir un alignement parfait. Toutefois, pour les images à haute résolution plus difficiles, l'utilisation de plusieurs fenêtres s'est avéré nécessaire. Puisque cela augmente le temps de calcul, il aurait idéalement été préférable d'avoir l'option de modifier le nombre de fenêtres considérées selon la résolution de l'image.
Par ailleurs, dans cette fonction, les alignements sont réalisés sur les images après les avoir transformé avec un filtre de Sobel. Celui-ci permet de faire ressortir les arêtes dans les images. L'alignement s'est avéré beaucoup plus facile avec cette transformation puisqu'il est logique que les arêtes contiennent plus d'information pour aligner des images qu'un fond davantage uniforme. En ce qui concerne l'explication de l'essentiel de la fonction, le meilleur alignement est trouvé en calculant la SDC moyenne sur les quatres fenêtres et ce pour tous les déplacements potentiels. À chaque itération, si l'erreur moyenne est plus faible que ce qui a été rencontré jusqu'à présent, le déplacement correspondant est enregistré. Le déplacement donnant la plus petite erreur pour toutes les combinaisons de x et y est celui qui est retourné au final.
L'algorithme a permis d'aligner toutes les images à basse résolution parfaitement. Un exemple de cet alignement est donné pour une image jpg ci-dessous. L'alignement pour les autres images sera présenté en même temps que l'ajustement de la qualité.
![]() |
Alignement de l'image 00106v (jpg) |
Avec les images à haute résolution, la même fonction d'alignement a été utilisée, mais de manière récursive avec l'approche de pyramide d'images. Par rapport à l'alignement pour les images à basse résolution, deux éléments additionnels ont été apportés. Le premier est simplement le facteur d'échelle à utiliser. Bien qu'il était suggéré d'utiliser un facteur 2 à chaque étape de la pyramide, il a plutôt été décidé d'utiliser de choisir les échelles 1/12, 1/4 et 1. Puisqu'on utilise toujours un déplacement absolu de -15 à +15, la première itération permet donc de trouver l'alignement grossier sur la plage -180 à +180. Le point ainsi trouvé correspond justement au second élément important, c'est-à-dire le décalage à utiliser comme point intial dans l'itération suivante de la pyramide. Dans la fonction "align_images", celui-ci correspond au paramètre d'entrée optionnel "shift". Il permet donc de recommencer la recherche du meilleur alignement dans la région la plus prometteuse trouvé au facteur d'échelle précédent.
L'utilisation de facteurs plus grands permet d'accélérer le processus puisque l'image redimensionnée est plus petite. Toutefois, la différence entre chaque étape ne doit pas être trop grande. Autrement, le point du vrai alignement pourrait être manqué. C'est analogue au principe de recouvrement spectral en analyse des signaux. Bref, le choix des facteurs d'échelle utilisés optimise la rapidité tout en évitant de passer à côté du véritable point d'alignement.
L'algorithme a permis d'aligner toutes les images à haute résolution parfaitement. Un exemple d'alignement obtenu sur une image tif de la collection de Gorskii est donné ci-dessous. Comme pour les images jpg, l'alignement pour les autres images sera présenté en même temps que l'ajustement de la qualité.
![]() |
Alignement de l'image 00458u (tif) |
Les fichiers "main_ajustement_jpg" et "main_ajustement_tif" permettent d'appliquer les algorithmes de correction des taches de couleur ainsi que de l'amélioration des contrastes sur les images à basse et haute résolution respectivement. Dans les deux cas, il est d'abord nécessaire de retirer les bordures des images autant que possible. Pour ce faire, trois approches ont été tentées. La première, qui correspond à la fonction "crop_with_rectangle" dans le fichier "tp1_functions", consiste à approximer le rectangle qui englobe l'image sans la bordure en utilisant la fonction "findContours" de openCV. Malgré l'application d'un filtre pour mettre en évidence les arêtes (filtre Canny) et d'un filtre gaussien pour brouiller les arêtes moins importantes (par exemple celles d'objets dans l'image), cette façon de faire ne s'est pas avérée fructueuse puisque la fonction réussit difficilement à voir le tour de l'image comme un rectangle continu. Dès qu'une ligne ou que le contour d'un objet vient toucher à la bordure, la détection de contours croit qu'il s'agit de la continuité de la bordure et le rectangle est brisé.
La seconde méthode est une approche de découpage en se fiant à l'intensité dans l'espace de la teinte (la fonction "crop_with_hue"). L'idée était d'utiliser le fait que les bordures de couleur apparaîtraient comme des lignes de forte intensité sur le filtre de la teinte. Avec ces quatre lignes, il serait donc possible de découper la bordure. Les résultats se sont avérés décents sur plusieurs images, mais décevants sur plusieurs autres. En fait, il pourrait être adapté pour être bon sur chaque image, mais cela implique d'analyser chaque image afin de déterminer le bon seuil d'intensité. Bref, l'algorithme ne généralise pas bien. Malgré tout, c'est en travaillant dessus que l'idée est venue pour l'approche qui a été retenue.
La troisième approche consiste à trouver les lignes des bordures dans les filtres R, G et B directement (voir la fonction "crop_with_color"). L'hypothèse est que la différence d'intensité entre la dernière ligne de la bordure et la première de l'image est significativement plus grande que celle entre deux lignes successives de la bordure ou deux lignes de l'image. En gros, la fonction commence par trouver les quatre lignes qui marquent une différence d'intensité significative proches du bord de l'image pour le filtre R. Puisqu'on sait que la bordure n'est pas trop loin du bord de l'image, le paramètre optionnel "border_perc" permet de limiter la recherche à un certain pourcentage des dimensions de l'image. Le processus est ensuite répété pour les filtres G et B en repartant toujours la recherche à partir de la ligne la plus loin du bord trouvée dans les étapes précédentes. Autrement dit, si la ligne supérieure trouvée pour le filtre R se situe à 10 pixels du haut de l'image, la recherche pour la ligne supérieure avec le filtre G commence au 10e pixel. Bien que cette approche ait nécessité quelques réglages de paramètres, elle s'est avérée assez performante tel que vous pourrez le constater dans la prochaine section. Le seul problème vient du fait que le seuil d'intensité doive être ajusté pour les images jpg et tif. C'est d'ailleurs la principale utilité du paramètre optionnel "difference_threshold". Cet inconvénient pourrait probablement être réduit en utilisant une différence d'intensité relative plutôt qu'absolue.
Après avoir recadré les images, le code dans les fichiers "main_ajustement_jpg" et "main_ajustement_tif" tente de retirer les taches de couleur avant d'améliorer les contrastes. Pour réaliser le premier point, un filtre médian est appliqué sur les images R, G et B séparemment. Avec un filtre de taille 2x2, les taches sont peu atténuées. Toutefois, avec un filtre plus gros, la qualité générale des images se dégrade rapidement. Personnellement, considérant que le filtre n'est pas vraiment efficace et qu'il dégrade quand même la qualité, je ne l'utiliserais pas du tout si le but est d'obtenir une belle image. Cependant, comme le but est ici de constater l'effet de différentes techniques, le filtre 2x2 a été conservé pour l'ajustement. Une technique plus avancée semble requise pour enlever les taches de couleur efficacement.
Le contraste a quant à lui été amélioré à l'aide de l'égalisation par histogramme. Après une conversion de RGB vers LAB, l'égalisation est appliquée sur l'image L. Comme l'image résultante est même trop contrastée, une moyenne pondérée est faite entre l'image avant et après ajustement. Une pondération à 50% pour chaque image s'est avérée satisfaisante. L'égalisation par histogramme a aussi été tentée sur chacune des images R, G et B avant d'être recombinées, mais cela avait le désavantage de modifier la distribution des couleurs par rapport à l'image originale. Les résultats étaient parfois plus impressionnants, mais ils étaient moins fidèles.
Passons maintenant à la présentation des résultats. La première série d'images correspond aux images à basse résolution (jpg). À gauche, ce sont les images après alignement, mais avant le recadrement et l'ajustement. À droite, ce sont les images finales obtenues avec l'algorithme complet. Lorsqu'il est jugé pertinent de le faire, une courte analyse est faite sous l'image.
![]() |
![]() |
Alignement de l'image 00106v | Ajustement de l'image 00106v. |
Sur cette première image, on constate la dégradation de la qualité liée au filtre médian. En effet, l'image apparaît plus floue, surtout dans la région de la face de l'homme. On voit également des artefacts de couleur liés à la bordure en bas à gauche et sur le côté gauche. Comme l'algorithme de recadrage regarde la différence d'intensité entre les lignes (ou colonnes), de tels artefacts informes qui n'affectent qu'une petite partie des lignes ne sont pas nécessairement détectés.
![]() |
![]() |
Alignement de l'image 00757v | Ajustement de l'image 00757v |
Même lorsque les taches de couleur ne sont pas collées à la bordure (voir en bas à gauche dans l'image précédente), elles sont peu atténuées. Ceci est dû au fait que le filtre médian n'est que de dimension 2x2.
![]() |
![]() |
Alignement de l'image 00888v | Ajustement de l'image 00888v |
![]() |
![]() |
Alignement de l'image 00889v | Ajustement de l'image 00889v |
Dans l'image 00889v, le bas de l'image a été légèrement trop coupé. Il semblerait que le barreau inférieure de la clôture ait été interprété comme une ligne de forte intensité à faire disparaître par l'algorithme de recadrage.
![]() |
![]() |
Alignement de l'image 00907v | Ajustement de l'image 00907v |
![]() |
![]() |
Alignement de l'image 00911v |
Ajustement de l'image 00911v |
![]() |
![]() |
Alignement de l'image 01031v | Ajustement de l'image 01031v |
![]() |
![]() |
Alignement de l'image 01657v | Ajustement de l'image 01657v |
L'artefact rouge dans le bas de l'image est plutôt difficile à traiter. L'algorithme de recadrage aurait pu le détecter si le seuil d'intensité pour supprimer une portion de l'image avait été diminué. Toutefois, cela aurait aussi eu comme effet d'avoir un découpage plus aggressif sur toutes les autres images.
![]() |
![]() |
Alignement de l'image 01880v | Ajustement de l'image 01880v |
De manière générale pour les images jpg, je considère que l'algorithme d'ajustement a fait un excellent travail. À l'exception d'une image, le découpage n'est jamais abusif. Je trouve qu'il fait le compromis parfait entre trop découper et ne pas retirer suffisamment d'artefacts de bordure. De plus, l'ajustement du contraste donne de bons résultats (surtout sur l'image 00907v à mon avis) sans altérer significativement les couleurs originales, ce qui n'était pas le cas de l'égalisation par histogramme sur chacun des canaux RGB. À ce point, le plus gros défaut de l'algorithme est de ne pas retirer efficacement les artefacts de couleur informes. Voyons maintenant comment il s'en tire sur les 9 images à haute résolution obligatoires de la collection de Gorskii.
![]() |
![]() |
Alignement de l'image 00029u | Ajustement de l'image 00029u |
Les premières différences pour les images à haute résolution apparaissent assez rapidement. Non seulement les taches de couleur ne sont pas atténuées, mais elles sont ici empirées (voir les deux taches jaunes à 2h). De plus, la bordure de couleur n'a pas été retirée dans le bas. C'est d'ailleurs la même chose pour la bordure jaune dans le haut de l'image suivante. Ceci est dû au fait qu'un ajustement plus aggressif de l'algorithme de recadrage avait tendance à trop découper plusieurs des autres images à haute résolution. La configuration actuelle correspond au meilleur compromis trouvé.
![]() |
![]() |
Alignement de l'image 00087u | Ajustement de l'image 00087u |
![]() |
![]() |
Alignement de l'image 00128u | Ajustement de l'image 00128u |
Dans cette image, on retrouve le même problème des artefacts de couleur informes liés à la bordure. Je considère que ce n'est pas un défaut de l'algorithme de recadrage puisque ce serait faux de dire que toute une ligne affectée par une tache de couleur devrait être retirée. La solution idéale serait de retoucher les couleurs pour diminuer les taches. Toutefois, si on exclut le filtre médian qui diminue trop la qualité de l'image à mon avis, j'ignore comment.
![]() |
![]() |
Alignement de l'image 00458u | Ajustement de l'image 00458u |
![]() |
![]() |
Alignement de l'image 00737u | Ajustement de l'image 00737u |
Si on oublie les coins de couleur, je trouve que l'algorithme d'ajustement de contraste a fait un très beau travail sur les images ci-haut et ci-bas. Les nuages sur l'image du bas sont particulièrement impressionnants.
![]() |
![]() |
Alignement de l'image 00822u | Ajustement de l'image 00822u |
![]() |
![]() |
Alignement de l'image 00892u | Ajustement de l'image 00892u |
![]() |
![]() |
Alignement de l'image 01043u | Ajustement de l'image 01043u |
Dans l'image précédente, on retrouve le problème rencontré pour le barreau de clôture, c'est-à-dire que le haut de l'image a beaucoup trop été coupé. La cause semble être la même dans la mesure où la ligne noire juste au-dessus de la porte doit avoir dépassé le seuil d'intensité à découper.
![]() |
![]() |
Alignement de l'image 01047u | Ajustement de l'image 01047u |
Somme toute, les résultats obtenus sur cette première série d'images à haute résolution sont comparables à ceux obtenus pour les images jpg. La plus grande résolution a évidemment demandé de modifier les paramètres de l'algorithme de recadrage. Bien qu'un compromis raisonnable ait été atteint, ceux-ci pourraient peut-être être mieux ajustés manuellement. Il est quand même intéressant de voir que l'algorithme a pu être adapté assez facilement à une résolution beaucoup plus grande. Voici maintenant le résultat sur les 10 images à haute résolution au choix de la collection de Gorskii.
![]() |
![]() |
Alignement de l'image 00001u | Ajustement de l'image 00001u |
Cette première image a de toute évidence donné beaucoup de misère à l'algorithme. C'est d'ailleurs difficile de comprendre pourquoi les bordures noires n'ont pas été retirées. On voit qu'il y a un certain flou dans la bordure. De plus, le noir apparaît plutôt pâle. Il est donc possible que la différence d'intensité entre deux lignes subséquentes proches de la bordure ne soit jamais assez grande pour dépasser le seuil. Un ajustement potentiel pour régler le problème serait de considérer plus de deux lignes à la fois.
![]() |
![]() |
Alignement de l'image 00002u | Ajustement de l'image 00002u |
![]() |
![]() |
Alignement de l'image 00003u | Ajustement de l'image 00003u |
On voit que le recadrement n'a pas été assez aggressif dans le bas. Dans le haut, c'est plutôt le fait que la bordure bleue était superposée en grande partie au ciel. Si elle avait plutôt été superposée à un décor d'une couleur autre que bleue, elle aurait assurément été supprimée par l'algorithme dans sa recherche sur le canal B.
![]() |
![]() |
Alignement de l'image 00004u | Ajustement de l'image 00004u |
L'image ci-haut présente un désalignement supérieur à la moyenne (regarder le chandelier et les personnes). Pourtant, certaines portions sont très bien alignées. Il semblerait donc que le chandelier était en mouvement.
![]() |
![]() |
Alignement de l'image 00005u | Ajustement de l'image 00005u |
![]() |
![]() |
Alignement de l'image 00006u | Ajustement de l'image 00006u |
![]() |
![]() |
Alignement de l'image 00007u | Ajustement de l'image 00007u |
![]() |
![]() |
Alignement de l'image 00008u | Ajustement de l'image 00008u |
Ici, il est n'est pas clair à savoir pourquoi les bordures noires à gauche et à droite n'ont pas été retirées. Elles sont très sombres et elles semblent contraster beaucoup avec les murs. L'explication la plus plausible est que bien que la différence d'intensité en RGB soit prononcée, elle ne l'était pas assez dans chacun des canaux R, G et B. Dans ce cas particulier, l'approche de découpage avec la teinte aurait assurément mieux fonctionnée.
![]() |
![]() |
Alignement de l'image 00009u |
Ajustement de l'image 00009u Artefacts de couleur tout autour. |
![]() |
![]() |
Alignement de l'image 00010u | Ajustement de l'image 00010u |
Les images à haute résolution au choix se sont avérées plus difficiles à traiter. C'est assurément dû au fait que le peaufinement de l'algorithme a été effectué sur les images obligatoires. Ainsi, l'algorithme était moins bien adapté à de nouveaux exemples (c'est comme de l'overfitting dans un réseau de neurones!). Attaquons maintenant la dernière étape: appliquer l'algorithme d'alignement à des images prises sur un téléphone cellulaire moderne. Tel que demandé, pour chaque exemple, trois images ont été prises avant d'être respectivement utilisées comme canaux R, G et B. Les images originales ont une résolution de 5312 x 2988.
![]() |
![]() |
Image originale "fire" | Alignement de l'image "fire" |
Pouvez-vous voir les différences? Petit indice, regardez les chaises du bas ainsi que les skis de plus près. On voit que l'alignement a très bien été effectué, mais que les bords de l'image sont quand même un peu flous. Pour améliorer davantage le résultat, il aurait été nécessaire d'aligner en rectifiant l'angle. Cette première image permet surtout de constater que l'algorithme fonctionne avec le cellulaire.
![]() |
![]() |
Image originale "snow" | Alignement de l'image "snow" |
Cette photo est particulièrement difficile puisqu'il y a peu de couleurs. C'est surtout du bleu et du blanc. Elle a été prise en connaissance de cause afin de tester l'algorithme. L'alignement semble presque être bon à certains endroits, mais très mauvais à d'autres. J'ai l'impression qu'une simple modification à l'algorithme pour qu'il considère plus de fenêtres lors de l'alignement suffirait à régler le problème. Il se pourrait toutefois que ce soit en raison du désalignement angulaire (il faisait très froid alors je me suis dépêché).
![]() |
![]() |
Image originale "irobot" | Alignement de l'image "irobot" |
Cette dernière image est ma préférée. J'ai simplement pris mon aspirateur robot en photo pendant qu'il faisait ses affaires. Comme on le constate, il était en déplacement vers la gauche. Puisque l'algorithme d'alignement fait la moyenne sur quatre fenêtres placées à différents endroits dans l'image, l'alignement avait très peu de chance de se faire sur le robot qui ne correspond qu'à une petite partie de l'image complète. Ceci m'apparaît comme un point positif. L'histoire aurait fort probablement été différente si l'objet en mouvement avait occupé la majorité de l'image. Dans ce cas, c'est plutôt l'arrière plan qui aurait semblé se déplacer.
Au final, l'algorithme fait un bon travail autant pour aligner que pour ajuster les images et ce autant pour les basses résolutions que pour les hautes. Son principal point faible consiste à ne pas retirer les taches de couleur liées à des artefacts informes. Ajouter une façon de gérer ce défaut ainsi que de considérer l'angle lors de l'alignement pourraient évidemment améliorer davatange les résultats. De telles améliorations sont laissées à d'autres.