The main content of this article comes from the image processing part of OpenCV in the OpenCV-Python tutorial . The main content of this part is as follows:
-
Learn to change images between different color spaces. Also learn to track colorful objects in videos.
-
Geometric transformation of images
Learn to apply different geometric transformations to images, such as rotation, translation, etc.
-
Learn to convert images into binary images using global thresholding, adaptive thresholding, Otsu's binarization, and more.
-
Learn to blur images, filter images with custom kernels, and more.
-
Understand morphological transformations such as erosion, expansion, opening, closing, etc.
-
Learn to find image gradients, edges, and more.
-
Learn to find edges with Canny edge detection.
-
Learn about image pyramids and how to use them for image blending.
-
All about contours in OpenCV.
-
All about histograms in OpenCV.
-
Image transformation in OpenCV
Different image transformations are encountered in OpenCV, such as Fourier transform, cosine transform, etc.
-
Learn to use template matching to search for objects in images.
-
Learn to detect lines in an image.
-
Learn to detect circles in an image.
-
Image segmentation using watershed algorithm
Learn to segment images using the watershed segmentation algorithm.
-
Interactive foreground extraction using GrabCut algorithm
Learn to use GrabCut algorithm to extract foreground
Target
In this chapter we will learn about
- The concept of Canny edge detection
- Function provided for this in OpenCV: cv.Canny()
theory
Canny edge detection is a popular edge detection algorithm. It was developed by John F. Canny
- This is a multi-stage algorithm and we will go through each stage.
- Noise Reduction
Since edge detection is susceptible to noise in the image, the first step is to use a 5x5 Gaussian filter to remove the noise in the image. We have seen this in previous chapters. - Find the intensity gradient of the image
and then filter the smoothed image using the Sobel kernel in the horizontal and vertical directions to obtain the horizontal direction ( G x G_xGx) and vertical direction ( G y G_yGy) first derivative. From these two images, we can find the edge gradient and direction of each pixel as follows:
E dge _ G radient ( G ) = G x 2 + G y 2 A ngle ( θ ) = tan − 1 ( G y G x ) Edge\_Gradient \; (G) = \sqrt{G_x^2 + G_y^2} \\ Angle \; (\theta) = \tan^{-1} \bigg(\frac{G_y} {G_x}\bigg)Edge_Gradient(G)=Gx2+Gy2Angle( i )=tan−1(GxGy)
gradient direction is always perpendicular to the edge. It is rounded to one of four angles representing vertical, horizontal, and two diagonal directions. - Non-maximum suppression
After obtaining the gradient magnitude and direction, a full scan of the image is performed to remove any unwanted pixels that may not constitute an edge. To do this, at each pixel, it is checked whether the pixel is a local maximum in its neighborhood along the gradient direction. Check the image below:
Point A is on the edge (vertical direction). The gradient direction is perpendicular to the edge. Points B and C are in the gradient direction. So point A is checked together with points B and C to see if it is a local maximum. If it is, it will be considered for the next stage, otherwise, it will be suppressed (zeroed).
In short, the result we get is a binary image with "thin edges". - Hysteresis Threshold
This stage determines which all edges are real edges and which are not. For this we need two thresholds,minVal
andmaxVal
. AnymaxVal
edge with an intensity gradient greater than is definitely an edge, and anyminVal
edge with an intensity gradient below is definitely a non-edge and therefore discarded. Those lying between these two thresholds are classified as edge or non-edge based on their connectivity. If they are connected to "definite edge" pixels, they are considered part of the edge. Otherwise, they are also discarded. See the image below:
Edge A ismaxVal
above and therefore considered a "definite edge". Even though edge C ismaxVal
below , it is connected to edge A and is therefore considered a valid edge, and we get the complete curve. But edge B, even though it isminVal
above and in the same area as edge C, is not connected to any "definite edge", so it is discarded.minVal
Therefore, it is very important that we choose and accordinglymaxVal
to get the correct results.
This stage also removes small pixel noise under the assumption that edges are long lines.
So what we end up with are strong edges in the image.
Canny edge detection in OpenCV
OpenCV puts all of the above into one function, cv.Canny() . We'll see how to use it. The first parameter is our input image. The second and third parameters are our minVal
and respectively maxVal
. The fourth parameter is aperture_size
. It is the size of the Sobel kernel used to find the gradient of the image. Its default is 3. The last parameter is L2gradient, which specifies the equation for finding the gradient magnitude. If it is True
, it uses the more precise equation mentioned above, otherwise it uses this function: E dge _ G radient ( G ) = ∣ G x ∣ + ∣ G y ∣ Edge\_Gradient \; (G) = |G_x | + |G_y|Edge_Gradient(G)=∣Gx∣+∣Gy∣ . Its default value isFalse
.
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def canary_edge_detection():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('messi5.jpg'))
edges = cv.Canny(img, 100, 200)
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edges, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
canary_edge_detection()
Let’s look at the following results:
Other resources
-
Canny edge detection in Wikipedia
-
Canny edge detection tutorial by Bill Green, 2002.
practise
Write a small application to find Canny edge detection, the threshold of which can be changed using the two track bars. This way, you can understand the impact of the threshold.
Reference documentation
Done.