Canny Edge Detector Using Python

Edge detection is an essential image analysis technique when someone is interested in recognizing objects by their outlines, and it’s also considered an essential step in recovering information from images. 

For instance, edge detection can extract important features like lines and curves, which are then normally used by higher-level computer vision or image-processing algorithms. A good edge-detection algorithm would highlight the locations of major edges in an image, while at the same time ignoring any false edges caused by noise. 

But what are edges anyway? Edges are image features that can be used in estimating and analyzing the structure of objects in an image. They represent significant local changes in the image intensity (i.e. pixel value). Edges normally occur on the boundary between two different regions in the image.

In this tutorial, I’m going to describe the Canny edge detector algorithm and show you how we can implement it in Python.

Canny Edge Detector

The Canny edge detector algorithm is named after its inventor, John F. Canny, who invented the algorithm in 1986. The Canny edge detector normally takes a grayscale image as input and produces an image showing the location of intensity discontinuities as output (i.e. edges).

I don’t want to go mathematical here, but I will describe what’s going on behind the scenes in the Canny edge detector algorithm from a high-level viewpoint.

The first thing the Canny edge detector does is that it uses Gaussian convolution to smooth the input image and remove noise. A first derivative operator is then applied to the smoothed image to highlight those regions of the image with high first spatial derivatives.

The algorithm then finds both the gradient magnitude and direction by calculating the x-derivative and the y-derivative, especially since knowing the direction of the gradient enables us to find the direction of the edges.

The algorithm then performs what’s called non-maximal suppression, where it tracks along the top of the ridges that rise from the edges and sets those pixels that are not on the ridge top to zero, eventually producing a thin line in the result. 

In other words, we check if the gradient calculated in the previous step is considered the maximum among the neighboring points lying in both the positive and negative directions of the gradient. If the gradient was the maximum, it is considered to be part of the edge, and vice versa.

The tracking process above is controlled by two thresholds, t1 and t2, such that t1>t2, referred to as hysteresis thresholding. Tracking begins at a point on the ridge higher than t1, and then continues in both of the directions out of that point until the height of the ridge becomes less than t2

So, basically, what happens here is that we select all the edge points that are above the upper threshold t1, and then investigate if there are neighbors of these points which are considered below the upper threshold t1 and above the lower threshold t2. In this case, such neighbors would be part of the edge.

Thus, the width of the Gaussian kernel used for smoothing the input image, and the t1 (upper) and t2 (lower) thresholds used by the tracker, are the parameters that determine the effect of the canny edge detector.

Python Implementation

In this section, I will describe two ways in which we can implement the Canny edge detector. One way uses the scikit-image library, and the other uses the OpenCV library.

Canny Edge Detector Using scikit-image

If you don’t have scikit-image already installed on your machine, go ahead and install it by following the instructions shown on the installing scikit-image page.

As I’m using an Ubuntu machine, I simply had to run the following command in my Terminal to get the library up and running:

1
sudo apt-get install python-skimage

The scikit-image library has a canny() function which we can use to apply the Canny edge detector on our image. Notice that the function is part of the feature module.

Before moving forward, let’s use a toy image to experiment with. You can use any image, though. I’m going to use the boat.png image shown below:

Original Boat ImageOriginal Boat ImageOriginal Boat Image

Without further ado, let’s see how we can detect the edges in the above image (i.e. boat) using the Canny edge detector. Remember that our image needs to be grayscale. Since our image is already grayscale, we don’t need to do anything at this point, such as converting the image from color to grayscale. The script for the Canny edge detector looks as follows:

1
from skimage import io
2
from skimage import feature
3
4
im = io.imread('boat.png')
5
edges = feature.canny(im)
6
io.imshow(edges)
7
io.show()

As you can see, we first read our image, boat.png. After that, we apply the canny() function on the image (I didn’t pass any custom parameters, except our image, and left it at the function’s defaults). Finally, we display our result that shows the detected edges. The result of the above script looks as follows:

scikit_canny_edge_detectorscikit_canny_edge_detectorscikit_canny_edge_detector

You can play around with the parameters to get different results on how edges are detected. But the result looks nice with those detected edges, doesn’t it?

Canny Edge Detector Using OpenCV

In this section, we are going to see how we can use OpenCV to apply the Canny edge detector on our boat image. If you don’t have OpenCV installed yet, go ahead and install it. You can check out the following articles on how you can install OpenCV on your machine. I have included different articles for different operating systems:

As with the scikit-image library, OpenCV also has a function called canny() to apply the Canny edge detector algorithm on the image. The following script shows how we can use OpenCV to find the edges in our image:

1
import cv2
2
import matplotlib.pyplot as plt
3
4
im = cv2.imread('boat.png')
5
edges = cv2.Canny(im,25,255,L2gradient=False)
6
plt.imshow(edges,cmap='gray')
7
plt.show()

Notice that I have passed the following as arguments to the Canny() function:

  • im: image name
  • lower threshold: 25
  • upper threshold: 255
  • L2gradient=False: this means that the L1-norm is used. If set to True, the L2-norm will be used.

The matplotlib library has then been used to plot the results. To learn more about this library, check out my tutorial: Introducing Python’s Matplotlib Library

The result of the above script is as follows:

opencv_canny_edge_detectoropencv_canny_edge_detectoropencv_canny_edge_detector

Conclusion

In this tutorial, we have learned about the Canny edge detector and seen how the scikit-image and OpenCV libraries enable us to easily implement this detector with a few lines of code.