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

686 Views Asked by At

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.stack.imgur.com/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)
1

There are 1 best solutions below

0
Rotem On BEST ANSWER

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