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)
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: