Finding the Edge: Canny and Sobel Detectors(Part 2)

Saisha
SRM MIC
Published in
6 min readSep 12, 2020

In the first part of the series I briefly touched upon the Canny and Sobel Edge Detectors. In part two here, I shall compare these detectors, explain the math behind it and finally we’ll see how one can code it.

Comparison of the various Edge Detection Operators

The main advantage of Sobel operator is its simplicity which is because of the approximate gradient. However, on the other hand Canny edge detection has greater computational complexity. The major drawback of Sobel operator is it’s signal to noise ratio. With the increase in noise the gradient magnitude degrades. This leads to inaccurate results.

The main purpose of the edge detector is to figure out where the edges are present. It does not focus on the thickness of the edges at all.

Let’s take an example and demonstrate how the canny edge detection algorithm is indeed powerful with a photo of this beautiful flower. Say we wish to find the edges of the petals of this flower.

The original picture-http://www.public-domain-photos.com/flowers/wet-flower-free-stock-photo-4.htm

When we apply Sobel Edge Detection it is going to detect the gradients on both the left and right side of the petal. If we have an incredibly high-resolution image of the flower the gradient is going to spread out and if we don’t, we will get a blurry edge.

Sobel Operator applied on the image

In contrast, the canny edge detector will remove all those rough blurry edges and will give you just the edge of the petal, exactly what we needed

Canny Operator applied on the image

The Canny edge detection algorithm is one of the most widely used in many computer vision and image analysis applications. It is extremely effective as well. It’s used, to name a few, are fingerprint matching, medical diagnosis, self-driving cars for lane detection, etc.

Let’s cut to the chase.

The process of this effective edge detection is broken down into:

  1. To smoothen the image we will be applying the Gaussian filter to reduce the noise in the image
  2. Find the intensity gradients of the image
  3. Apply non-maximum suppression and double threshold to determine potential edges
  4. Tracking the edges finally by hysteresis.

Gaussian filter helps in removing the noise in the image. This noise then removed enables further processing to be smooth and flawless.

The smoothened image is then filtered by Sobel Kernel vertically and horizontally to get the first derivative which is (Gx). In the vertical direction, we will get our second derivative i.e (Gy). From this, we can now find the edge gradient.

https://docs.opencv.org/trunk/da/d22/tutorial_py_canny.html

Gradient direction is always perpendicular to edges. It is rounded to one of four angles representing vertical, horizontal and two diagonal directions

Non-maximum suppression can effectively locate the edge and suppress the occurrence of false edges. Once we get the gradient magnitude and direction, a full scan of an image is done to remove any unwanted pixels which may not be considered to be an edge. For this, at every pixel, the point is checked to see if it is a local maximum in its neighborhood in the direction of the gradient. Check the image below:

https://docs.opencv.org/trunk/da/d22/tutorial_py_canny.html

Point A is on the edge ( in the vertical direction). The gradient direction is normal to the edge. Point B and C are located in the gradient direction. So point A is checked with point B and C to see if it forms a local maximum. If it does, then we can move to the next step, otherwise, it is suppressed (put to zero). The result you get is basically a binary image with thin edges.

Let’s move on to the next step.

We will now define the two thresholds: maxVal and minVal.

https://docs.opencv.org/trunk/da/d22/tutorial_py_canny.html

Now we will use the higher threshold to start the edge curves and the lower threshold to continue them. Any edges with intensity gradient more than maxVal are sure to edge and those below minVal are sure to be non-edges, so they are discarded.

The ones that lie in between them are classified based on their connectivity.

Let’s take a look at the code:

Original Image:

I have chosen a picture of a butterfly. You can try it out on any image you wish to.

The original picture -https://wsimag.com/economy-and-politics/61348-murder-and-the-monarch-butterfly

Firstly, import the necessary libraries and the image which we would be using.

import cv2 as cv
import matplotlib.pyplot as plt
image=cv.imread('./butterfly.jpg')

Let us first use the Sobel edge Operator to find the edges of the image.

img = np.array(Image.open('./butterfly.jpeg')).astype(np.uint8)# Apply gray scale
gray_img = np.round(0.299 * img[:, :, 0] +
0.587 * img[:, :, 1] +
0.114 * img[:, :, 2]).astype(np.uint8)
# Sobel Operator
h, w = gray_img.shape
# define filters
horizontal = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]) # s2
vertical = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]]) # s1
# define images with 0s
newhorizontalImage = np.zeros((h, w))
newverticalImage = np.zeros((h, w))
newgradientImage = np.zeros((h, w))
# offset by 1
for i in range(1, h - 1):
for j in range(1, w - 1):
horizontalGrad = (horizontal[0, 0] * gray_img[i - 1, j - 1]) + \
(horizontal[0, 1] * gray_img[i - 1, j]) + \
(horizontal[0, 2] * gray_img[i - 1, j + 1]) + \
(horizontal[1, 0] * gray_img[i, j - 1]) + \
(horizontal[1, 1] * gray_img[i, j]) + \
(horizontal[1, 2] * gray_img[i, j + 1]) + \
(horizontal[2, 0] * gray_img[i + 1, j - 1]) + \
(horizontal[2, 1] * gray_img[i + 1, j]) + \
(horizontal[2, 2] * gray_img[i + 1, j + 1])
newhorizontalImage[i - 1, j - 1] = abs(horizontalGrad)verticalGrad = (vertical[0, 0] * gray_img[i - 1, j - 1]) + \
(vertical[0, 1] * gray_img[i - 1, j]) + \
(vertical[0, 2] * gray_img[i - 1, j + 1]) + \
(vertical[1, 0] * gray_img[i, j - 1]) + \
(vertical[1, 1] * gray_img[i, j]) + \
(vertical[1, 2] * gray_img[i, j + 1]) + \
(vertical[2, 0] * gray_img[i + 1, j - 1]) + \
(vertical[2, 1] * gray_img[i + 1, j]) + \
(vertical[2, 2] * gray_img[i + 1, j + 1])
newverticalImage[i - 1, j - 1] = abs(verticalGrad)# Edge Magnitude
mag = np.sqrt(pow(horizontalGrad, 2.0) + pow(verticalGrad, 2.0))
newgradientImage[i - 1, j - 1] = mag
plt.figure()
plt.title('Butterfly')
plt.imsave('Butterfly.jpg', newgradientImage, cmap='gray', format='jpg')
plt.imshow(newgradientImage, cmap='gray')
plt.show()

Results:

Sobel edge operator applied to the image of the Butterfly

Now, let’s find the edges in the image using the Canny algorithm.

edges = cv.Canny(image, 100, 200, 3, L2gradient=True)
plt.figure()
plt.title('Butterfly')
plt.imsave('Butterfly.jpg', edges, cmap='gray', format='jpg')
plt.imshow(edges, cmap='gray')
plt.show()

Results:

Canny edge detection applied to the image of the Butterfly

Conclusion:

This article shows the comparison between Sobel and Canny edge detection methods. Both are very common and are popularly used operators for edge detection. Each is useful in a specific domain. Sobel is useful for applications such as heavy data transfer in the form of images and videos. While, Canny is used for object detection, and pattern matching purposes where it is necessary to retain the features even in case of noisy images. Each application has its own requirement that can be met by using the appropriate edge detection algorithm.

You can refer to the following links below:

https://aip.scitation.org/doi/pdf/10.1063/1.5005213

https://ieeexplore.ieee.org/document/8265710

--

--