CS 180 Project 2

Table of Contents

Introduction

This project covers several frequency-based techniques for image manipulation and processing. These methods include:

  • Enhancing image sharpness by amplifying high-frequency components
  • Detecting edges through the use of finite difference kernels
  • Generating hybrid images by combining high-frequency elements from one image with low-frequency elements from another
  • Blending multiple images across various frequency levels using Gaussian and Laplacian stacks

These approaches showcase the diverse applications of frequency analysis in creating innovative and interesting image processing effects.

Finite Difference Operator

Method

Two finite difference kernels were implemented as NumPy arrays to compute partial derivatives:

  • dx = np.array([[1, -1]]) for horizontal changes
  • dy = np.array([[1], [-1]]) for vertical changes

These kernels were applied to the original image using scipy.signal.convolve2d with the parameter mode='same', resulting in two images representing partial derivatives in the x and y directions.

To create a single edge image, the gradient magnitude was calculated at each pixel using:

np.sqrt(dx ** 2 + dy ** 2)

This operation effectively computes the L2 norm of the gradient vector formed by corresponding pixel values from the two partial-derivative images, producing the final edge-detected image.

Outputs

cameraman_dx

Cameraman (dx)

cameraman_dy

Cameraman (dy)

cameraman_grad_bin

Cameraman (Gradient, binarized)

Derivative of Gaussian Filter

Method

I took a different approach to edge detection by pre-processing the Gaussian kernels:

  1. First, I convolved the Gaussian kernels with the dx and dy finite difference kernels. This produced two new kernels: gaussian_dx and gaussian_dy.
  2. These new kernels effectively represent the partial derivatives of the Gaussian kernel with respect to x and y.
  3. Next, I applied these modified kernels to the original image through convolution. This step generated two partial derivative images.
  4. Finally, I combined these partial derivative images into a single edge image by calculating the magnitude of the gradient at each pixel.

This technique allowed me to integrate the Gaussian smoothing and differentiation steps, potentially offering a more efficient edge detection process. I also experimented with different values for kernel size and sigma and visualized the differences below:

Outputs

cameraman_dx

Cameraman (dx_gaussian)

cameraman_dy

Cameraman (dy_gaussian)

cameraman_grad_bin

Cameraman (ksize = 6, sigma = 1)

cameraman_grad_bin

Cameraman (ksize = 12, sigma = 2)

As expected, the results of the finite difference operator and derivative of Gaussian filter were quite similar in goal. The key difference was that the smoothing prevented additional artifacts and improved edge detection. For concrete differences, observe the "sky" in both pictures. Without smoothing, there are some white spots. Also, the edge corresponding to the back of the cameraman is captured in greater detail with smoothing.

Image Sharpening

Method

Here's how I sharpened an image:

  1. Blur the original image by convolving it with a Gaussian kernel.
  2. Extract high-frequency components: edges = img - img_blur
  3. Enhance the image: sharpened = img + alpha * edges

Where alpha is a constant that controls the sharpening intensity. I experimented with the value of alpha until it looked aesthetically pleasing.

Sharpening

For the first set of pictures, I set ksize=10 and sigma=2 when blurring and used alpha=1. This was done step-by-step, and the results were similar when using the filter.

taj_sharpen

Sharpening Taj step-by-step

Custom Images

I found this segment particularly cool and picked two distinct pictures. The first is of me on a sailboat, and the second is a picture of Cambridge, UK, I had taken this year.

sail_sharpen

Sharpening myself on a sailboat step-by-step

cambridge_sharpen

Sharpening a picture of Cambridge, UK step-by-step

Resharpening

taj_resharpen

Sharpening blurred Taj with unsharp mask filter

sail_resharpen

Sharpening blurred sailing picture with unsharp mask filter

cambridge_resharpen

Sharpening blurred picture of Cambridge, UK with unsharp mask filter

When working with the original image, the effects of sharpening were prominent. As the details outline indicates, we are adding emphasis to various bricks of the Taj Mahal as well as the lined bushes near it. If we blur the original image, we lose some information, and so we cannot expect to reconstruct details we didn't have access to. Still, the filter does a good job at highlighting the prominent edges.

Hybrid

Method

The approach here was to blur im1 to obtain a low pass filtered version of it, with sigma=sigma1. Then, I constructed the laplacian of im2 by computing the difference between the original and a blurred im2, with sigma=sigma2.

Outputs

Cats are great. Both Nutmeg and a random cat from the internet formed a pretty nice hybrid image with my LinkedIn picture.

nutmeg_yash

Me x Nutmeg

hybrid_cat_yash

Me x Random Cat

