Overview

Mikhailovich Prokudin-Gorsky took three images of each scene with red, green, and blue filters. This project aims to align these three images of each scene to obtain the colored image.
Since each image contains three parts, each one for each channel. First, I divided an input image into three images. Then, I cropped each image to cut the spare borders. After that, I took the green channel as the anchor and aligned the red and blue channels to the anchor. At the end, I merged aligned blue, green, and aligned red channels.
I used the following methods for aligning images:

  1. Exhaustive Alignment
  2. Pyramid Alignment

1. Exhaustive Alignment

This method finds the best amount of shift needed to align two images by searching all possible movements in the space of [-15,15]. This method is an exhaustive search, so it is slow (especially for large images like “emir.tif”).

2. Pyramid Alignment

This method recursively scales the input image to half size until the size of the image reaches a specified threshold. It uses exhaustive method on the image with the size less than the threshold. The method adjusts returned shift from the next recursion by multiplying by 2. Then, it finds the best amount of shift needed to align current image in a space of [-4,4] of adjusted shift.

Displacement score

To find the optimal shift, I used error function which computes the displacement score for two images. I used following ways to compute the score:

  1. Sum of Square Difference of the two images
  2. Cross-correlation
  3. Normalized Cross-correlation

Results of Normalized Cross-correlation are better than others. The reason is that the different channels may use different brightness values.

Results

The results of Pyramid method are better than exhaustive method because searching process of exhaustive method limited to the window size while the optimal displacement may be out of the window.

Exhaustive Alignment Pyramid Alignment
Displacement:
Blue: (-2, -5)
Red: (1, 7)
Displacement:
Blue: (-2, -5)
Red: (1, 7)
Displacement:
Blue: (-15, -15)
Red: (15, 15)
Displacement:
Blue: (-24, -52)
Red: (18, 60)
Displacement:
Blue: (-12, -15)
Red: (-2, 15)
Displacement:
Blue: (-14, -56)
Red: (-2, 64)
Displacement:
Blue: (-8, -15)
Red: (15, 15)
Displacement:
Blue: (-6, -44)
Red: (26, 46)
Displacement:
Blue: (-15, -15)
Red: (5, 15)
Displacement:
Blue: (-17, -42)
Red: (5, 52)
Displacement:
Blue: (15, 0)
Red: (-15, 15)
Displacement:
Blue: (-14, -68)
Red: (11, 76)
Displacement:
Blue: (-15, -15)
Red: (14, 15)
Displacement:
Blue: (-28, -84)
Red: (8, 100)
Displacement:
Blue: (-15, -15)
Red: (-1, 15)
Displacement:
Blue: (-17, -64)
Red: (-3, 68)
Displacement:
Blue: (-8, -15)
Red: (0, 15)
Displacement:
Blue: (-8, -56)
Red: (3, 64)
Displacement:
Blue: (-15, -15)
Red: (3, 15)
Displacement:
Blue: (-21, -60)
Red: (7, 64)
Displacement:
Blue: (15, -15)
Red: (-15, 15)
Displacement:
Blue: (36, -44)
Red: (-40, 72)
Displacement:
Blue: (1, -15)
Red: (-15, 15)
Displacement:
Blue: (1, -60)
Red: (-15, 64)
Displacement:
Blue: (-8, 15)
Red: (2, -12)
Displacement:
Blue: (-8, 22)
Red: (2, -12)
Displacement:
Blue: (12, -15)
Red: (0, 15)
Displacement:
Blue: (13, -42)
Red: (2, 60)
Displacement:
Blue: (7, -15)
Red: (-15, 15)
Displacement:
Blue: (6, -52)
Red: (-18, 50)

Bells & Whistles

For Bells & Whistles, I implemented the followings:

  1. Torch Implementation
  2. Alignment by Edges
  3. Auto Cropping

1. Torch Implementation

I implemented the method by PyTorch in the following way:
I wrote a class named ImageAlignDataset to read data. I used dataloader to get data in batch.

2. Alignment by Edges

