Contour Extracion is a common image processing technique aims at indentifying the structural outline of objects in image. A contour can help in indentifying a shape of object and for such a reason, they are widely use for segmentation, text recognition, shape recognition, etc. A contour is a curved line representing the boundary of the same values or the same intensities. Before going deeper into the contour world, we have to understand the difference between edges and contours.
Edges are computed as points that are extrema of the image gradient in the direction of the gradient. In other words, they are points with values significantly different from the values of the neighbor points.
While, on the other hand, Contours are close curves that are obtained from edges representing the boundary of a figure. So, from the definition, we can easily understand that before finding the contours in an image, we have to extract the edges. To this end, we use one of the most common technique called: Canny edge detector.
Canny Edge Detector
This method has been developed by John F. Canny in 1986 and it is an edge detection operator that uses a multi-stage algorithm to detect a wide range of edges in images. The approach is based on 3 steps:
- Intensity Gradient Computation: a filter based on a Gaussian is used of computing the intensity. Moreover, by using a Gaussian approach, we reduce the noise in the image;
- Thinning Down: the edge candidates are thinned down to a 1-pixel curve by removing non-maximum pixels of the gradient magnitude;
- Hysteresis Thresholding: the useles pixels are removed by using hysteresis thresholding on the gradient magnitude.
So, we have 3 parameters to consider: (1) the size of the Gaussian filter, (2) the low and (3) hish threshold for the hysteresis thresholding.
Let’s have look at the code:
#import opencv and numpy import cv2 import numpy as np #read the image image = cv2.imread("shapes.png") #convert it to black and white gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #gaussian kernel kernel_size = 3 #low thresh low = 50 #high thresh high = 100 #Canny computation canny = cv2.Canny(gray, low, high, kernel_size) canny_color = np.concatenate((canny, canny, canny), axis=2) #concatenating the bgr image and the canny one concat_image = np.concatenate((image, canny_color), axis=1) #show the image cv2.imshow("result", concat_image) cv2.waitKey(0)
The output of the code is the following:
Now, that we have the edges, we can compute the contours.
Contour detection and extraction can be implemented by the opencv function
Such a function takes 2 important parameters as input:
- mode: indicating the way of finding contours (cv2.RETR_TREE, cv2.RETR_EXTERNAL, cv2.RETR_LIST, cv2.RETR_CCOMP);
- method: indicating the approximation method (cv2.CHAIN_APPROX_NONE, cv2.CHAIN_SIMPLe) mode is the way of finding contours, and method is the approximation method for the detection.
For more information about the 2 paramenters, you can have a look at the official documentation
Now, we are going to modify the code we used for finding the edges, in order to find the contours:
#import opencv and numpy import cv2 import numpy as np #for making random colors import random as rng rng.seed(12345) #read the image image = cv2.imread("shapes.png") ##Image preprocessing #defining the kernel size kernel_size = (3, 3) #set sigma X = 0 (sigma Y is set as 0 as default) sigmaX = 0 #apply the filter blur_image = cv2.GaussianBlur(image, kernel_size, sigmaX) #convert it to black and white gray = cv2.cvtColor(blur_image, cv2.COLOR_BGR2GRAY) #gaussian kernel kernel_size = 3 #low thresh low = 100 #high thresh high = 200 #Canny computation canny = cv2.Canny(gray, low, high, kernel_size) cv2.imshow("Canny", canny) #contour detection contours, hierarchy = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) #create a new image for drwaing all the contours contour_img = np.zeros(image.shape, dtype=np.uint8) #draw all the contours for i in range(len(contours)): color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)) cv2.drawContours(contour_img, contours, i, color, 2, cv2.LINE_8, hierarchy, 0) #concatenating the bgr image and the canny one concat_image = np.concatenate((image, contour_img), axis=1) #show the image cv2.imshow("result", concat_image) cv2.waitKey(0)
As you can see, in the code, we added a preprocessing step with a Gaussian filter in order to extract only the strongest edges.
The results of the code above is the following:
If we change the mode from cv2.RETR_TREE to cv2.RETR_EXTERNAL, we get only the external contours:
We covered one of the important topic of the classical computer vision methodology. We can use contours for many things: from shape detection to text recognition.