FILL IN THIS PART
As a warmup, I implemented a low-pass filter using a Gaussian kernel and used it to sharpen an image. This is also called an unsharp masking technique.
sigma
By finding the difference between the blurred and original images, I isolated the areas of highest frequency (i.e. details). Then, by adding these pixels back into the original image, I can emphasize the details, creating a sharpened effect.
In an more involved exercise, I created hybrid images by combining two images treated with a high or low pass filter. The high pass filter isolates sharp details while the low pass filter gives a lburry photo represented by patches of light or dark areas. The optical illusion is that the viewer sees two different images from near and far distances.
For the "Watermelon/Apple" case, I produced a Fourier analysis using the provided code, np.log(np.abs(np.fft.fftshift(np.fft.fft2(gray_image))))
,
where gray_image
is a grayscale (luminance channel) version of each image.
Next, I created image stacks to analyze the structure of images at different "resolution" levels. Every image in
a Gaussian stack is the same dimension (so we do not downscale, as in a pyramid). However, each one uses an
exponentially larger σ (I used ** 1.3
) than the previous level. In a Laplacian stack, each
image is the difference between two adjacent levels of a Gaussian stack.
I also revisit my hybrid watermelon/apple from earlier. The Gaussian and Laplacian stacks also reveal a pattern about the hybrid image. Specifically, the lower levels of the Laplacian stack highlight the high-pass differences.
In a Laplacian stack, each level captures different amounts of detail. I can use this to create a smooth blend between two photos, where the finest detail uses a sharp mask and the most general color areas use a blended mask.
(mask[i] * img_1[i]) + ((1 - mask[i]) * img_2[i])
np.interp()
to rescale the sum back to [0.0, 1.0]
.Instead of blending together high and low pass frequencies, it's also possible to blend photos using the gradient domain. This means I am looking at the difference between two pixels and try to match these relationships instead of looking at pixels in isolation.
In the toy problem, we looked at horizontal and vertical gradients to reconstruct an image. I constructed a sparse
matrix A
of height 2*width*height+1. The extra +1 row was to match the intensities of the target and
solution image.
For Poisson blending, instead of looking at the horiztonal and vertical gradients separately, we look at the NSEW neighbors of a pixel. As a result my matrix is just width * height rows long. For neighbor pixels that are outside of the mask area, we want to set the result intensity to exactly the target pixel intensity. Then we also use least squares to blend out the errors and get the result image.
Possible failure explanations: the source image only has one RGB channel (green), the mask cropped too closely and didn't leave any concrete around the ball.
Improvements: the background isn't blended to a medium gray, it stays white because we're directly copying pixels from the target. Also it looks great in color! :)