In this method, I calculated displacement score using edges of images instead of intensity of pixels. This method leads better results.
I used two ways to detect edge:

  1. I extract edges by Robert filter.
  2. I wrote edge_detect function that uses cv2.getGaussianKernel to create a 1d kernel and dot product this kernel by its transpose to make it a 2d Gaussian filter and applied resulted kernel on image by cv2.filter2D. Then, I appliled [-1,1] (gradient y) kernel and its transpose (gradient x) kernel on the filtered image. At the end, I computed gradient.

3. Auto Cropping

I implemented automatic cropping in the following ways:

  1. I croped 20 percent of each dimension.
  2. I located the black rows and columns boarders and removed them. For finding black borders, I counted number of black pixels in each row and columns separately and considered the ones which had more than 80 percent black pixels as black boarders. At the end, I picked the intersection of resulted box of different channels.
  3. I converted input image to gray and applied a binary threshold to mask all black pixels using (cv2.threshold). Then, I found contours (non-black regions) in the masked image and picked the largest bounding box to crop the image correctly.
  4. I used crop and displacement methods for autocropping. First, I cropped 10 precent of image and then found maximum absolute value of displacements to crop more accurately.

The last auto cropping method works better.

Bells & Whistles Results

Torch Implementation Alignment by Edges Auto Cropping
Displacement:
Blue: (-2, -5)
Red: (1, 7)
Displacement:
Blue: (-2, -5)
Red: (1, 7)
Displacement:
Blue: (-2, -5)
Red: (1, 7)
Displacement:
Blue: (-24, -49)
Red: (17, 57)
Displacement:
Blue: (-23, -52)
Red: (17, 60)
Displacement:
Blue: (-24, -52)
Red: (17, 60)
Displacement:
Blue: (-14, -53)
Red: (-3, 58)
Displacement:
Blue: (-13, -56)
Red: (-4, 64)
Displacement:
Blue: (-14, -56)
Red: (-3, 62)
Displacement:
Blue: (-5, -42)
Red: (27, 43)
Displacement:
Blue: (-2, -44)
Red: (26, 46)
Displacement:
Blue: (-5, -44)
Red: (27, 46)
Displacement:
Blue: (-17, -41)
Red: (5, 48)
Displacement:
Blue: (-17, -42)
Red: (5, 52)
Displacement:
Blue: (-17, -42)
Red: (5, 50)
Displacement:
Blue: (-12, -64)
Red: (10, 73)
Displacement:
Blue: (-12, -68)
Red: (10, 76)
Displacement:
Blue: (-14, -68)
Red: (11, 76)
Displacement:
Blue: (-29, -78)
Red: (8, 98)
Displacement:
Blue: (-26, -84)
Red: (8, 100)
Displacement:
Blue: (-30, -84)
Red: (8, 100)
Displacement:
Blue: (-16, -59)
Red: (-3, 65)
Displacement:
Blue: (-16, -64)
Red: (-3, 68)
Displacement:
Blue: (-17, -62)
Red: (-3, 68)
Displacement:
Blue: (-9, -51)
Red: (3, 61)
Displacement:
Blue: (-8, -60)
Red: (4, 68)
Displacement:
Blue: (-9, -52)
Red: (3, 66)
Displacement:
Blue: (-21, -56)
Red: (7, 60)
Displacement:
Blue: (-22, -60)
Red: (7, 64)
Displacement:
Blue: (-22, -60)
Red: (7, 64)
Displacement:
Blue: (38, -40)
Red: (-43, 67)
Displacement:
Blue: (36, -44)
Red: (-40, 72)
Displacement:
Blue: (-40, 72)
Red: (36, -42)
Displacement:
Blue: (1, -55)
Red: (-15, 58)
Displacement:
Blue: (1, -60)
Red: (-15, 64)
Displacement:
Blue: (1, -58)
Red: (-15, 62)
Displacement:
Blue: (-8, 22)
Red: (2, -12)
Displacement:
Blue: (-9, 23)
Red: (2, -12)
Displacement:
Blue: (-7, 22)
Red: (17, 60)
Displacement:
Blue: (13, -40)
Red: (2, 57)
Displacement:
Blue: (14, -48)
Red: (2, 60)
Displacement:
Blue: (10, -52)
Red: (2, 60)
Displacement:
Blue: (6, -49)
Red: (-18, 47)
Displacement:
Blue: (6, -52)
Red: (-19, 52)
Displacement:
Blue: (6, -52)
Red: (-18, 50)