Project 1: Images of the Russian Empire - Colorizing the Prokudin-Gorskii Photo Collection

NITYA SRI ADAPALA

Background & Overview (credits: CS180 website)

Sergei Mikhailovich Prokudin-Gorskii (1863-1944) [Сергей Михайлович Прокудин-Горский, to his Russian friends] was a man well ahead of his time. Convinced, as early as 1907, that color photography was the wave of the future, he won Tzar's special permission to travel across the vast Russian Empire and take color photographs of everything he saw including the only color portrait of Leo Tolstoy. And he really photographed everything: people, buildings, landscapes, railroads, bridges... thousands of color pictures! His idea was simple: 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 on-line. 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. In order to do this, 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.

Aligning Color Channels using Sum of Squared Distances

First, I read in the image from the input file and split it into three separate color channels by splitting the image into three equal parts. I then constructed a function compute_euclidean_distance(image1, image2) which computed the sum of the squared distances between the reference image and the image to be rolled over the reference image. Next, I constructed a function: align_using_euclidean_distance(fixed_img, img, window_displacement) to align the images using the euclidean distance as a dissimilarity metric. Essentially, I search over all the possible displacements within a certain range, compute the euclidean distance between the shifted image and the reference image, find the shift that results in the smallest euclidean distance and return that shift along with the image shifted by that amount. I first start by aligning the green channel with the blue channel and then I align the red channel with the blue channel before stacking both aligned images to create the colored image with all three channels. I tested out various different values for the size of the window and found that a minimum of 15 px is required but greater values can be used at the expense of runtime with negligible improvements regarding the alignment.

Aligning cathedral using euclidean distance
Reconstructed image of a cathedral G:(1, -1) R:(7, -1)
Aligning monastery using euclidean distance
Reconstructed image of a monastery G:(-6, 0) R:(9, 1)
Aligning Tobolsk using euclidean distance
Reconstructed image of Tobolsk G:(3, 2) R:(6, 3)

Aligning Color Channels using Normalized Cross-Correlation

Just as before, I read in the image from the input file and split it into three separate color channels by splitting the image into three equal parts. I then constructed a function compute_cross_corr_norm(image1, image2) which computed the normalized cross-correlation between the reference image and the image to be rolled over the reference image. Next, I constructed a function: align_using_cross_corr_norm(fixed_img, img, window_displacement) to align the images using the normalized cross-correlation as a dissimilarity metric. Essentially, I search over all the possible displacements within a certain range, compute the normalized cross-correlation between the shifted image and the reference image, find the shift that results in the most correlation and return that shift along with the image shifted by that amount. I first start by aligning the green channel with the blue channel and then I align the red channel with the blue channel before stacking both aligned images to create the colored image with all three channels. I tested out various different values for the window displacement and found that a minimum of 15 px is required but greater values can be used at the expense of runtime with negligible improvements regarding the alignment.

Aligning cathedral using normalized cross-correlation
Reconstructed image of a cathedral G:(1, -1) R:(7, -1)
Aligning monastery using normalized cross-correlation
Reconstructed image of a monastery G:(-6, 0) R:(9, 1)
Aligning Tobolsk using normalized cross-correlation
Reconstructed image of Tobolsk G:(3, 2) R:(6, 3)

Euclidean Distance VS. Normalized Cross-Correlation

Both functions took a similar amount of time to run and produced similar outputs so there was no advantage in using one over the other in this case.

Cropping constant amount

In order to improve the alignment of the color channels, I decided to crop the images by cutting off 10% of the image on all four sides of the image. In order to accomplish this, I used image slicing with the user's input percentage. I created two separate functions: crop_grayscale(img, percentage) and crop_color(img, percentage) to account for the different sizes of input images, i.e. colored images have a depth of three, whereas grayscale images have a depth of two.

Euclidean Distance:

Aligning cropped cathedral using euclidean distance
Reconstructed image of a cathedral with cropping \n G:(5, 2) R:(12, 3)
Aligning cropped monastery using euclidean distance
Reconstructed image of a monastery with cropping \n G:(-3, 2) R:(3, 2)
Aligning cropped Tobolsk using euclidean distance
Reconstructed image of Tobolsk with cropping \n G:(3, 3) R:(6, 3)

Normalized Cross-Correlation:

Aligning cropped cathedral using normalized cross-correlation
Reconstructed image of a cathedral with cropping G:(5, 2) R:(12, 3)
Aligning cropped monastery using normalized cross-correlation
Reconstructed image of a monastery with cropping G:(-3, 2) R:(3, 2)
Aligning cropped Tobolsk using normalized cross-correlation
Reconstructed image of Tobolsk with cropping G:(3, 3) R:(6, 3)

Coarse-to-fine Pyramid Speedup

