Sergei Mikhailovich Prokudin-Gorskii (1863-1944) était un homme en avance sur son temps. En 1907, convaincu que la photographie couleur était la voie de l'avenir, il a obtenu une permission spéciale du Tsar (le dernier, d'ailleurs) pour traverser le vaste empire russe et de prendre des photographies couleurs de tout ce qu'il voyait. Il a photographié des gens, des édifices, des paysages, des chemins de fer, des ponts... pour ainsi produire des milliers de photos en couleur. Tout ça avant que la photographie couleur n'existe! Son idée était toute simple: enregistrer sur des lames de verre trois expositions de chaque scène en utilisant un filtre rouge, vert et bleu pour chacune d'elles. Comme il était impossible d'imprimer ces photos par la suite, il s'imaginait des projecteurs spéciaux installés dans des locaux de classe «multimédia» où les enfants russes pourraient en apprendre plus sur leur vaste pays. Hélas, ses plans ne furent jamais concrétisés: il quitta la Russie en 1918, juste après la révolution, pour ne plus jamais y revenir. Heureusement pour nous, les négatifs capturés par M. Prokudin-Gorskii survécurent et furent achetés en 1948 par la Librairie du Congrès aux États-Unis. Celle-ci a récemment (2004) numérisé ces négatifs et les a rendus accessibles sur Internet.
Sergei Mikhailovich Prokudin-Gorskii (1863-1944) was a man well ahead of his time. Convinced, as early as 1907, that color photography was the wave of the future, he won the Tsar's special permission to travel across the vast Russian Empire and take color photographs of everything he saw. And he really photographed everything: people, buildings, landscapes, railroads, bridges... thousands of color pictures! Unfortunately, color photography did not exist yet, so he used a simple idea: record three exposures of every scene onto a glass plate using a red, a green, and a blue filter. Never mind that there was no way to print color photographs until much later -- he envisioned special projectors to be installed in "multimedia" classrooms all across Russia where the children would be able to learn about their vast country. Alas, his plans never materialized: he left Russia in 1918, right after the revolution, never to return again. Luckily, his RGB glass plate negatives, capturing the last years of the Russian Empire, survived and were purchased in 1948 by the Library of Congress. The LoC has recently digitized the negatives and made them available online.
Le but de ce travail est de générer automatiquement une image couleur à partir des plaques de verre numérisées de la collection Prokudin-Gorskii, et ce, avec le minimum d'artifacts visuels possible. Pour ce faire, il vous faudra extraire les trois canaux de couleurs, les chevaucher l'un «par-dessus» l'autre, et les aligner pour que leur combinaison forme une image couleur en RGB. Dans ce TP, nous ferons l'hypothèse qu'un simple modèle de translation (en x,y) est suffisant pour aligner les images correctement. Par contre, puisque les plaques de verre numérisées ont une très grande résolution, votre procédure d'alignement devra être rapide et efficace.
The goal of this assignment is to take the digitized Prokudin-Gorskii glass plate images and, using image processing techniques, automatically produce a color image with as few visual artifacts as possible. To do so, you will need to extract the three color channel images, place them "on top" of each other, and align them so that they form a single RGB color image. We will assume that a simple translation model $(x,y)$ is sufficient for proper alignment. However, the full-size glass plate images are very large, so your alignment procedure will need to be fast and efficient.
Votre programme devra prendre une image de la plaque de verre en entrée et devra retourner l'image couleur correspondante en sortie.
Votre programme devra:
Cette ébauche de code Python effectue l'étape 1 pour vous. Vous pouvez vous en servir comme point de départ.
Tout d'abord, vous devez implémenter une approche simple qui consiste à essayer toutes les combinaisons de déplacements en $x$ et $y$, et de choisir le meilleur. Pour ce faire, définissez tout d'abord une plage de déplacements possibles (par exemple $[-15, 15]$ pixels et $x$ et en $y$). Pour chaque déplacement:
Après avoir calculé le score pour tous les déplacements, conservez celui qui a le score minimal.
Il existe plusieurs métriques de comparaison permettant d'évaluer si les images sont bien alignées. La plus simple est la norme L2, ou «somme des différences au carré» (SDC). Comme son nom l'indique, cette mesure se calcule de cette façon entre deux images $\mathbf{I}_1$ et $\mathbf{I}_2$: $$ \text{SDC} = \sum_i \sum_j \left( \mathbf{I}_1(i,j) - \mathbf{I}_2(i,j) \right)^2 $$ Vous êtes encouragés à expérimenter avec des mesures plus ingénieuses! Par exemple, vous pouvez expérimenter avec la corrélation croisée normalisée (voir ce code Python), ou encore effectuer la SDC sur une version modifiée de l'image (détection d'arêtes, par exemple).
Créez un fichier nommé main_echellesimple.py
qui exécute cette partie du TP.
Afin que vous puissiez valider si vous êtes sur la bonne voie, nous vous fournissons deux exemples de ce que vous devriez obtenir. Les translations obtenues par votre code devraient être très près de celles-ci:
![]() |
![]() |
Canal G: $\Delta x = 1, \Delta y = 4$ Canal R: $\Delta x = -1, \Delta y = 9$ |
Canal G: $\Delta x = 3, \Delta y = 2$ Canal R: $\Delta x = 5, \Delta y = 5$ |
La recherche exhaustive de l'étape précédente devient rapidement trop lourde si le déplacement de pixels est trop grand. En effet, un plage de déplacements de $[-15, 15]$ (qui devrait fonctionner pour des images à faible résolution) nécessite l'évaluation de $31^2$ possibilités. Cependant, une image à plus haute résolution (par exemple, 10 fois plus grande) nécessiterait une plage de recherche de $[-150, 150]$, ce qui équivaudrait à $301^2$ possibilités!
Dans ce cas, vous devrez implémenter une procédure de recherche plus rapide basée sur une pyramide d'images. Une pyramide d'image représente l'image à plusieurs échelles. Généralement, on obtient une pyramide en réduisant la taille de l'image d'un facteur de 2 à chaque étape. On obtient donc une série d'images, qui représentent l'image originale à l'échelle 1, 1/2, 1/4, etc. L'alignement d'images se fait de manière séquentielle à partir de l'échelle la plus petite (1/4 par exemple) jusqu'à la plus grande (1) en mettant à jour l'estimation de translation au fur et à mesure. Cela est très facile à implémenter en ajoutant des appels récursifs à votre implémentation initiale à une seule échelle.
Créez un fichier nommé main_echellesmultiples.py
qui exécute cette partie du TP.
Testez vos algorithmes sur vos propres photos! Pour ce faire, il n'est pas nécessaire d'avoir des filtres couleur (bien que si vous en avez, n'hésitez pas à expérimenter!). Il suffit de prendre trois photos l'une après l'autre, et d'extraire ensuite le canal «R» de la première, «G» de la deuxième, et «B» de la troisième. Vous pourrez de cette façon simuler ce que M. Prokudin-Gorskii a fait il y a plus de 100 ans avec la technologie d'aujourd'hui. Est-ce que l'alignement est aussi bon quand vous prenez trois photos avec votre appareil? Qu'arrive-t-il s'il y a des éléments dans la scène qui bougent, ou si la caméra s'est légèrement déplacée entre les photos? Expérimentez avec diverses scènes et commentez sur vos résultats.
Créez un fichier nommé main_jesuisgorskii.py
qui exécute cette partie du TP.
skimage.feature.register_translation
ou encore skimage.registration.phase_cross_correlation
calculent déjà la translation entre deux images, il est donc interdit de s'en servir. En cas de doute sur les fonctions à utiliser (ou pas), demandez-nous!Your program will take a glass plate image as input and produce a single color image as output.
The program should:
This Python code implements step 1 for you. You can use it as a starting point.
First, you will have to implement a simple approach, which is to try all possible displacements in $x$ and $y$, and to choose the best. To do so, first define a window of possible displacements (say $[-15,15]$ pixels in $x$ and $y$). For each displacement:
When you have computed a score for all the displacements in the window, keep the one with minimum score.
There is a number of possible metrics that one could use to score how well the images match. The simplest one is just the L2 norm also known as the Sum of Squared Differences (SSD) distance which is computed between images $\mathbf{I}_1$ and $\mathbf{I}_2$ as: $$ \text{SSD} = \sum_i \sum_j \left( \mathbf{I}_1(i,j) - \mathbf{I}_2(i,j) \right)^2 $$ You are encouraged to experiment with other, cleverer metrics! For example, you could use normalized cross-correlation (NCC), which is simply a dot product between two normalized vectors (see this Python code), or compute the SSD between image edges instead.
Create a file main_echellesimple.py
which executes this part of the homework.
To help you validate if you're on the right track, we provide below two examples of what you should obtain with your code. The translations should be quite close to the ones below:
![]() |
![]() |
G channel: $\Delta x = 1, \Delta y = 4$ R channel: $\Delta x = -1, \Delta y = 9$ |
G channel: $\Delta x = 3, \Delta y = 2$ R channel: $\Delta x = 5, \Delta y = 5$ |
Exhaustive search will become prohibitively expensive if the pixel displacement is too large (which will be the case for high-resolution glass plate scans). Indeed, a displacement window of $[-15,15]$ (appropriate for low resolution images) generates $31^2$ possibilities. However, a high resolution image (say, 10 times bigger) would require a displacement window of $[-150,150]$, which would generate $301^2$ possibilities!
In this case, you will need to implement a faster search procedure such as an image pyramid. An image pyramid represents the image at multiple scales (usually resized by a factor of 2) and the processing is done sequentially starting from the coarsest scale (smallest image) and going down the pyramid, updating your estimate as you go. It is very easy to implement by adding recursive calls to your original single-scale implementation.
Create a file main_echellesmultiples.py
which executes this part of the homework.
Test your algorithms on your own photos! You do not need to use color filters to do so. You only need to take three pictures, one after the other, and then extract the 'R' channel from the first, the 'G' channel from the second, and the 'B' channel from the third. This way, you can simulate what Mr. Prokudin-Gorskii did more than 100 years ago with today's technology. Is the alignment as good with the three pictures from your own camera? What happens if there are some elements that move in the scene, or if the camera itself moves? Experiment with different scenes and comment on your results.
Create a file main_jesuisgorskii.py
which executes this part of the homework.
skimage.feature.register_translation
and skimage.registration.phase_cross_correlation
may not be used since they more or less do everything you need. If you're wondering which functions are ok to use (or not), ask us!Essayez une (ou plusieurs!) de ces idées pour approfondir vos connaissances (et bonifier votre note):
Try one (or many!) of these ideas to increase your understanding on this subject (and your score):
imread
, imsave
, imshow
de la librarie scikit-image
, de même que roll
, dstack
de la librarie numpy
devraient l'être également.imread
, imsave
, imshow
from the scikit-image
library, as well as roll
, dstack
from the numpy
library to be helpful.Pour ce travail, vous devrez soumettre votre code et une page web simple illustrant vos résultats et contenant une courte discussion sur ceux-ci. Pour vous aider à démarrer, nous vous fournissons une ébauche de page web (facultative). L'apparence esthétique du site Web ne sera pas évaluée, mais il est important que les informations soient clairement présentées.
Votre rapport doit contenir:
For this project you must turn in both your code and a project webpage in which you will put your results and a short discussion on these. To help you get started, here's a webpage template (optional). The aesthetic appearance of the website will not be evaluated, but it is important to clearly present the information.
Your report must contain:
Pour la remise de votre travail, créez un fichier tp1.zip
qui contient :
tp1/web
.
tp1/web/index.html
. De plus, assurez-vous qu'il n'y ait aucun caractère spécial (accent, ponctuation, espace, etc.) dans les noms de vos fichiers, images, etc.tp1/code
. N'incluez pas les images que vous avez utilisées pour produire vos résultats dans ce dossier dans le but de ne pas alourdir le fichier.
Finalement, veuillez téléverser votre fichier tp1.zip
sur le portail des cours avant la date limite. La politique des retards mentionnée dans le plan de cours sera appliquée. Pour toutes questions concernant la procédure de remise ou le travail lui-même, posez vos questions sur PAX!
For this homework, you must create a tp1.zip
file. In this file you'll put:
tp1/web
.
tp1/web/index.html
. Make sure none of the files have special characters (e.g. accents, punctuation, spaces, etc.) in their filenames. tp1/code
. Do not include the images you have used to generate your results inside this folder, as this will likely generate huge files.
Finally, you should upload this file (tp1.zip
) on the "portail des cours" before the deadline. The late submission policy described in the course plan will be applied. For any question regarding the submission process or the project as such, ask your questions on PAX!
tp1.zip
file size does not exceed 50MB. Convert TIF files to JPG to reduce file size. Do NOT include any TIF files in your zip file.Merci à Alyosha Efros d'avoir créé le TP original ayant servi d'inspiration pour celui-ci!
Many thanks to Alyosha Efros for the original version of this assignment!