This project is a direct implementation of the following paper: Canny Edge Detection
Noise reduction: Convert the image to grayscale, and perform antialiasing over the image by convolving with the Gaussian filter (the Matlab function fspecial is used).
Intensity gradient: The size of the Gaussian Kernel used for both antialiasing and computing Gaussian derivatives is not computed automatically, it is manually inputted. Magnitude and orientation of the gradient vectors are computed using the first derivatives in the horizontal and vertical direction.
Non-max Suppression: Given estimates of the image gradients, perform a search to determine if the gradient magnitude assumes a local maximum in the gradient direction.
Hysteresis: The value of the high threshold is supplied directly, and 0.4 of that value is used for the low threshold.
Images generated from left to right (for every individual example): original image, post-Gaussian convolution, gradient magnitudes, gradient directions, final image post non-maxima suppression and hysteresis. All images were produced with a Gaussian Kernal size of 5x5, sigma = 1.4, and hysteresis threshold at 0.1.
Image results using MATLAB’s native implementation of a canny edge detector (using the same σ and threshold values used in the images above):
Notice that images produced by Matlab’s native implementation filter out more noise and visualize a few more stronger and continuous edges than the image results from my implementation. That difference is likely to be a byproduct of one or both of two issues: (1) Matlab automatically computes the size of the Gaussian Kernel from σ (details unknown) as opposed to allowing a direct specification (the way my code handles it), meaning varying filter sizes. Or (2), Matlab’s default handling of hysteresis utilizes alternative threshold values other than the ones usually used in most documentation and handles it differently. My method: the low threshold is computed as 0.4 of the high threshold, which is passed in as an argument. Either way, the edges seem to be computed relatively similarly between both implementations.