Parsa Fereydouni - cs194-26-agy
# load blurry image that needs to be touched up
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import convolve2d
import skimage.io as skio
import skimage as sk
from numpy import fft
imname = './images/blur.jpg'
blur = skio.imread(imname)
blur = sk.img_as_float(blur)
plt.figure(figsize=(8,8))
skio.imshow(blur)
skio.show()
# smoothed and detail
imname = './images/smooth.jpg'
smooth = skio.imread(imname)
smooth = sk.img_as_float(smooth)
imname = './images/detail.jpg'
detail = skio.imread(imname)
detail = sk.img_as_float(detail)
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(smooth)
plt.title("smoothed")
plt.subplot(1,2,2)
skio.imshow(detail)
plt.title("detail")
skio.show()
imname = './images/sharpened.jpg'
sharp = skio.imread(imname)
sharp = sk.img_as_float(sharp)
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(blur)
plt.title("original")
plt.subplot(1,2,2)
skio.imshow(sharp)
plt.title("sharpened")
skio.show()
The image has improved, and we can see more detail now. If you look at the car in the top left corner, for example, not much of the headlights and the grill of the origianl are visible but in the sharpened you can clearly see them.
We use Hybrid Images as described in the paper by Oliva et al. Hybrid Images is a tenchnique that produces static images with two interpretations, which change as a function of viewing distance. This technique enables us to layer the high frequencies from one image on top of low frequencies of another. Finally, the results shows that when seen up close, the viewer wil observe (mainly) the high frequency image, while from afar the low frequency image
# first hybrid's constituent images
imname = './images/nutmeg.jpg'
close = skio.imread(imname)
imname = './images/DerekPicture.jpg'
far = skio.imread(imname)
close = sk.color.rgb2gray(sk.img_as_float(close))
far = sk.color.rgb2gray(sk.img_as_float(far))
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("close")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("far")
skio.show()
# hybrid 1
imname = './images/hybrid1.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(8,8))
skio.imshow(hybrid)
skio.show()
# second hybrid's constituent images
imname = './images/lion.jpg'
close = skio.imread(imname)
imname = './images/kitten.jpg'
far = skio.imread(imname)
close = sk.color.rgb2gray(sk.img_as_float(close))
far = sk.color.rgb2gray(sk.img_as_float(far))
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("close")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("far")
skio.show()
# hybrid 2
imname = './images/hybrid2.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(8,8))
skio.imshow(hybrid)
skio.show()
# third hybrid's constituent images
imname = './images/run.png'
close = skio.imread(imname)
imname = './images/stand.png'
far = skio.imread(imname)
close = sk.color.rgb2gray(sk.img_as_float(close))
far = sk.color.rgb2gray(sk.img_as_float(far))
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("close")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("far")
skio.show()
# hybrid 3
imname = './images/hybrid3.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(8,8))
skio.imshow(hybrid)
skio.show()
# frequency of the orignal images
imname = './images/hybrid3_1_fft.png'
close = skio.imread(imname)
imname = './images/hybrid3_2_fft.png'
far = skio.imread(imname)
close = sk.color.rgb2gray(sk.img_as_float(close))
far = sk.color.rgb2gray(sk.img_as_float(far))
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("close")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("far")
skio.show()
# frequency of the filtered images
imname = './images/hybrid3_high_fft.png'
close = skio.imread(imname)
imname = './images/hybrid3_low_fft.png'
far = skio.imread(imname)
close = sk.color.rgb2gray(sk.img_as_float(close))
far = sk.color.rgb2gray(sk.img_as_float(far))
plt.figure(figsize=(8,8))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("close")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("far")
skio.show()
# frequency of the hybrid
imname = './images/hybrid3_fft.png'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(8,8))
skio.imshow(hybrid)
skio.show()
In this part we will make use of Laplacian and Gaussian Stacks. Gaussian Stacks are generated by continuously filtering each subsequent level of an image, consequently stacking lower and lower frequencies. Using the Gaussian Stack we can generate a Laplacian Stack, which stores high frequencies, starting from the highest frequencies. To make the Laplacian stack, at each level i
we subtract the corresponding (i-1)
th from the i
th level of the Gaussian Stack, giving us the residual high frequencies in-between the Gaussian Stack levels.
# An interesitng multi frequency image by Teal Scot
imname = './images/multifreq.jpg'
pint = skio.imread(imname)
pint = sk.img_as_float(pint)
plt.figure(figsize=(10,10))
plt.imshow(pint)
plt.show()
# A 5 level stack
imname = './images/multifreq_stacks.png'
pint = skio.imread(imname)
pint = sk.img_as_float(pint)
plt.figure(figsize=(30,30))
plt.imshow(pint)
plt.show()
The first row of this image shows the gaussian stack of the image.
The second row shows the gaussian stack of the image.
By subtracting image (i + 1)
from image (i)
in the gaussian row (first row), you get image (i)
in the laplasian stack (second row).
# A 5 level stack of hybrid 1
imname = './images/hybrid1_stacks.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(30,30))
plt.imshow(hybrid)
plt.show()
# A 5 level stack of hybrid 2
imname = './images/hybrid2_stacks.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(30,30))
plt.imshow(hybrid)
plt.show()
# A 5 level stack of hybrid 3
imname = './images/hybrid3_stacks.jpg'
hybrid = skio.imread(imname)
hybrid = sk.img_as_float(hybrid)
plt.figure(figsize=(30,30))
plt.imshow(hybrid)
plt.show()
We use the Multiresolution Spline technique described in the paper by Burt et al. for combining two or more images into a larger image mosaic. This method is applied in a layered manner, where we take advantage of the Gaussian and Laplacian stacks in order to establish a visibly appealing blended image.
I'm using color!!
# original images
imname = './images/orange.jpeg'
orange = skio.imread(imname)
orange = sk.img_as_float(orange)
imname = './images/apple.jpeg'
apple = skio.imread(imname)
apple = sk.img_as_float(apple)
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
plt.imshow(orange)
plt.subplot(1,2,2)
plt.imshow(apple)
plt.show()
imname = './images/oraple_layers.png'
layers = skio.imread(imname)
layers = sk.img_as_float(layers)
plt.figure(figsize=(30,30))
plt.imshow(layers)
plt.show()
Start from the rightmost column. Take the last level of the laplacian stack for the apple and the orange, and mask them with a blurred mask taken from the gaussian stack of the mask (row 1 and 2 of this column). Add these two levels of detail to the last level of the gaussians of the masked apple and orange (not shown in this image) to get the last image in this column. Move to the left column and add the levels of detail corresponding to this column in the 2 laplacians and add them to the image we had before to get the last image in this column. Repeat for every column.
# multiresolution blend vs. naive blend
imname = './images/oraple.jpg'
close = skio.imread(imname)
imname = './images/pasted.jpg'
far = skio.imread(imname)
close = (sk.img_as_float(close))
far = (sk.img_as_float(far))
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("multiresolution")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("naive")
skio.show()
# original images
imname = './images/eyes.png'
eye = skio.imread(imname)
eye = sk.img_as_float(eye)
plt.figure(figsize=(8,8))
plt.imshow(eye)
plt.show()
imname = './images/eyes_layers.png'
layers = skio.imread(imname)
layers = sk.img_as_float(layers)
plt.figure(figsize=(30,30))
plt.imshow(layers)
plt.show()
# multiresolution blend vs. naive blend
imname = './images/eyes_blended.jpg'
close = skio.imread(imname)
imname = './images/eyes_pasted.jpg'
far = skio.imread(imname)
close = (sk.img_as_float(close))
far = (sk.img_as_float(far))
plt.figure(figsize=(10,10))
plt.subplot(1,2,1)
skio.imshow(close)
plt.title("multiresolution")
plt.subplot(1,2,2)
skio.imshow(far)
plt.title("naive")
skio.show()
Please note that for this image I had to go through more steps than with the oraple. First, instead of having two different source images to blend together as in the case with the oraple, I'm taking an eye from the source image and putting on the forehead of the same source image. To be able to do this, I had to make a new copy of the source so that this new image's eye would align with the others forehead. For this purpose I used the starter code that was provided for the hybrid images in 1.2. Then I made a mask for the new source image using the started code that was provided for 2.2. The rest of the steps are the same as the oraple.
you can see the aligned source images and the mask below.
imname = './images/i1.png'
s1 = skio.imread(imname)
imname = './images/i2.png'
s2 = skio.imread(imname)
imname = './images/i2_mask.png'
mask = skio.imread(imname)
s1 = (sk.img_as_float(s1))
s2 = (sk.img_as_float(s2))
plt.figure(figsize=(10,10))
plt.subplot(1,3,1)
skio.imshow(s1)
plt.title("eye")
plt.subplot(1,3,2)
skio.imshow(s2)
plt.title("forehead")
plt.subplot(1,3,3)
skio.imshow(mask)
plt.title("mask")
skio.show()
imname = './images/eyes_stacks.png'
layers = skio.imread(imname)
layers = sk.img_as_float(layers)
plt.figure(figsize=(30,30))
plt.imshow(layers)
plt.show()
First row: Gaussian stack
Second row: Laplacian stack
In this part of the project we takes advantage of the fact the human eye is tuned to the gradients and process the images in the gradient domain. As we know our visual perception is highly sensitive to changes, rather than real values, and so we a task like blending would theoretically be very suitable to such novel techniques.
In this part we effectively check if theory aligns with application, or more correctly, make sure our math is correct.
Our problem is as such: If we have 1 known pixel and the changes (gradients) of the image, are we able to perfectly reconstruct the image?
We have formulated the problem as a linear algebra problem, specifically as Ax=b
, using 3 questions:
where s denotes the source vector, and v is our result vector.
We will try this on an image from Toy Story!
# Read, convert, and display image
imname = './images/downscale.jpg'
source = skio.imread(imname)
source = sk.img_as_float(source)
plt.imshow(source, cmap='gray')
plt.title("Source")
plt.show()
imname = './images/reconstruct.jpg'
recon = skio.imread(imname)
recon = sk.img_as_float(recon)
plt.figure(figsize=(20,20))
plt.subplot(1,2,1)
plt.title("Source")
plt.imshow(source, cmap='gray')
plt.subplot(1,2,2)
plt.title("Reconstructed")
plt.imshow(recon, cmap='gray')
plt.show()
print("magnitude of difference:",np.linalg.norm(np.abs(recon-source)))
Reconstructed image looks good with very minimial error, thus our math is correct -> we can continue!
In the last part of the project we use the Poisson Blending technique described in the paper by Perez et al. for seamless editing of image regions. This technique follows closely to what we have done just prior in section 2.1, where me use the gradients while keeping some known
values to reconstruct. The difference in this part is we inject
the source's gradient patch into the target, leaving the border pixels as is, and solving it using least squares method. This will allow us to transition from one image to another without the need to white balance.
injection
siteInject
the patch into the corresponding location within the target # source images
imname = './images/penguin-chick_newsource.png'
penguin = skio.imread(imname)
imname = './images/im3.jpg'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
# masks images
imname = './images/penguin-chick_mask.png'
penguin = skio.imread(imname)
imname = './images/im3_mask.png'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
# source images
imname = './images/out_penguin.jpg'
blended = skio.imread(imname)
imname = './images/pasted_penguin.jpg'
pasted = skio.imread(imname)
penguin = sk.img_as_float(blended)
tahoe = sk.img_as_float(pasted)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(blended)
plt.title("belnded")
plt.subplot(1,2,1)
plt.imshow(pasted)
plt.title("naive")
plt.show()
# source images
imname = './images/sunbathing.jpg'
penguin = skio.imread(imname)
imname = './images/southpole.jpg'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
imname = './images/sunbathing_newsource.png'
penguin = skio.imread(imname)
penguin = sk.img_as_float(penguin)
plt.imshow(penguin)
plt.title("rescaled source to fit target image")
plt.show()
# masks images
imname = './images/sunbathing_mask.png'
penguin = skio.imread(imname)
imname = './images/southpole_mask.png'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
# source images
imname = './images/out_sunbathing.jpg'
blended = skio.imread(imname)
imname = './images/pasted_southpole.jpg'
pasted = skio.imread(imname)
penguin = sk.img_as_float(blended)
tahoe = sk.img_as_float(pasted)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(blended)
plt.title("belnded")
plt.subplot(1,2,1)
plt.imshow(pasted)
plt.title("naive")
plt.show()
# source images
imname = './images/disgust.jpg'
penguin = skio.imread(imname)
imname = './images/rembrandt.jpg'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
imname = './images/disgust_newsource.png'
penguin = skio.imread(imname)
penguin = sk.img_as_float(penguin)
plt.imshow(penguin)
plt.title("rescaled and rotated source to fit target image")
plt.show()
# masks images
imname = './images/disgust_mask.png'
penguin = skio.imread(imname)
imname = './images/rembrandt_mask.png'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
# source images
imname = './images/out_good.jpg'
blended = skio.imread(imname)
imname = './images/pasted_good.jpg'
pasted = skio.imread(imname)
penguin = sk.img_as_float(blended)
tahoe = sk.img_as_float(pasted)
plt.figure(figsize=(30,30))
plt.subplot(1,2,2)
plt.imshow(blended)
plt.title("belnded")
plt.subplot(1,2,1)
plt.imshow(pasted)
plt.title("naive")
plt.show()
# source images
imname = './images/source.jpg'
penguin = skio.imread(imname)
imname = './images/rembrandt.jpg'
tahoe = skio.imread(imname)
penguin = sk.img_as_float(penguin)
tahoe = sk.img_as_float(tahoe)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(penguin)
plt.subplot(1,2,1)
plt.imshow(tahoe)
plt.show()
# masks images
imname = './images/mask.jpg'
penguin = skio.imread(imname)
penguin = sk.img_as_float(penguin)
plt.imshow(penguin)
plt.show()
Notice that since the source image is aligned with the injection
site, we can use only one mask
# source images
imname = './images/out_bad.jpg'
blended = skio.imread(imname)
imname = './images/pasted_bad.jpg'
pasted = skio.imread(imname)
penguin = sk.img_as_float(blended)
tahoe = sk.img_as_float(pasted)
plt.figure(figsize=(16,16))
plt.subplot(1,2,2)
plt.imshow(blended)
plt.title("belnded")
plt.subplot(1,2,1)
plt.imshow(pasted)
plt.title("naive")
plt.show()
Even though Possion blending tried its best and managed to blend parts of the image, this example didn't work so well. My guess is the reason for it has to do with 2 facts. The lighting in the two images is too different. the source of light in the two images are on opposite sides so the gradients were not agreeble enough. Also the source image gradients around the borders of the mask are False values. The source image has a white background with the image of the man inserted into that white background artificially. That means that the gradients around the borders of the mask in the source image don't capture the changes in the lighting of the image quite right.
imname = './images/posion_eye.jpg'
imname = './images/eyes_blended.jpg'
blended = skio.imread(imname)
imname = './images/eyes_pasted.jpg'
pasted = skio.imread(imname)
poisson = sk.img_as_float(poisson)
blended = sk.img_as_float(blended)
pasted = sk.img_as_float(pasted)
plt.figure(figsize=(16,16))
plt.subplot(1,3,1)
plt.imshow(poisson)
plt.title("possion")
plt.subplot(1,3,2)
plt.imshow(blended)
plt.title("multiresolution")
plt.subplot(1,3,3)
plt.imshow(pasted)
plt.title("naive")
plt.show()
Multi resolution blending seems to be doing better thatn possion blending here, because here we don't actually want the eye to be following the same color pattern as the forehead, which is what possion is doing. We watn the edges of the eye (the seem) to be smoothed out with the forehead but we want to keep the colors of the eye intact so that it looks similar to the other two eyes.