I also wanted to experiment with pictures taken moments apart to see if I could communicate motion. The swing up by Big C provided a perfect opportunity. After a bit of alignment and tuning of sigma1 and sigma2, the results were:

yash_swing1

Swing Image 1

yash_swing2

Swing Image 2

hybrid_yash_swing

Hybrid Swing

Failure

When combining the flowers below, I had higher hopes. I thought the edges would be captured well in the high-pass filter, and the intricacy could provide the perception of two different flowers.

pink_flower

Flower Image 1

orange_flower

Flower Image 2

hybrid_flower

Hybrid Flower

Fourier Analysis

I chose to visualize the Fourier transforms of the swing pictures below:

im1_fft

Swing1 FFT (Original + High Filter)

im2_fft

Swing2 FFT (Original + Low Filter)

hybrid_fft

Hybrid Fourier (creatively normalized)

Art?

As a quick bonus, I attempted to combine these two pictures from within and outside a water tower. It didn't work as expected, but with some normalization and blurring, it felt Picasso-esque.

hole_in

Hole (Inside)

hole_out

Hole (Outside)

hybrid_hole_norm

Hybrid Hole (normalized creatively)

Gaussian and Laplacian Stacks

Method

Here, I built up the gaussian and laplacian stacks like so:

This pseudocode:

  1. Initialized the gaussian list with the input img.
  2. Looped through the depth range, performing the following steps:
    • Blurred the previous gaussian image to get the current gaussian image.
    • Calculated the laplace image as the difference between the previous and current gaussian images.
    • Normalized the laplace image and stored it in the norm_laplace list.
  3. Appended the final gaussian image to the laplace list.
  4. Returned the gaussian, laplace, and norm_laplace lists.
I did not include the bottom-most blurred gaussian layer in the normalized laplace stack while displaying, though it is needed to reconstruct the original image with the laplacian stack.

Outputs

cameraman_dx

Laplace Stack of Apple

cameraman_dx

Laplace Stack of Orange

Multiscale Blending

Method

I defined my blend function such that the inputs were two image filenames, a 2D mask, and several optional parameters to control the size, blurring, and the output format. Rather than deal with alignment or cropping, I worked with all square images and performed resizing before blending.

Big picture, my function did the following:

  1. Load and process the two input images to resize them to a common size and convert them to the appropriate data format.
  2. Generate Gaussian, Laplace, and normalized Laplace image stacks for both input images using the generate_stacks function.
  3. Resize the 2D mask to the same size as the input images and generate a Gaussian pyramid of the mask.
  4. Blend the Laplace image stacks of the two input images using the Gaussian mask pyramid, creating a combined Laplace stack.
  5. Collapse the combined Laplace stack into a single output image by summing the Laplace images and clipping the result to the valid pixel value range.
  6. Optionally, return the output image and the normalized combined Laplace stack.

The key idea behind this function is to blend the two input images by decomposing them into Gaussian and Laplace image pyramids, and then using a mask to selectively combine the Laplace components of the two images. This allows for a more seamless and natural-looking blend, especially around the edges and boundaries defined by the mask.

Outputs

I had the most fun here as I tried out a variety of combinations and masks.

Food Combos

First up, I have a pineapple bun (courtesy of Sheng Kee), and I have pizza. I present pineapple bun pizza. While I could reuse the same half mask from earlier, I found a custom semi-circle mask worked better.

pineapple_bun_pizza

Original images

bun_mask

Custom Bun Mask

high_res_pineapple_bun_pizza

High-resolution pineapple bun pizza

Then, I tried combining different flavors of ice cream with that filter. I let it run for longer (15-20 minutes) with higher resolutions instead of downsampling, and as one would expect, that made the blend sharper.

choc_icecream

Chocolate Ice Cream

blue_icecream

Blue Ice Cream

choc_blue_icecream

Chocolate and Blue Ice Cream Combo

For fun, I tried a pineapple bun ice cream combo. Here, I realized that leaving the kernel size the same meant that the blurring was not as effective with a significantly higher resolution, making the seam more apparent.

choc_bun

Chocolate Pineapple Bun Combo

Landscapes

Finally, I experimented with more complicated masks after many struggles with Photoshop. I decided on a photo of me at the top of Mission Peak and tried to blend it into various backgrounds: Tahoe, a Spiderverse-inspired wallpaper of NYC, pictures from a Switzerland trip a decade ago, and one of London. The original images are below:

jump_and_mask

Jump and Mask

locations

Original Backgrounds: Switzerland, London, Tahoe, NYC

As one would guess, when the lighting conditions are similar across the blends, it is more seamless.

jump_switzerland

Jump Switzerland Blend

jump_london

Jump London Blend

jump_tahoe

Jump Tahoe Blend

jump_nyc

Jump NYC Blend