Digital Image Processing: Usage of Contour

Sean
NTUST-AIVC
Published in
8 min readFeb 6, 2023

Contour is a curve joining all continuous points along the boundary of a connected component and the pixels in the same connected components have the same intensity.

This article will use python as programming language and will mainly introduce the details of the function, cv2.findContours() , about how to use and what are the meaning of arguments, and what effects arguments lead to? You also can find the document in OpenCV to know the usage but I tried to make it easy and simple to understand the function. Hope this article can give you guys a favor.

Basic Introduction of cv2.findContours()

The function to find contours is based on the paper —
Suzuki,S. and Abe, K., Topological Structural Analysis of Digitized Binary Images by Border Following. CVGIP 30 1, pp 32–46 (1985) .
This border-following algorithm cannot only find out different borders ( contours ) but also extract the relation of surroundness among the borders.
In this algorithm, you should know first that there are two kinds of borders to which every contour belongs:

1. Hole border: A set of the border points between 1-components and 0-components in the condition that 1-components surround 0-components.

2. Outer border: A set of border points between 0-components and 1-components in the condition that 0-components surround 1-components.

Define 0-components as a connected component with 0-pixels and the definition of 1-components is similar to 0-components except for that the value of pixels are 1.

In other words, finding contour is like finding white objects from a black background so the source image should be a binary image so as to use the algorithm. Let us check how to use it first.

cv2.findContours() Usage

To find the contours of an image, turning a colorful image into binary is necessary because cv2.findContours() only supports 8-bit-single-channel images. You can use functions such as cv2.Canny() orcv2.Threshold() to process.

Code :

contours, hierarchy = cv2.findContours(binary_img, mode, method, offset)

The following images show the source of the image and the result with contours:

Pic 1. Source image vs Result

Now, let me give you a description to the meaning of return value and parameters:

Contours:

Continuous points of borders in the image.
In the official document, it specifies that the type of contours is std::vector<std::vector<cv::Point> > in C++. However, in python, the type, std::vector< > , will be turned to the type, list() and cv::Point will be represented by a coordinate enclosed by two list() such as
[ [180 92] ]. The element of contours are all points of a certain border. For example, if you want to gain the point of 0-border, you can give an index contours like cnt = contours[0] .

In the official document, it specifies that the type of contours is std::vector<std::vector<cv::Point> > in C++. However, in python, the type, std::vector< > , will be turned to the type, list() and cv::Point will be represented by two numbers enclosed by two list() such as
[ [180 92] ]. The element of contours are all points of a certain border. For example, if you want to gain the point of 0-border, you can give an index contours like

cnt = contours[0]

Hierarchy:

Relation of surroundness among borders.
The type of hierarchy is std::vector<cv::Vec4i> in C++ based on the official document. In python, its type is list( list( list( 4 integer ) )) . When you want to access the property of the border with the index,i , you can use like

relation = hierarchy[0][i]

The i -th hierarchy corresponds to i -th contour. For example, hierarchy[0][i] represents the characteristics of contours[i]. Then the characteristic you gained is listed below:

[Next, Previous, First_Child, Parent]

The value of those variables indicates the number of contour which meets the condition. When value equal to -1 , it means that there isn’t any border that meets the condition. I use a binary image and its tree structure of surroundness as an example. Let me start to describe them.

Pic 2. All borders & Tree structures of relation

Parent & First_child:
Assume i and j are two borders. We call i is the parent border of j when i is the different type of border from j and i surrounds j directly. In other words, i is the closet border with different type and it surrounds j . On the contrary, when i is the closet border with different type and it is surrounded by j , we call i is the child border of j.
Every border has only one corresponding parent border but it may have many child borders. Like 1-border and 3-border in Pic 2. They both are the child border of 0-border. Because of that, define the first child as the border which has a lower number than others. In Pic 2, the first child of 0-border is 1-border.

Next & Previous:
Next and Previous indicate the borders that have the same parent border as the current border. Next shows the index of the border with the next larger number. Previous shows the previous lower one. They all are in the same hierarchy and have same Parent. Remember that If there isn’t any border suitable, the variable will equal to -1.

As you can see in the above picture. 1-border and 3-border have the same parent. The next border of 1-border is 3-border. On the other hand, 1-border is the previous border of 3-border.

