CS194-26: Image Manipulation and Computational Photography


Project 3: Fun with Frequencies and Gradients

Part 1: Frequency Domain

Part 1.1: Warmup

For this part of the project, we had to sharpen an image using the unsharp masking technique introduced in lecture. First I blurred the image using a Gaussian filter with sigma = 10, then subtracted the blurred image from the original image to get the "details" of the image. I then added this back to the original image, resulting in a sharpened image!

Original (Blurry) Image, taken at the San Diego Zoo Safari Park
Image Details (high frequency)
Sharpened Image, produced by adding the image details to the original image


Part 1.2: Hybrid Images

In this part of the project, we blended together two photos, one isolating the high frequencies and one isolating the low frequencies, so that the blended image would appear different depending on the distance from which it is viewed from. At a close distance, the high frequency image should dominate and be visible. At a greater distance, only the low frequency image should be visible.
To accomplish this, I used a 2D Gaussian filter as a low-pass filter for one image. For the other image, I used the same low-pass filter, then subtracted the result from the original image to get only the high frequencies of that image. I experimented with different values of sigma for the Gaussian filters of each image to determine which value worked best for each image. I used the given python code to align the images, and then added them to get a hybrid image!
Here are a few of my favorites:

Example 1: Puppy to Roommate

Original of the high frequency image
Original of the low frequency image
Resultant hybrid image

Example 2: Space Needle to Eiffel Tower

Original of the high frequency image
Original of the low frequency image
Resultant hybrid image

Example 3: Tiger to Mackenzie

Original of the high frequency image
Original of the low frequency image
Resultant hybrid image

Fourier Analysis

I decided to do a Fourier analysis for the images relating to the Space Needle/Eiffel Tower hybrid image. Below I have displayed an image followed by the log magnitude of the Fourier transform of that image.

Eiffel Tower Input Image
Fourier Transform of Eiffel Tower
Space Needle Input Image
Fourier Transform of Space Needle
Filtered Eiffel Tower
Fourier Transform of Filtered Eiffel Tower
Filtered Space Needle
Fourier Transform of Filtered Space Needle
Hybrid Image
Fourier Transform of Hybrid Image

Failure Hybrid Image

I tried to create a hybrid image of my cousin's proposal (before and after she said yes), but as you can see, the lack of alignment in their bodies makes it so that the result is not quite a hybrid image, but instead looks like an image with two ghosts at whatever distance it is viewed at.

High Frequency Image
Low Frequency Image
Attempted Hybrid Image

Part 1.3: Gaussian and Laplacian Stacks

For this part of the project, I implemented Gaussian and Laplacian stacks. To implement Gaussian stacks, I took an image, then applied a Gaussian filter to that image to produce a result. I then applied a Gaussian filter to that result, and continued that process for the specified number of levels in the stack. To implement the Laplacian stack, each level of the Laplacian stack was the difference between two levels in the Gaussian, with the exception of the last level of the Laplacian stack, which was equal to the last level in the Gaussian stack. The motivation behind this is so that superimposing all images in the Laplacian stack will result in the original image.
Here is an example of an image that I applied the Gaussian and Laplacian stacks to:

Example: Space Needle/Eiffel Tower Hybrid

For this image, I used 5 levels for each stack, with a sigma value of 3.
Gaussian Stack:

1
2
3
4
5

Laplacian Stack:

1
2
3
4
5

Part 1.4: Multiresolution Blending

For this part of the project, we blended two images together. To determine which part of the image to blend, we created a mask for the image such that the pixels overlapping the areas of the image we wanted to include were set to 1, and the rest of the pixels were set to 0. Before processing the images, I split them into their separate RGB color channels, and did the processing to each channel. I calculated the Laplacian stack for both images, and the Gaussian stack for the mask. Then, for each layer of the stack, I multiplied the Gaussian of the mask with the Laplacian of one image and added it to the product of the compliment of the Gaussian of the mask and the other image. After completing this for each level of the stack, I added the results for all the layers together, normalized the final results, and combined the color channels together to make one smoothly-blended and colored image! I used 5 levels for the stacks, and used different values of sigma depending on the image choices. Here are some of my favorites:

