pythonimage-processingedge-detectioncanny-operatorsobel

Canny vs Sobel. Why Canny is not able to detect edges?


I am having a 16 bit image from which I am trying to detect edges. I converted the image to 8 bit and then applied edge detection. I am getting edges using Sobel, however, Canny doesn't detect edges.

Why does this happen ?

The original image is attached here.(https://i.sstatic.net/uY0KI.png)

The code is given below:

from skimage import feature
from skimage import filters
#Read image
image = cv2.imread("Path_to_img",0)
edge_sobel = filters.sobel(image)
edges_canny = feature.canny(image, sigma=0)

Solution

  • The issue with the Canny edge detector is related to the fact that the original image has a very low "dynamic range".

    image.min() = 9
    image.max() = 15
    The dynamic range is only 15-9 = 6 (up to 7 levels of gray).

    The Canny has two optional threshold arguments, described here:

    low_threshold : float, optional
    Lower bound for hysteresis thresholding (linking edges).
    If None, low_threshold is set to 10% of dtype's max.
    high_threshold : float, optional
    Upper bound for hysteresis thresholding (linking edges).
    If None, high_threshold is set to 20% of dtype's max.

    According to the documentation, defaults for uint8 type are:
    low_threshold = 255*0.1 = 25.5 and high_threshold= 255*0.2 = 51.

    Since the pixel levels range of the image is only 6 (and not 255), none of the edges passes the threshold, and no edge is detected.


    We may compute drange and set low_threshold = drange*0.1 and high_threshold = drange*0.2:

    drange = image.max() - image.min()  # 6
    edges_canny = feature.canny(image, sigma=1.5, low_threshold=drange*0.1, high_threshold=drange*0.2)
    

    An alternative solution is applying cv2.normalize before Canny (linear "stretching" the image to range [0, 255]):

    norm_image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX)
    edges_canny_norm = feature.canny(norm_image, sigma=1.5)
    

    Code sample:

    import cv2
    from skimage import feature
    from skimage import filters
    #Read image
    image = cv2.imread("Path_to_img.png", 0)
    edge_sobel = filters.sobel(image)
    
    drange = image.max() - image.min()  # 6 (the image has only 7 levels of grays)
    edges_canny = feature.canny(image, sigma=1.5, low_threshold=drange*0.1, high_threshold=drange*0.2)
    
    norm_image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX)
    #edges_canny_norm = feature.canny(norm_image, sigma=1.5)
    cv2.imshow('norm_image', norm_image)
    cv2.imshow('edges_canny', edges_canny.astype('uint8')*255)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    Result:

    enter image description here