CS194-26 Project 2: Fun with Filters and Frequencies

Gradient Magnitude Computation: Brief Description

I used the gradient formula from the lecture slides to compute the gradient magnitude and display the image. The slides state that the gradient magnitude is calculated by taking the square root of the sum of the partial derivative (with respect to x) squared and the partial derivative (with respect to y) squared. This is shown in my code, where I assign im_gradient = np.sqrt((im_x ** 2) + (im_y ** 2)). It did take me a while to figure out how get the partial derivatives themselves, but after reading a bit of documentation for the signal.convolve2d function, I realized that it was just about convolving two 2D arrays together, and I was able to create 2D arrays for D_x and D_y.

Below are (from left to right):
1. original image convolved with the D_x operator
2. original image convolved with D_y operator
3. gradient magnitude original image
4. binarized gradient magnitude original image

Original image convolved with D_x Original image convolved with D_y Gradient magnitude original image Binarized gradient magnitude original image

Below are (from left to right):
1. blurred image (with gaussian) convolved with the D_x operator
2. blurred image convolved with the D_y operator
3. gradient magnitude blurred image
4. binarized gradient magnitude original image

Blurred image convolved with D_x Blurred image convolved with D_y Gradient magnitude blurred image Binarized gradient magnitude blurred image

Answers to Questions in 1.2

The main difference is that the outlines in the images where we blurred it with the gaussian filter first are much more defined than in the original image. The edges in the gradient magnitude images are also smoother and thicker than the original ones, and there is much less noise. Then, I changed the process to using a single convolution, as mentioned in the spec, and got the exact same images as before, but with less convolution steps needed.

Blurred image convolved with D_x Blurred image convolved with D_y Gradient magnitude blurred image Binarized gradient magnitude blurred image

Unsharp Masking

I first blurred the castle image using convolve2d on each one of its colour channels, and then I sharpened it using the unsharp mask filter formula given to us in lecture: f + alpha(f - f*g), using alpha=2, because any value higher than that resulted in the image being too sharp.

Blurred castle image Sharpened castle image

Then, using a different image of my liking (NYC skyline), I repeated the blur-sharpen process, and then re-blurred the result of that, then re-sharpened the result of that. We can see that the re-blurred image is not as blurry as the original blurred one, but the re-sharpened one is not as clear as the original sharpened one. This makes sense because we repeated the process on an image that was already sharpened, so it wouldn't be as blurry, but then sharpening it again made the last image a little too hazy. These are the four resulting images in order:

NYC blurred NYC sharpened

re-blurred and re-sharpened:

NYC blurred again NYC sharpened again

Hybrid Images and Fourier Analysis

I had to experiment with different values for the kernel sizes and sigmas of both Gaussian filters. I had to run align_images in terminal to be able to click on the points in the images, and then I copied and pasted those points into my Jupyter notebook. Ultimately, I found that having high values for the Gaussian filter that would be used in the high-pass worked best to bring out the high frequencies, and that having lower values for the low-pass Gaussian filter worked best to subdue the low frequencies at a close-up perspective. I also found it really helpful to use the ratio that I saw on Piazza, where sigma should be 3 * half-width (of the filter itself). I made sure to keep this proportion as I tried different values, and it worked very well.

I then tried making a hybrid image out of a basketball and a smiley face. It took me a while to learn that I had to resize one of the images to match the other one, or they would not align. Once I figured this out, I was able to align and blend the two images a little bit, but the two circles are not exactly the same size, and it's really hard to make out the eyes and mouth of the smiley face in comparison to the huge, bold "Wilson" on the basketball, no matter how I adjusted the kernel size and sigma values. I consider this one a failure; the two images just don't go together very well without the basketball overpowering the smiley face.

Lastly, I made a hybrid image out of two of my favourite characters from Avatar: The Last Airbender, Azula and Zuko. The images that I downloaded from online turned out to be coincidentally exactly the same size, which was super convenient! I made Azula the high frequency one and Zuko the low frequency one, and it works! I can see Zuko when I'm close to the screen, but from afar, I can mainly see Azula's hair hanging over the sides of her face, and way more of Azula's facial features. This was definitely my favourite one, and I consider it a success.

Derek nutmeg final image Basketball smiley final image Azula Zuko final image

Below is the Fourier analysis for Derek-Nutmeg (from left to right):
1. fft of im1 (derek)
2. fft of im2 (nutmeg)
3. fft of low pass filter
4. fft of high pass filter
5. fft of hybrid image

Fft of im1 Fft of im2 Fft of low pass filter Fft of high pass filter Fft of hybrid image

Multiresolution Blending

I created the Gaussian and Laplacian stack functions by following Piazza answers and doing test runs with different values of kernel size and sigma. Once again, I made sure to keep the proportion of 3 * sigma = 1/2 kernel size, so with each iteration, I doubled both the sigma and kernel size values. It took a little bit of trial and error to determine the best starting values for kernel size and sigma, but I found that higher values made the image lighter, and lower values made the final image darker, so I chose something in-between. Finally, I used the formula: blended = mask_gaussian * orange_laplacian + (1 - mask_gaussian) * apple_laplacian, from Piazza, to put everything together, and took the sum of the 0-axis for the final image. This was my final oraple!

Final oraple image

After doing the blending, I worked on re-creating the 12 images shown that we needed to replicate. I did this by multiplying each apple and orange image with the binary mask, so that half of the image would be completely grey, and so that it would be exactly like the given ones. This part was relatively easy.

First 6 images Last 6 images

Two Other Multiresolution Examples

Next I did my own example of a multiresolution blending, on Nina Dobrev and Victoria Justice. I've always thought the two celebrities have really similar faces, and I wanted to put that to the test. I used the same steps I did for oraple.jpg, and put Nina on the left side and Victoria on the right side. Overall, Victoria's face seems to be slightly longer, so although her eyes and nose line up with Nina's, the bottom of her chin extends a little further. Other than that though, the two halves go together very well and proves the similarity between them.

Lastly, I did a horizontal mask (irregular mask), putting the top half of a Naturday can on top of the bottom half of a Natural Light can. The sides of the can blend seamlessly, and you can tell that the Naturday's flamingos (exclusive to Naturdays) transitions into the eagle of the Natural Light (the eagle being exclusive to Natural Light regular beers).

Final nina-victoria image Final natty-naturday image