After the description of hierarchy, I’ll show the result of hierarchy of the above example to let you have a better understanding. Notice that hierarchy lists out properties of all borders.

hierarchy :                             
[[[ 4 -1 1 -1] # for 0 border 
[ 3 -1 2 0] # for 1 border
[-1 -1 -1 1] # for 2 border
[-1 1 -1 0] # for 3 border
[-1 0 5 -1] # for 4 border
[-1 -1 6 4] # for 5 border
[-1 -1 -1 5]]] # for 6 border

Later, we can talk about the retrieval mode of cv2.findContours() which have high relativity about hierarchy.

Retrieval mode ( mode)

There are four modes you can use when you find contours. I’ll show different results of hierarchy and their corresponding tree structures to give you a reference.

  1. RETR_LIST: Just retrieve contours and it doesn’t create any hierarchy. All contours are on the same level. Parent and First_chile are equal to -1 .
hierarchy : 
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[ 3 1 -1 -1]
[ 4 2 -1 -1]
[ 5 3 -1 -1]
[ 6 4 -1 -1]
[-1 5 -1 -1]]]
Pic 3. RETR_LIST

2. RETR_EXTERNAL: Only the outermost contours are retrieved. Their children are discarded.

hierarchy :                            
[[[ 1 -1 -1 -1]
[-1 0 -1 -1]]]
Pic 4. RETR_EXTERNAL

3. RETR_CCOMP: It creates a two-level hierarchy. The first level belongs to outer borders and hole borders are in the second level. No more levels were created. Pay attention to the outer border surrounded by hole border. The outer border will belong to the child of frame not the child of hole border.

hierarchy : 
[[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[ 5 1 3 -1]
[ 4 -1 -1 2]
[-1 3 -1 2]
[-1 2 6 -1]
[-1 -1 -1 5]]]
Pic 5. RETR_CCOMP

4. RETR_TREE: It retrieves all the contours and hierarchy which has been referred to by the above Hierarchy topic.

hierarchy : 
[[[ 4 -1 1 -1]
[ 3 -1 2 0]
[-1 -1 -1 1]
[-1 1 -1 0]
[-1 0 5 -1]
[-1 -1 6 4]
[-1 -1 -1 5]]]
Pic 6. RETR_TREE

Contour Approximation Modes ( method)

The argument, mode , will cause different methods to store border points. There are four methods listed in the official document :

  • CHAIN_APPROX_NONE,
  • CHAIN_APPROX_SIMPLE,
  • CHAIN_APPROX_TC89_L1,
  • CHAIN_APPROX_TC89_KCOS.

Here, I will briefly introduce the first two methods to you.

  1. CHAIN_APPROX_NONE: Store all points in contours.
  2. CHAIN_APPROX_SIMPLE: Compress the horizontal, vertical, and diagonal segments. It will only store their endpoints.
    It can shrink amount of border points to save memory.
Pic 7. The left side picture is the saved points with CHAIN_APPROX_NONE. The right side picture is the saved points with CHAIN_APPROX_SIMPLE.

As you can see, different methods will change the number of points needed to be saved.

Offset

This argument will make all points of contours shift with assigned pixels. The value of offset can be assigned a tuple of coordinates :

offset = (5,10) # x-axis shifts 5 pixels. y-axis shifts 10 pixels.

Conclusion

Thanks for reading. Hope the contents are helpful for you. In my will, I want to precisely summarize the contents in the official document as possible. And also make up the tree structure of different retrieval modes to make you easily realize the concepts.
Besides cv2.findContours() , there are a lot of common functions related to contour such as cv2.drawPoints(), cv2.minAreaRect(), cv2.arcLength() and so on. I think those functions are easy to understand with documentation so I just post the link here for reference.

Useful function related to contours
https://docs.opencv.org/3.4/dd/d49/tutorial_py_contour_features.html

Official documentation https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gae4156f04053c44f886e387cff0ef6e08

After reading, If you still don’t know what kind of situation we can apply contour, you can browse the following repository which is one of my projects. It’s about traffic sign identification. Maybe it can help you realize contour more.

Traffic sign identification
https://github.com/Sean053047/Traffic-Sign-Identification

--

--