Required Reading/Viewing:
Additional Reading
Recommended Jupyter Theme for presenting this notebook:
jt -t grade3 -cellw=90% -fs=20 -tfs=20 -ofs=20
#(Optional) Download data + videos if you don't have them.
import os, sys
sys.path.append('..')
from util.get_and_unpack import get_and_unpack
if not os.path.isdir('../data/'):
url = 'http://www.welchlabs.io/unccv/the_original_problem/data/data.zip'
get_and_unpack(url, location='..')
if not os.path.isdir('../videos/'):
url = 'http://www.welchlabs.io/unccv/the_original_problem/videos.zip'
get_and_unpack(url, location='..')
Hopefully you've taken some time to think through some possible fixes to these problems. Let's start by seeing if we can make our images less noisy.
%pylab inline
#Load image
im = imread('../data/medium_2/ball/ball_3.jpg')
#Let's have a look
fig = figure(0, (12,12))
imshow(im)
title('Gross, so noisy! What is this, 2003?');
#Add path to top level directory so we can access utilities
import sys
sys.path.append('..')
#Import grayscale method
from util.image import convert_to_grayscale
#Scale image from 0-1, and convert to grayscale
gray = convert_to_grayscale(im/255.)
fig = figure(0, (6,6))
imshow(gray, cmap = 'gray');
hist(gray.ravel(), 100);
grid(1)
from util.filters import roberts_cross
edges = roberts_cross(gray)
fig = figure(0, (12,12))
imshow(edges);
A very successful/popular appraoch to noise reduction is to replace the individual pixels in our images with some type of weighted average of their local neighborhoods.
#Little trick to progress through slides within the notebook
from IPython.display import Image, display
from ipywidgets import interact
#Quick method to let me step through "slides"
def slide_show(slide_num=1):
display(Image('../graphics/averaging_slides/' + str(slide_num).zfill(2) + '.png'))
interact(slide_show, slide_num = (1, 2));
fig = figure(0, (9,9))
ax = fig.add_subplot(111)
ax.imshow(edges)
ax.tick_params(axis='both', which='major', labelsize=25)
def filter_2d(im, kernel):
'''
Filter an image by taking the dot product of each
image neighborhood with the kernel matrix.
Args:
im = (H x W) grayscale floating point image
kernel = (M x N) matrix, smaller than im
Returns:
(H-M+1 x W-N+1) filtered image.
'''
M, N = kernel.shape
H, W = im.shape
filtered_image = np.zeros((H-M+1, W-N+1), dtype = 'float64')
for i in range(filtered_image.shape[0]):
for j in range(filtered_image.shape[1]):
image_patch = im[i:i+M, j:j+N]
filtered_image[i, j] = np.sum(np.multiply(image_patch, kernel))
return filtered_image
filter_2d
method. kernel = (1/9)*np.ones((3,3))
kernel
# Filter Image!
im_filtered = filter_2d(gray, kernel)
im_filtered.shape
fig = figure(0, (20,10))
fig.add_subplot(1,2,1)
imshow(gray, cmap = 'gray');
title('Before');
fig.add_subplot(1,2,2)
imshow(im_filtered, cmap = 'gray');
title('After');
kernel = (1/81)*np.ones((9,9))
im_filtered = filter_2d(gray, kernel)
fig = figure(0, (20,10))
fig.add_subplot(1,2,1)
imshow(gray, cmap = 'gray');
title('Before');
fig.add_subplot(1,2,2)
imshow(im_filtered, cmap = 'gray');
title('After');
kernel = (1/25)*np.ones((9,9))
#Load up another image:
brick = imread('../data/easy/brick/brick_4.jpg')
#Scale intensity values between 0 and 1, convert to grayscale, and filter
brick_gray = convert_to_grayscale(brick/255.)
brick_filtered = filter_2d(brick_gray, kernel)
fig = figure(0, (12,12));
imshow(brick_filtered, cmap = 'gray');
toy_image = np.zeros((21,21))
toy_image[10, 10] = 1.0
fig = figure(0, (6, 6))
imshow(toy_image,vmin=0, vmax=1);
(21x21)
, image of all zeros with a single value of 1 in the very center.Note: color range is different in each image, be sure to pay attention to the colorbar units.
Quick Explanation
3x3
- it should be the same size as our filter.#And finally, just in case you're suspicious, we can actually perform the filtering in question:
box_5 = (1/25)*np.ones((5,5))
filtered_box_5 = filter_2d(toy_image, box_5)
imshow(filtered_box_5)
colorbar()
An important point here - The above result kind of gives an impression that, filtering has made the center pixel spread without changing its intensity becuase we've scaled the colors to the maximum value in the filtered image. Another option here is to use a consistent color mapping, which allows us to get a better feel for how our input and output intensities compare:
fig = figure(0, (18, 7))
fig.add_subplot(1,2,1)
imshow(toy_image,vmin=0, vmax=1);
colorbar();
title('Before Filtering');
fig.add_subplot(1,2,2)
imshow(filtered_box_5, vmin=0, vmax=1);
colorbar();
title('After Filtering');
When our eyes and most cameras create blur by being out of focus, the intensity generally falls of much more smoothly than we've seen with our boxcar smoothing filter.
G_{\sigma}(x, y) = \frac{1}{2 \pi \sigma^2} exp \bigg(- \frac{(x^2+y^2)}{2 \sigma^2} \bigg) $$
Explanation
(5x5)
, this would make k=2
. (i=2, j=2)
.x=0
and y=0
.x=0
and y=0
.(i=2, j=2)
. Alright, let's code it up!
def make_gaussian_kernel(size, sigma):
'''
Create a gaussian kernel of size x size.
Args:
size = must be an odd positive number
sigma = standard deviation of gaussian in pixels
Returns: A floating point (size x size) guassian kernel
'''
#Make kernel of zeros:
kernel = np.zeros((size, size))
#Handle sigma = 0 case (will result in dividing by zero below if unchecked)
if sigma == 0:
return kernel
#Helpful for indexing:
k = int((size-1)/2)
for i in range(size):
for j in range(size):
kernel[i, j] = (1/(2*np.pi*sigma**2))*exp(-((i-k)**2 + (j-k)**2)/(2*sigma**2))
return kernel
gaussian_kernel = make_gaussian_kernel(size = 11, sigma = 2)
imshow(gaussian_kernel)
And in 3d:
from mpl_toolkits.mplot3d import Axes3D
X, Y = np.meshgrid(range(11), range(11))
# Plot kernel as 3d surface.
fig = plt.figure(0, (9,9))
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, gaussian_kernel, cmap=cm.viridis, antialiased=True)
box_kernel = (1/25)*np.ones((5,5))
gaussian_kernel = make_gaussian_kernel(size = 7, sigma = 1.5)
fig = figure(0, (20, 12))
fig.add_subplot(2,3,1)
imshow(toy_image, cmap = 'gray')
title('Original Image');
fig.add_subplot(2,3,2)
imshow(filter_2d(toy_image, box_kernel), cmap = 'gray')
title('Box Function Blur');
fig.add_subplot(2,3,3)
imshow(filter_2d(toy_image, gaussian_kernel), cmap = 'gray')
title('Gaussian Blur');
fig.add_subplot(2,3,4)
imshow(brick_gray, cmap = 'gray')
title('Original Image');
fig.add_subplot(2,3,5)
imshow(filter_2d(brick_gray, box_kernel), cmap = 'gray')
title('Box Function Blur');
fig.add_subplot(2,3,6)
imshow(filter_2d(brick_gray, gaussian_kernel), cmap = 'gray')
title('Gaussian Blur');
Ok, so we've gound a good method for reducing the noise level in our images - let's try it out with our edge image from Robert's cross, and see if we can reduce some of the noise.
fig = figure(0, (6,6))
imshow(edges);
#Make gaussian kernel
gaussian_kernel = make_gaussian_kernel(size = 11, sigma = 2)
#Filter edge image
edges_filtered = filter_2d(edges, gaussian_kernel)
fig = figure(0, (16,9))
fig.add_subplot(1,2,1)
imshow(edges)
title('Original');
fig.add_subplot(1,2,2)
imshow(edges_filtered)
title('Filtered with Gaussian Kernel');
from ipywidgets import interact
def blur_and_display(size = 5, sigma = 2):
#Make gaussian kernel
gaussian_kernel = make_gaussian_kernel(size = size, sigma = sigma)
#Filter edge image
edges_filtered = filter_2d(edges, gaussian_kernel)
fig = figure(0, (9,9))
imshow(edges_filtered)
interact(blur_and_display, size = (3, 31, 2), sigma = (1, 10, 0.5));
Computation for first filtering example.
im = np.array([[3, 2, 8 , 5, 7],
[1, 4, 8, 9, 5],
[2, 6, 7, 3, 9],
[9, 7, 8, 3, 1],
[8, 2, 3, 4, 2]])
k = np.array([[0, -1, 0],
[-1, 1, 0],
[0, 2, -1]])
filter_2d(im, k)