162 lines
6.8 KiB
Python
162 lines
6.8 KiB
Python
import pandas as pd
|
|
import matplotlib as plt
|
|
import numpy as np
|
|
import scipy.misc as spm
|
|
import scipy.ndimage as ndi
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from scipy.interpolate import BSpline
|
|
from scipy.spatial.distance import cdist
|
|
from sklearn import cluster
|
|
from skimage import color,io
|
|
from skimage.filters import threshold_otsu,threshold_multiotsu,prewitt,sobel
|
|
from skimage.data import camera
|
|
from skimage.util import compare_images
|
|
import numpy as np
|
|
import matplotlib.pylab as plt
|
|
import matplotlib.pyplot as plt
|
|
from skimage import color, io
|
|
from scipy.ndimage.filters import convolve
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class CannyAlgorithm:
|
|
def __init__(self):
|
|
#Input gaussian filter to smooth the input image
|
|
self.h = 1/1115* np.array([[1,4,7,10,7,4,1],
|
|
[4,12,26,33,26,12,4],
|
|
[7,26,55,71,55,26,7],
|
|
[10,33,71,91,71,33,10],
|
|
[7,26,55,71,55,26,7],
|
|
[4,12,26,33,26,12,4],
|
|
[1,4,7,10,7,4,1]])
|
|
#Load grayscale image
|
|
self.grayscale = color.rgb2gray(io.imread('./horse1-2.jpg'))*255
|
|
|
|
def gaussian_filter(self):
|
|
"""[Apply gaussian filter to image for smoothing purposes and add 0 padding to matrix]
|
|
"""
|
|
logger.info("Creating smoothed matrix from applying gaussian filter")
|
|
return np.pad(convolve(self.grayscale,self.h), ((1,1),(1,1)),'constant')
|
|
|
|
def compute_partial_derivatives(self, s_i1_j: float, s_i_j: float, s_i1_j1: float, s_i_j1:float):
|
|
"""[Compute partial derivate for pixel with respect to x]
|
|
|
|
Args:
|
|
si_j1 (float): [Pixel directly to the east in image matrix]
|
|
s_i_j (float): [Current pixel]
|
|
s_i1_j1 (float): [Pixel directly in south-east coordinate of image in relation to pixel]
|
|
s_i1_j (float): [Pixel directly south of pixel image matrix]
|
|
"""
|
|
deriv_x = 1/2*(s_i1_j-s_i_j+s_i1_j1-s_i_j1)
|
|
deriv_y = 1/2*(s_i_j1-s_i_j+s_i1_j1-s_i1_j)
|
|
return deriv_x, deriv_y
|
|
|
|
def compute_gradients(self, S: np.ndarray):
|
|
"""[Iterate through each pixel and calculate the gradient direction and magnitute]
|
|
|
|
Args:
|
|
S (np.ndarray): [Smoothed image S representing the output of the grayscale image after
|
|
gaussian filter has been applied]
|
|
"""
|
|
logger.info("Computing the gradient magnitude matrices G and theta, respectively")
|
|
#Create gradient magnitute array
|
|
G = np.zeros(S.shape)
|
|
#Create theta for gradient direction
|
|
theta = np.zeros(S.shape)
|
|
#Iterate through each pixel and calculate the magnitute and direction
|
|
for i in range(1,S.shape[0]-1):
|
|
for j in range(1,S.shape[1]-1):
|
|
s_i_j = S[i,j]
|
|
s_i1_j1 = S[i+1,j+1]
|
|
s_i1_j = S[i+1,j]
|
|
s_i_j1 = S[i,j+1]
|
|
#Compute partial derivative for wrt to x and y
|
|
x,y = self.compute_partial_derivatives(s_i1_j, s_i_j, s_i1_j1, s_i_j1)
|
|
#Compute gradient magnitude
|
|
G[i,j] = np.sqrt((x**2)+(y**2))
|
|
#Compute gradient direction
|
|
theta[i,j] = np.arctan2(x,y)
|
|
plt.imsave('q3_G.png', G, cmap=plt.cm.gray)
|
|
plt.imsave('q3_theta.png', theta, cmap=plt.cm.gray)
|
|
return G, theta
|
|
|
|
|
|
def compute_nonmaximal_suppresion(self, G: np.ndarray, theta: np.ndarray):
|
|
"""[Nonmaximal suppression method]
|
|
|
|
Args:
|
|
G (np.ndarray): [Gradient magnitude matrix]
|
|
theta (np.ndarray): [Gradient direction matrix]
|
|
"""
|
|
#Create gradient magnitute array
|
|
phi = np.zeros(theta.shape)
|
|
#Iterate through each pixel and calculate the magnitute and direction
|
|
logger.info("Computing non-max suppression phi output")
|
|
for i in range(1,theta.shape[0]-1):
|
|
for j in range(1,theta.shape[1]-1):
|
|
#Iterate through each row in theta
|
|
if (-1/8*np.pi < theta[i,j] <= 1/8*np.pi):
|
|
i_1 = (i,j-1)
|
|
i_2 = (i,j+1)
|
|
elif (1/8*np.pi < theta[i,j] <= 3/8*np.pi):
|
|
i_1 = (i+1,j-1)
|
|
i_2 = (i-1,j+1)
|
|
elif (-3/8*np.pi < theta[i,j] <= -1/8*np.pi):
|
|
i_1 = (i-1,j-1)
|
|
i_2 = (i+1,j+1)
|
|
elif ((3/8*np.pi < theta[i,j] <= 1/2*np.pi) or (-1/2*np.pi < theta[i,j] <= -3/8*np.pi)):
|
|
i_1 = (i-1,j)
|
|
i_2 = (i+1,j)
|
|
elif G[i,j] >= G[i_1] and G[i,j] >= G[i_2]:
|
|
phi[i,j]=G[i,j]
|
|
plt.imsave('q3_phi.png', phi, cmap=plt.cm.gray)
|
|
return phi
|
|
|
|
def hysterisis(self, non_max_supress: np.ndarray):
|
|
"""[Run threshold with hysterisis to mitigate streaking]
|
|
|
|
Args:
|
|
G (np.ndarray): [description]
|
|
theta (np.ndarray): [description]
|
|
"""
|
|
#Create gradient magnitute array
|
|
logger.info("Computing the hysterisis step and outputting the final edge image.")
|
|
edge_image = np.zeros(non_max_supress.shape)
|
|
tau_1, tau_2 = 3, 8
|
|
|
|
#Iterate through each pixel and calculate the magnitute and direction
|
|
for i in range(1,non_max_supress.shape[0]-1):
|
|
for j in range(1,non_max_supress.shape[1]-1):
|
|
|
|
if non_max_supress[i,j] >= tau_2 and edge_image[i,j]==0:
|
|
edge_image[i,j]=1
|
|
elif non_max_supress[i,j] >= tau_1 and edge_image[i,j]==0:
|
|
neighbors = []
|
|
neighbors.append(edge_image[i-1,j-1])
|
|
neighbors.append(edge_image[i-1,j])
|
|
neighbors.append(edge_image[i-1,j+1])
|
|
neighbors.append(edge_image[i,j+1])
|
|
neighbors.append(edge_image[i+1,j+1])
|
|
neighbors.append(edge_image[i+1,j])
|
|
neighbors.append(edge_image[i+1,j-1])
|
|
neighbors.append(edge_image[i,j-1])
|
|
|
|
if 1 in neighbors:
|
|
edge_image[i,j]=1
|
|
plt.imsave('q3_edge_image.png', edge_image, cmap=plt.cm.gray)
|
|
return edge_image
|
|
|
|
|
|
if __name__ in "__main__":
|
|
logging.basicConfig(level=logging.INFO)
|
|
canny = CannyAlgorithm()
|
|
#Apply gaussian filter to input image in order to smooth
|
|
smooth_image = canny.gaussian_filter()
|
|
#Compute gradient direction and magnitute vectors
|
|
G, theta = canny.compute_gradients(smooth_image)
|
|
#Non-max suppression
|
|
non_max_suppression = canny.compute_nonmaximal_suppresion(G, theta)
|
|
#Calculate hysterisis
|
|
edge_image = canny.hysterisis(non_max_suppression) |