Using the above approaches to align color channels works fine for smaller images but when we scale up to much larger images, it becomes a very time-consuming process. To speed up the process, we introduce the concept of image pyramids which significantly decrease the number of displacements that need to be examined at higher resolutions. To implement this, I created a function: pyramid_align_cc(fixed_img, img, window_displacement, levels, scaling_factor). First, we initialize displacement_x and displacement_y to zero so that we can cumulatively add the displacements from each level of the image pyramid and return the final displacement tuple with the first value being the displacement along the x-axis and the second value being the displacement along the y-axis. Then, we use a for loop to go through the pyramid level by level starting at the lowest resolution image and ending at the highest resolution image. At each level i, we resize the fixed reference image and the image to be shifted by multiplying their dimensions by the scaling factor to the power of i. Then, I use normalized cross-correlation as our dissimilarity metric to find the optimal shift with the most correlation. I multiply the x and y values of the optimal shift by the inverse of the scaling factor to the power of i (given that we were at level i). This rescales the displacement to the original resolution so that the original image can be shifted accurately. Then, we add the rescaled optimal shift's x and y values to displacement_x and displacement_y. The last step of this iteration is to shift the original image by the rescaled optimal shift so that the image is better aligned to the fixed image for the next pyramid level. After we go through each level of the pyramid, we return the accumulated x and y displacements at the original resolution. In order to align all three color channels, I start by aligning the green channel with the blue channel and then I align the red channel with the blue channel before stacking both aligned images to create the colored image with all three channels. This method greatly sped up the process of aligning the images. I used a pyramid with 4 levels, a window displacement of 20 px and a scaling factor of 1/2. I found that cropping the images and then performing the coarse-to-fine pyramid speedup resulted in the best images.

Aligning church using image pyramid and normalized cross-correlation
Reconstructed image of a church G:(0, -6) R:(52, -6)
Aligning cropped church using image pyramid and normalized cross-correlation
Reconstructed image of a church with cropping G:(24, 4) R:(58, -4)
Aligning harvesters using image pyramid and normalized cross-correlation
Reconstructed image of harvesters G:(118, -4) R:(120, 8)
Aligning cropped harvesters using image pyramid and normalized cross-correlation
Reconstructed image of harvesters with cropping G:(60, 16) R:(124, 14)
Aligning icon using image pyramid and normalized cross-correlation
Reconstructed image of icon G:(42, 16) R:(90, 22)
Aligning cropped icon using image pyramid and normalized cross-correlation
Reconstructed image of icon with cropping G:(40, 16) R:(90, 22)
Aligning lady using image pyramid and normalized cross-correlation
Reconstructed image of lady G:(56, -6) R:(122, -16)
Aligning cropped lady using image pyramid and normalized cross-correlation
Reconstructed image of lady with cropping G:(50, 8) R:(112, 12)
Aligning melons using image pyramid and normalized cross-correlation
Reconstructed image of melons G:(82, 4) R:(176, 8)
Aligning cropped melons using image pyramid and normalized cross-correlation
Reconstructed image of melons with cropping G:(82, 10) R:(178, 12)
Aligning onion_church using image pyramid and normalized cross-correlation
Reconstructed image of onion_church G:(52, 22) R:(108, 0)
Aligning cropped onion_church using image pyramid and normalized cross-correlation
Reconstructed image of onion_church with cropping G:(52, 26) R:(108, 36)
Aligning sculpture using image pyramid and normalized cross-correlation
Reconstructed image of sculpture G:(32, -10) R:(140, -26)
Aligning cropped sculpture using image pyramid and normalized cross-correlation
Reconstructed image of sculpture with cropping G:(34, -10) R:(140, -26)
Aligning emir using image pyramid and normalized cross-correlation
Reconstructed image of emir G:(-4, 8) R:(106, 18)
Aligning cropped emir using image pyramid and normalized cross-correlation
Reconstructed image of emir with cropping G:(48, 24) R:(94, -388)
Aligning self portrait using image pyramid and normalized cross-correlation
Reconstructed image of self portrait G:(50, -2) R:(130, -4)
Aligning cropped self portrait using image pyramid and normalized cross-correlation
Reconstructed image of self portrait with cropping G:(78, 28) R:(176, 36)
Aligning three generations using image pyramid and normalized cross-correlation
Reconstructed image of three generations G:(52, 4) R:(108, 8)
Aligning cropped three generations using image pyramid and normalized cross-correlation
Reconstructed image of three generations with cropping G:(54, 14) R:(112, 10)
Aligning train using image pyramid and normalized cross-correlation
Reconstructed image of train G:(110, -8) R:(108, 0)
Aligning cropped train using image pyramid and normalized cross-correlation
Reconstructed image of train with cropping G:(42, 4) R:(88, 32)