Example 1: The Orapple (sigma=20)

Apple
Orange
Apple-Orange Blend

Example 2: The Space Needle and Eiffel Tower, Again (sigma=15)

Space Needle
Eiffel Tower
Tower Blend

Example 3: Roses (sigma=50)

Rose (from my Mom's garden)
Rose (from Monet's garden in Giverny)
Rose Blend

Example 4: Coffee is my Universe, irregular mask (sigma=15)

Coffee
The Milky Way
Coffe with some Milky Way

Part 2: Gradient Domain Fusion

Part 2.1: Toy Problem

For this part of the project, we had to compute the gradients of an image, and then use these calculated gradients and a pixel value as a boundary constraint to reconstruct this image. We did this by writing these gradients and the pixel value as a system of linear equations which we then solved by using the Least Squares approximation. The resulting image was the closest solution to that system of linear equations - which means it was essentially identical to the original image itself!

The Original Image
The Reconstructed Image

Part 2.2: Poisson Blending

For this part of the project, we blended an image (the source image) onto another background image (the target image). The first step for combining these images was to create a mask that included only the piece of the source image we wanted to copy over, and to create a new source image that placed that piece of the image in the place that we wanted it to be in the target image. I used the provided Matlab code to take care of this step. Then, using this mask and generated source image, I set up a system of linear equations (like in the previous problem). My goal was to have the gradients of the source image within the mask be equal to the gradients in the final image, and for the boundary pixel values of the mask to match the pixel values of the target image. I translated these constraints into a system of linear equations, and then determined the resulting image by finding the Least-Squares approximation to the system of equations (as this is the best approximation to the constraints we desire). In the resulting image, you should not be able to tell which part of the image came from the source, and which came from the target! Here are a few of my favorite examples:

Example 1: Swimming with Dolphins

Source
Target
Source Pixels Pasted onto Target Image
Final Image

Example 2: Creepy Coffee

Source
Target
Source Pixels Pasted onto Target Image
Final Image

Failure Example: Clock Faces

Source
Target
Source Pixels Pasted onto Target Image
Final Image

Failure Explanation
As you can see, this combination did not work very well. One reason for this is that preserving the gradients of the source image results in any texture from the target image not being propagated. This creates a more obvious border, as you can clearly see where the texture of the bricks ends. Another reason that this combination did not work well is that the face crosses over an edge - in this case, it was the edge between the Campanile and the sky. There are boundary conditions for both of these, which creates conflicting constraints that the face tried to satisfy. This resulted in the face propagating some of the blue sky color into the Campanile, and overall adopting a color that did not quite blend.

Laplacian Blending vs. Poisson Blending

Laplacian blending is better in the situation where you want to more closely preserve the source image colors, or when the source image is being placed in an area that crosses over an edge separating distinct colors. Laplacian blending blurs the borders between images, so it would not be good for cases in which you want to preserve the image detail around the border. This is also good for images in which there is a texture shift across boundaries, because the blurring will make this transition smoother.
Poisson blending is better in the opposite scenarios. The use of boundary constraints causes the source image to adopt the colors of the target image, so Poisson blending would be prefered if you do not need the colors of the source image to be preserved. Because Poisson blending aims to preserve the gradients of the source image, Poisson blending is good for images in which you would like to preserve the detail of the source image and do not mind the colors being shifted. However, Poisson blending is not very successful in combinations of images where the texture is very different across the border, because preserving the gradients preserves the difference in texture, and it will make a very obvious boundary.

Example: Coffe Cup + Milky Way

Coffee (target)
The Milky Way (source)

Laplacian Blend
Poisson Blend

As you can see, Poisson blending resulted in the galaxy adopting more of the colors of the coffee, whereas the Laplacian blending resulted in the galaxy maintaining more of its own color. I think that both approaches worked for this combination of images, but I personally prefer the Laplacian blended image in this case.