Canny Edge Detection

Canny Edge Detection

Canny is multi-step algorithm used for highlighting the shape of objects on an image. 

The basic idea is that the gradient of pixel value is high at a border.

step 1.

It firstly applies a 5x5 Gaussian filter to remove background noise by a certain level. This basically blurs the image a bit.

step 2.

It applies a Sobel kernel to calculate the Intensity Gradient of the Image and also the direction of gradient.

The direction obviously is perpendicular to edges.

step 3.

Non-maximum suppression.

The gradient values along the gradient direction are checked. Only the local max value (appears at the edge) is retained.

the other pixels are reset to 0.

step 4.

Hysteresis Thresholding

A gradient larger than a high threshold is considered as on an edge for sure. 

A gradient lower than a low threshold is considered as not on an edge for sure.

Anything between the low and high thresholds depends on if it is connected to an edge pixel. If yes, it is considered on the edge

too, otherwise, it is not.

For different images, the low and high thresholds are different because the pixel color distributions are different.

A adaptive way is Otsu's method, which derives the low and high thresholds by maximizing inter-class variance between the foreground

and background pixel classes.

Usually, it is better to run a 5x5 GaussianBlur to blur the image before running the Otsu's algorithm.

    img = Image.open('..../sample.jpg').convert('RGB')

    img_array = np.array(img)

    

    #convert to gray scale image

    gray = cv2.cvtColor(img_array, cv2.COLOR_BGR2GRAY)

    

    #median blur to remove repetitive noise e.g. texture of a background, this might be redundant as the Gaussian blur below solves it too

# aperture size = 5. the aperture block (window) is 5 x 5

    blur = cv2.medianBlur(gray, 5)

    

    #apply a 5x5 gaussian filter to smooth image

    blur = cv2.GaussianBlur(blur, (5,5), 0)

    

    #apply Otsu's algorithm to calculate thresholds for a later Canny operation

#high_thresh is the threshold, thresh_img is the binary image

# use both cv2.THRESH_BINARY and cv2.THRESH_OTSU transformation

    high_thresh, thresh_img = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)    

    low_thresh = 0.5 * high_thresh

    

       

    #apply Canny's algorithm to find edges

    edges = cv2.Canny(blur, low_thresh, high_thresh)

    

    #convert to Pil image  

    edges = Image.fromarray(edges)

    #bounding box of non-zero area

    bbox = edges.getbbox()

    #crop the bounding box area

    trim = img.crop(bbox)