part0.py
import sys
import os
import numpy as np
import random
import cv2
import run
def convolve(image, kernel):
'''Convolve the given image and kernel
Inputs:
image - a single channel (rows, cols) image with dtype uint8
kernel - a matrix of shape (d, d) and dtype float
Outputs:
output - an output array of the same dtype as image, and of shape
(rows - d + 1, cols - d + 1)
Every element of output should contain the application of the kernel to the
corresponding location in the image.
Output elements that result in values that are greater than 255 should be
cropped to 255, and output values less than 0 should be set to 0.
'''
output = None
# Insert your code here.----------------------------------------------------
(rows, cols) = image.shape
(drows, dcols) = kernel.shape
d = 0
if drows != dcols:
raise ValueError('The kernel must be a square')
else:
d = drows
if (d % 2 != 1):
raise ValueError('The kernel size must be a odd number')
k = (d - 1)/2
output = np.zeros((rows - d + 1, cols - d + 1), dtype = np.uint8)
for i in range(rows - d + 1):
for j in range(cols - d + 1):
result = 0
for u in range(-k, k + 1):
for v in range(-k, k + 1):
result = result + kernel[u + k, v + k] * image[i - u + k, j - v + k]
if (result < 0):
result = 0
if (result > 255):
result = 255
output[i, j] = result
#---------------------------------------------------------------------------
return output
def test():
'''This script will perform a unit test on your function, and provide useful
output.
'''
images = []
kernels = []
outputs = []
x = np.zeros((9,9), dtype = np.uint8)
x[4,4] = 255
images.append(x)
x = np.zeros((5,5), dtype = np.uint8)
x[2,2] = 255
images.append(x)
y = np.array(
[[-0.9,-0.7, 0.5, 0.3, 0.0],
[-0.7, 0.5, 0.3, 0.0, 0.4],
[ 0.5, 0.1, 0.0, 0.4, 0.6],
[ 0.1, 0.0, 0.2, 0.6, 0.8],
[ 0.0, 0.2, 0.6, 0.8, 1.2]])
kernels.append(y)
y = np.array(
[[0.1, 0.1, 0.1],
[0.1, 0.2, 0.1],
[0.1, 0.1, 0.1]])
kernels.append(y)
z = np.array(
[[ 0, 0, 127, 76, 0],
[ 0, 127, 76, 0, 102],
[ 127, 25, 0, 102, 153],
[ 25, 0, 51, 153, 204],
[ 0, 51, 153, 204, 255]], dtype = np.uint8)
outputs.append(z)
z = np.array(
[[ 25, 25, 25],
[ 25, 51, 25],
[ 25, 25, 25]], dtype = np.uint8)
outputs.append(z)
for image, kernel, output in zip(images, kernels, outputs):
if __name__ == "__main__":
print "image:\n{}".format(image)
print "kernel:\n{}".format(kernel)
usr_out = convolve(image, kernel)
if not type(usr_out) == type(output):
if __name__ == "__main__":
print "Error- output has type {}. Expected type is {}.".format(
type(usr_out), type(output))
return False
if not usr_out.shape == output.shape:
if __name__ == "__main__":
print "Error- output has shape {}. Expected shape is {}.".format(
usr_out.shape, output.shape)
return False
if not usr_out.dtype == output.dtype:
if __name__ == "__main__":
print "Error- output has dtype {}. Expected dtype is {}.".format(
usr_out.dtype, output.dtype)
return False
if not np.all(usr_out == output):
if __name__ == "__main__":
print "Error- output has value:\n{}\nExpected value:\n{}".format(
usr_out, output)
return False
if __name__ == "__main__":
print "Passed."
if __name__ == "__main__":
print "Success."
return True
if __name__ == "__main__":
# Testing code
print "Performing unit test."
test()
part1.py
import sys
import os
import numpy as np
from scipy import signal
import math
import random
import cv2
import run
def make_gaussian(k, std):
'''Create a gaussian kernel.
Input:
k - the radius of the kernel.
std - the standard deviation of the kernel.
Output:
output - a numpy array of shape (2k+1, 2k+1) and dtype float.
If gaussian_1d is a gaussian filter of length 2k+1 in one dimension,
kernel[i,j] should be filled with the product of gaussian_1d[i] and
gaussian_1d[j].
Once all the points are filled, the kernel should be scaled so that the sum
of all cells is equal to one.'''
kernel = None
# Insert your code here.----------------------------------------------------
kernel = np.zeros((2 * k + 1, 2 * k + 1), dtype = np.float)
#---------------------------------------------------------------------------
gaussian_1d = np.zeros((2 * k + 1), dtype = np.float)
for i in range(-k, k + 1):
gaussian_1d[i + k] = math.exp(-0.5*i*i/(std*std))
total = 0
for u in range(-k, k + 1):
for v in range(-k, k + 1):
kernel[u + k, v + k] = gaussian_1d[u + k] * gaussian_1d[v + k] #math.exp(-(u*u + v*v)/(std*std))/(2*math.pi*std*std)
total = total + kernel[u + k, v + k]
for u in range(-k, k + 1):
for v in range(-k, k + 1):
kernel[u + k, v + k] = kernel[u + k, v + k]/total
return kernel
def test():
'''This script will perform a unit test on your function, and provide useful
output.
'''
np.set_printoptions(precision=3)
ks = [1, 2, 1, 2, 1]
sds = [1, 2, 3, 4, 5]
outputs = []
# 1,1
y = np.array([[ 0.075, 0.124, 0.075],
[ 0.124, 0.204, 0.124],
[ 0.075, 0.124, 0.075]])
outputs.append(y)
# 2,2
y = np.array([[ 0.023, 0.034, 0.038, 0.034, 0.023],
[ 0.034, 0.049, 0.056, 0.049, 0.034],
[ 0.038, 0.056, 0.063, 0.056, 0.038],
[ 0.034, 0.049, 0.056, 0.049, 0.034],
[ 0.023, 0.034, 0.038, 0.034, 0.023]])
outputs.append(y)
# 1,3
y = np.array([[ 0.107, 0.113, 0.107],
[ 0.113, 0.120, 0.113],
[ 0.107, 0.113, 0.107]])
outputs.append(y)
# 2,4
y = np.array([[ 0.035, 0.039, 0.04 , 0.039, 0.035],
[ 0.039, 0.042, 0.044, 0.042, 0.039],
[ 0.04 , 0.044, 0.045, 0.044, 0.04 ],
[ 0.039, 0.042, 0.044, 0.042, 0.039],
[ 0.035, 0.039, 0.04 , 0.039, 0.035]])
outputs.append(y)
# 1,5
y = np.array([[ 0.11 , 0.112, 0.11 ],
[ 0.112, 0.114, 0.112],
[ 0.11 , 0.112, 0.11 ]])
outputs.append(y)
for k, sd, output in zip(ks, sds, outputs):
if __name__ == "__main__":
print "k:{}, sd:{}".format(k, sd)
usr_out = make_gaussian(k, sd)
if not type(usr_out) == type(output):
if __name__ == "__main__":
print "Error- output has type {}. Expected type is {}.".format(
type(usr_out), type(output))
return False
if not usr_out.shape == output.shape:
if __name__ == "__main__":
print "Error- output has shape {}. Expected shape is {}.".format(
usr_out.shape, output.shape)
return False
if not usr_out.dtype == output.dtype:
if __name__ == "__main__":
print "Error- output has dtype {}. Expected dtype is {}.".format(
usr_out.dtype, output.dtype)
return False
if not np.all(np.abs(usr_out - output) < .005):
if __name__ == "__main__":
print "Error- output has value:\n{}\nExpected value:\n{}".format(
usr_out, output)
return False
if __name__ == "__main__":
print "Passed."
if __name__ == "__main__":
print "Success."
return True
if __name__ == "__main__":
# Testing code
print "Performing unit test. Tests will be accepted if they are within .005 \
of the correct answer."
test()
part2.py
import sys
import os
import numpy as np
from scipy.stats import norm
import math
import random
import cv2
import run
def make_sharp(k, sd):
'''Create a sharpen kernel.
Input:
k - the radius of the kernel.
sd - the standard deviation of the gaussian filter used to make the kernel.
Output:
output - a numpy array of shape (2k+1, 2k+1) and dtype float.
The sharpen filter is constructed by first taking a filter with a 2 in the
center and 0's everywhere else, and subtracting from that a gaussian filter.
Note:
You can use the make_gaussian function from part one by typing:
import part1
part1.make_gaussian(k, sd)
'''
kernel = None
# Insert your code here.----------------------------------------------------
kernel = np.zeros((2 * k + 1, 2 * k + 1), dtype = np.float)
kernel[k, k] = 2
import part1
kernel = kernel - part1.make_gaussian(k, sd)
#---------------------------------------------------------------------------
return kernel
def test():
'''This script will perform a unit test on your function, and provide useful
output.
'''
np.set_printoptions(precision=3)
ks = [1, 2, 1, 2, 1]
sds = [1, 2, 3, 4, 5]
outputs = []
# 1,1
y = np.array([[-0.075, -0.124, -0.075],
[-0.124, 1.796, -0.124],
[-0.075, -0.124, -0.075]])
outputs.append(y)
# 2,2
y = np.array([[-0.023, -0.034, -0.038, -0.034, -0.023],
[-0.034, -0.049, -0.056, -0.049, -0.034],
[-0.038, -0.056, 1.937, -0.056, -0.038],
[-0.034, -0.049, -0.056, -0.049, -0.034],
[-0.023, -0.034, -0.038, -0.034, -0.023]])
outputs.append(y)
# 1,3
y = np.array([[-0.107, -0.113, -0.107],
[-0.113, 1.880, -0.113],
[-0.107, -0.113, -0.107]])
outputs.append(y)
# 2,4
y = np.array([[-0.035, -0.039, -0.04 , -0.039, -0.035],
[-0.039, -0.042, -0.044, -0.042, -0.039],
[-0.04 , -0.044, 1.955, -0.044, -0.04 ],
[-0.039, -0.042, -0.044, -0.042, -0.039],
[-0.035, -0.039, -0.04 , -0.039, -0.035]])
outputs.append(y)
# 1,5
y = np.array([[-0.11 , -0.112, -0.11 ],
[-0.112, 1.886, -0.112],
[-0.11 , -0.112, -0.11 ]])
outputs.append(y)
for k, sd, output in zip(ks, sds, outputs):
if __name__ == "__main__":
print "k:{}, sd:{}".format(k, sd)
usr_out = make_sharp(k, sd)
if not type(usr_out) == type(output):
if __name__ == "__main__":
print "Error- output has type {}. Expected type is {}.".format(
type(usr_out), type(output))
return False
if not usr_out.shape == output.shape:
if __name__ == "__main__":
print "Error- output has shape {}. Expected shape is {}.".format(
usr_out.shape, output.shape)
return False
if not usr_out.dtype == output.dtype:
if __name__ == "__main__":
print "Error- output has dtype {}. Expected dtype is {}.".format(
usr_out.dtype, output.dtype)
return False
if not np.all(np.abs(usr_out - output) < .005):
if __name__ == "__main__":
print "Error- output has value:\n{}\nExpected value:\n{}".format(
usr_out, output)
return False
if __name__ == "__main__":
print "Passed."
if __name__ == "__main__":
print "Success."
return True
if __name__ == "__main__":
# Testing code
print "Performing unit test. Answers will be accepted as long as they are \
within .005 of the input."
test()
import sys
import os
import numpy as np
from scipy.stats import norm
import math
import random
import cv2
import run
def filter_median(image, k):
'''Filter the image using a median kernel.
Inputs:
image - a single channel image of shape (rows, cols)
k - the radius of the neighborhood you should use (positive integer)
Output:
output - a numpy array of shape (rows - 2k, cols - 2k) and the same dtype as
image.
Each cell in the output image should be filled with the median value of the
corresponding (2k+1, 2k+1) patch in the image.
'''
output = None
# Insert your code here.----------------------------------------------------
(rows, cols) = image.shape
if (k <= 0):
raise ValueError('The k must be a postive integer')
output = np.zeros((rows - 2*k, cols - 2*k), dtype = image.dtype)
for i in range(rows - 2*k):
for j in range(cols - 2*k):
patch = np.zeros((2*k + 1, 2*k + 1), dtype = image.dtype)
for u in range(2*k + 1):
for v in range(2*k + 1):
patch[u, v] = image[i + u, j + v]
output[i, j] = np.median(patch)
#---------------------------------------------------------------------------
return output
def test():
'''This script will perform a unit test on your function, and provide useful
output.
'''
images = []
x = np.array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[ 10, 11, 12, 13, 14],
[ 15, 16, 17, 18, 19],
[ 20, 21, 22, 23, 24]], dtype = np.uint8)
images.append(x)
images.append(x)
x = np.array([[ 0, 1, 2, 3, 4, 5, 6],
[ 7, 8, 9, 10, 11, 12, 13],
[14, 15, 16, 17, 18, 19, 20],
[21, 22, 23, 24, 25, 26, 27],
[28, 29, 30, 31, 32, 33, 34],
[35, 36, 37, 38, 39, 40, 41],
[42, 43, 44, 45, 46, 47, 48]], dtype = np.uint8)
images.append(x)
images.append(x)
ks = [1, 2, 1, 2]
outputs = []
z = np.array([[ 6, 7, 8],
[11, 12, 13],
[16, 17, 18]], dtype=np.uint8)
outputs.append(z)
z = np.array([[12]], dtype=np.uint8)
outputs.append(z)
z = np.array([[ 8, 9, 10, 11, 12],
[15, 16, 17, 18, 19],
[22, 23, 24, 25, 26],
[29, 30, 31, 32, 33],
[36, 37, 38, 39, 40]], dtype=np.uint8)
outputs.append(z)
z = np.array([[16, 17, 18],
[23, 24, 25],
[30, 31, 32]], dtype=np.uint8)
outputs.append(z)
for image, k, output in zip(images, ks, outputs):
if __name__ == "__main__":
print "image:\n{}".format(image)
print "k:\n{}".format(k)
usr_out = filter_median(image, k)
if not type(usr_out) == type(output):
if __name__ == "__main__":
print "Error- output has type {}. Expected type is {}.".format(
type(usr_out), type(output))
return False
if not usr_out.shape == output.shape:
if __name__ == "__main__":
print "Error- output has shape {}. Expected shape is {}.".format(
usr_out.shape, output.shape)
return False
if not usr_out.dtype == output.dtype:
if __name__ == "__main__":
print "Error- output has dtype {}. Expected dtype is {}.".format(
usr_out.dtype, output.dtype)
return False
if not np.all(usr_out == output):
if __name__ == "__main__":
print "Error- output has value:\n{}\nExpected value:\n{}".format(
usr_out, output)
return False
if __name__ == "__main__":
print "Passed."
if __name__ == "__main__":
print "Success."
return True
if __name__ == "__main__":
# Testing code
print "Performing unit test."
test()
import sys
import os
import numpy as np
import cv2
from scipy.signal import convolve2d
from scipy.ndimage.filters import gaussian_filter
import math
import part0
import part1
import part2
import part3
import run
def sobel_filter_x():
'''Return a 3x3 sobel filter in the x direction.
'''
return np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
def sobel_filter_y():
'''Return a 3x3 sobel filter in the y direction.
'''
return np.array([[-1,-2,-1],
[ 0, 0, 0],
[ 1, 2, 1]])
def transform_xy_theta(dx, dy):
'''Transform from xy gradients to edge direction.
Input:
dx, dy - the gradient images generated by applying sobel filters to an
image. They both have shape (rows, cols) and dtype float.
Output:
theta - a numpy array of shape (rows, cols) and dtype float.
Each location theta[i,j] should contain the inverse tangent of dy[i,j]/dx[i,j]
, in the range of [-pi/2, pi/2] radiants.
Hint: you may find the np.arctan function useful here.
'''
# To avoid dividing by zero, set dy to a small value in locations where it
# is zero.
dx[dx == 0] = 0.001
theta = None
# Insert your code here -------------------------------------------------------
#------------------------------------------------------------------------------
(rowsx, colsx) = dx.shape
(rowsy, colsy) = dy.shape
if (rowsx != rowsy or colsx != colsy):
raise ValueError('dx and dy must have the same size.')
theta = np.zeros((rowsx, colsx), dtype = np.float)
for i in range(rowsx):
for j in range(colsx):
theta[i, j] = np.arctan(dy[i,j]/dx[i,j])
return theta
def transform_xy_mag(dx, dy):
'''Transform from xy gradients to edge direction.
Input:
dx, dy - the gradient images generated by applying sobel filters to an
image. They both have shape (rows, cols) and dtype float.
Output:
mag - a numpy array of shape (rows, cols) and dtype float.
Each location mag[i,j] should contain the magnitude of the gradient, which
is sqrt(dx[i,j]^2 + dy[i,j]^2)
Hint: you may find the np.sqrt and np.square funcitons useful here.
'''
mag = None
# Insert your code here -------------------------------------------------------
#------------------------------------------------------------------------------
(rowsx, colsx) = dx.shape
(rowsy, colsy) = dy.shape
if (rowsx != rowsy or colsx != colsy):
raise ValueError('dx and dy must have the same size.')
mag = np.zeros((rowsx, colsx), dtype = np.float)
for i in range(rowsx):
for j in range(colsx):
mag[i, j] = math.sqrt(dx[i,j]*dx[i,j] + dy[i,j]*dy[i,j])
return mag
def get_color(theta, mag):
'''Return the color for a given edge theta and magnitude.
Given the local edge orientation and magnitude, return the corresponding
color. The intensity of the color is given by the magnitude (stronger edges
are brighter)
'''
boundaries = np.array([0.375, 0.125, -0.125, -0.375]) * math.pi
# crop the magnitude to 0, 255 range.
if mag < 0:
mag = 0
if mag > 255:
mag = 255
# (vertical) | yellow
if theta > boundaries[0] or theta < boundaries[3] :
return (0, mag, mag)
# \ green
if theta >= boundaries[3] and theta < boundaries[2] :
return (0, mag, 0)
# -- blue
if theta >= boundaries[2] and theta < boundaries[1] :
return (mag, 0, 0)
# / red
if theta >= boundaries[1] and theta < boundaries[0] :
return (0, 0, mag)
def run_edges(image):
''' This function finds and colors all edges in the given image.
'''
# Convert image to gray
if len(image.shape) > 2:
grayimage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
else:
grayimage = image
# blur so the gradient operation is less noisy.
# uses a gaussian filter with sigma = 2
grayimage = gaussian_filter(grayimage, 2).astype(float)
# Filter with x and y sobel filters
dx = convolve2d(grayimage, sobel_filter_x())
dy = convolve2d(grayimage, sobel_filter_y())
# Convert to orientation and magnitude images
theta = transform_xy_theta(dx, dy)
mag = transform_xy_mag(dx, dy)
outimg = np.zeros((image.shape[0], image.shape[1], 3), dtype = np.uint8)
# Fill with corresponding color.
for r in range(outimg.shape[0]):
for c in range(outimg.shape[1]):
outimg[r,c,:] = get_color(theta[r,c], mag[r,c])
return outimg
def test():
'''This script will perform a unit test on your function, and provide useful
output.
'''
dxs = []
dys = []
thetas = []
mags = []
y = np.array([[ 0, 1],
[-1, 0]], dtype = float)
dys.append(y)
x = np.array([[ 1, 0],
[ 0,-1]], dtype = float)
dxs.append(x)
theta = np.array([[ 0. , math.pi/2],
[-math.pi/2, 0. ]], dtype = float)
thetas.append(theta)
mag = np.array([[ 1, 1],
[ 1, 1]], dtype = float)
mags.append(mag)
y = np.array([[ 0, 0, 0],
[ 1, 1, 1],
[-1,-1,-1]], dtype = float)
dys.append(y)
x = np.array([[ 0, 1,-1],
[ 0, 1,-1],
[ 0, 1,-1]], dtype = float)
dxs.append(x)
theta = np.array([[ 0, 0, 0],
[ math.pi/2, math.pi/4, -math.pi/4],
[-math.pi/2, -math.pi/4, math.pi/4]], dtype = float)
thetas.append(theta)
mag= np.array([[ 0, 1, 1],
[ 1, 1.414, 1.414],
[ 1, 1.414, 1.414]], dtype = float)
mags.append(mag)
for dx, dy, theta, mag in zip(dxs, dys, thetas, mags):
if __name__ == "__main__":
print "dx:\n{}\n, dy:\n{}\n".format(dx, dy)
usr_theta = transform_xy_theta(dx, dy)
usr_mag = transform_xy_mag(dx, dy)
for usr_out, true_out, name in zip((usr_theta, usr_mag), (theta, mag), ('theta', 'mag')):
if not type(usr_out) == type(true_out):
if __name__ == "__main__":
print "Error- {} has type {}. Expected type is {}.".format(
name, type(usr_out), type(true_out))
return False
if not usr_out.shape == true_out.shape:
if __name__ == "__main__":
print "Error- {} has shape {}. Expected shape is {}.".format(
name, usr_out.shape, true_out.shape)
return False
if not usr_out.dtype == true_out.dtype:
if __name__ == "__main__":
print "Error- {} has dtype {}. Expected dtype is {}.".format(
name, usr_out.dtype, true_out.dtype)
return False
if not np.all(np.abs(usr_out - true_out) < .05):
if __name__ == "__main__":
print "Error- {} has value:\n{}\nExpected value:\n{}".format(
name, usr_out, true_out)
return False
if __name__ == "__main__":
print "{} passed.".format(name)
if __name__ == "__main__":
print "Success."
return True
if __name__ == "__main__":
print "Performing unit tests. Your functions will be accepted if your result is\
within 0.05 of the correct output."
np.set_printoptions(precision=3)
if not test():
print "Unit test failed. Halting"
sys.exit()
sourcefolder = os.path.abspath(os.path.join(os.curdir, 'images', 'source'))
outfolder = os.path.abspath(os.path.join(os.curdir, 'images', 'filtered'))
print 'Searching for images in {} folder'.format(sourcefolder)
# Extensions recognized by opencv
exts = ['.bmp', '.pbm', '.pgm', '.ppm', '.sr', '.ras', '.jpeg', '.jpg',
'.jpe', '.jp2', '.tiff', '.tif', '.png']
# For every image in the source directory
for dirname, dirnames, filenames in os.walk(sourcefolder):
for filename in filenames:
name, ext = os.path.splitext(filename)
if ext in exts:
print "Reading image {}.".format(filename)
img = cv2.imread(os.path.join(dirname, filename))
print "Applying edges."
outimg = run_edges(img)
outpath = os.path.join(outfolder, name + 'edges' + ext)
print "Writing image {}.".format(outpath)
cv2.imwrite(outpath, outimg)