edge detection code pushed

Edge detection using sobel filtering
master
mehulnagpurkar 2019-02-09 16:22:04 +11:00
parent 6241a58c27
commit 81224815f9
5 changed files with 250 additions and 0 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,250 @@
'''
author: mehulnagpurkar
Edge detection is a technique in computer graphics that attempts to find the boundaries of objects in digital images.
The code will take a greyscale image as input and produce a black and white edge image as output.
Theory:
The Sobel filter approximates the magnitude of the local intensity gradient at each pixel coordinate.
Edges are identified when the gradient goes above a given threshold parameter.
The filter employs two 3 × 3 kernels to calculate the gradient at each image coordinate;
one approximates the gradient across rows,
the other across columns.
The row and column gradients are then combined to approximate the maximum magnitude of the intensity gradient.
This computation is applied at each coordinate in the input image in a process known as a convolution.
The magnitude of the gradient can be understood as a score of edge strength.
The larger the gradient the more confident we are of there being an edge at the corresponding coordinate.
The final output image is produced by applying a threshold to the gradient.
If the gradient is above the threshold then the corresponding output pixel is white, otherwise it is black.
'''
from math import *
from PIL import Image
from copy import deepcopy
def read_image(filename):
"""read_image(filename) -> list of lists of pixel intensities
Output image is rectangular, grey-scale, 8 bits per pixel,
in row major coordinates.
"""
image = Image.open(filename)
# Convert image to 8 bit per pixel grey-scale
# if it is not already in that format.
if image.mode != 'L':
image = image.convert('L')
assert (image.mode == 'L')
pixels = list(image.getdata())
# Convert the flat representation into a list of rows.
width, height = image.size
rows_cols = []
for row in range(height):
this_row = []
row_offset = row * width
for col in range(width):
this_row.append(pixels[row_offset + col])
rows_cols.append(this_row)
return rows_cols
def write_image(image, filename):
"""write_image(image, filename) -> None
Writes image data file to filename.
Input image must be rectangular, grey-scale, 8 bits per pixel,
in row major coordinates.
"""
flat_pixels = []
for row in image:
flat_pixels += row
out_image = Image.new('L', (get_width(image), get_height(image)))
out_image.putdata(flat_pixels)
out_image.save(filename)
def get_width(image):
"""get_width(image) -> integer width of the image (number of columns).
Input image must be rectangular list of lists. The width is
taken to be the length of the first row of pixels. If the image is
empty, then the width is defined to be 0.
"""
if len(image) == 0:
return 0
else:
return len(image[0])
def get_height(image):
"""get_height(image) -> integer height of the image (number of rows).
Input image must be rectangular list of lists. The height is
taken to be the number of rows.
"""
return len(image)
'''
We will address this problem by assuming that the pixels on the very boundary of the image are duplicated in imaginary
rows and columns just outside the image.
This can be achieved by clamping all row and column coordinates referred to by the kernels to ensure that they always
stay within the image bounds.
Any coordinate which goes outside the image bounds can be mapped to its nearest in-bounds neighbour.
'''
def clamp(val, lower_bound, upper_bound):
return max(min(val, upper_bound), lower_bound)
def get_pixel(image, row, col):
max_row = get_height(image) - 1
max_col = get_width(image) - 1
new_row = clamp(row, 0, max_row)
new_col = clamp(col, 0, max_col)
return image[new_row][new_col]
def gradient_row(image, row, col):
"""
Implement a Python function gradient_row(image, row, col) that computes an approximation to
the row gradient at the coordinate (row, col) in image.
:param image: user wants to process
:param row: row number
:param col: column number
:return: partial differential of the row gradient
"""
count = 0
gradient = 0
matrix = [-1, 0, 1]
kernel = ((1, 2, 1), (0, 0, 0), (-1, -2, -1))
# Maximum length and width of the image
length = get_height(image) - 1
width = get_width(image) - 1
for n in matrix:
for m in matrix:
if (0 < row < length) and (0 < col < width):
gradient += image[row + n][col + m] * kernel[count][m + 1]
else:
gradient += get_pixel(image, row + n, col + m) * kernel[count][m + 1]
count += 1
return gradient
def gradient_column(image, row, col):
"""
Implement a Python function gradient_column(image, row, col) that computes an approximation to
the column gradient at the coordinate (row, col) in image.
:param image: user wants to process
:param row: row number
:param col: column number
:return: partial differential of the column gradient
"""
count = 0
gradient = 0
col_matrix = [1, 0, -1]
kernel = ((1, 2, 1), (0, 0, 0), (-1, -2, -1))
# Maximum length and width of the image
length = get_height(image) - 1
width = get_width(image) - 1
for n in col_matrix:
for m in col_matrix:
if (0 < row < length) and (0 < col < width):
gradient += image[row + m][col + n] * kernel[count][m + 1]
else:
gradient += get_pixel(image, row + m, col + n) * kernel[count][m + 1]
count += 1
return gradient
def gradient_magnitude(image, row, col):
"""
Implement a Python function gradient_magnitude(image, row, col) that computes an approximation
to the gradient magnitude at the coordinate (row, col) in image.
:return: gradient magnitude approximation
"""
return sqrt((gradient_row(image, row, col)**2)+(gradient_column(image, row, col)**2))
def gradient_threshold(image, row, col, threshold):
"""
Implement a Python function gradient_threshold(image, row, col, threshold) that computes the edge pixel intensity
at the coordinate (row, col) in image, for the given threshold value.
:param threshold: threshold value for filtering
:return: 255 (True) if the gradient is above the threshold, 0 (False) otherwise
"""
return 255 if gradient_magnitude(image, row, col) > threshold else 0
def convolute(image, threshold):
"""
Implement a Python function convolute(image, threshold) that computes an edge image
for the input image and threshold. The output edge image must have the same number of rows and columns
as the input image. All intensity values in the output edge image must be either 0 or 255.
Your implementation of convolute must not modify the input image,
instead it must construct a completely new image as output.
:return: new convoluted image as output
"""
# Maximum length and width of the image
length = get_height(image)
width = get_width(image)
convolute_image = deepcopy(image)
for row in range(length):
for col in range(width):
convolute_image[row][col] = gradient_threshold(image, row, col, threshold)
return convolute_image
def edge_detect(in_filename, out_filename, threshold=200):
"""
The function edge_detect an be used to test your implementation of convolute on real image files
:param in_filename: input picture (png)
:param out_filename: input picture (png)
:param threshold: filter value (default = 200)
:return: writes the new filtered image
"""
in_image = read_image(in_filename)
out_image = convolute(in_image, threshold)
write_image(out_image,out_filename+'_'+str(threshold)+'.png')
def main():
# Sobel Filter
matrix = [-1, 0, 1]
kernel = ((1, 2, 1), (0, 0, 0), (-1, -2, -1))
col_matrix = [1, 0, -1]
# Running the edge detection code
edge_detect('batman.png', 'batman_edge', 50)
main()