【OpenCV基础】第二十六课:轮廓发现

轮廓发现,cv::findContours,cv::drawContours

Posted by x-jeff on December 16, 2021

本文为原创文章,未经本人允许,禁止转载。转载请注明出处。

1.轮廓发现

轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现结果。

2.相关API

2.1.cv::findContours

该API所基于的算法来自论文“Topological Structural Analysis of Digitized Binary Images by Border Following”:原文博客讲解

1
2
3
4
5
6
7
8
void findContours( 
	InputArray image, 
	OutputArrayOfArrays contours,
	OutputArray hierarchy, 
	int mode,
	int method, 
	Point offset = Point()
);

参数详解:

  1. InputArray image:输入图像,需为8-bit单通道图像。因为算法是基于二值图像进行轮廓查找,所以这里将像素值不为0的视为1,像素值为0的视为0。
  2. OutputArrayOfArrays contours:输出找到的轮廓。每个轮廓上的点都分别存为一个vector,例如:std::vector<std::vector<cv::Point> >
  3. OutputArray hierarchy:输出图像的拓扑结构,类型为std::vector<cv::Vec4i>Vec4i即为Vec<int,4>),和第2个参数中的轮廓是一一对应的。
    • hierarchy[i][0]:第i个轮廓的后一个轮廓。
    • hierarchy[i][1]:第i个轮廓的前一个轮廓。
    • hierarchy[i][2]:第i个轮廓的子轮廓。
    • hierarchy[i][3]:第i个轮廓的父轮廓。

    如果第i个轮廓没有前一个、后一个、子或父轮廓,则对应的值会是一个负值。

  4. int mode:轮廓的检索模式,共有5种:
    • RETR_EXTERNAL=0
    • RETR_LIST=1
    • RETR_CCOMP=2
    • RETR_TREE=3
    • RETR_FLOODFILL=4
  5. int method:定义轮廓的近似方法。共有4种:
    • CHAIN_APPROX_NONE=1
    • CHAIN_APPROX_SIMPLE=2
    • CHAIN_APPROX_TC89_L1=3
    • CHAIN_APPROX_TC89_KCOS=4
  6. Point offset:偏移量。所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且offset还可以是负值。这一参数在使用ROI时非常有用。

2.1.1.参数OutputArray hierarchy详解

以上图为例,一共存在8个轮廓:

且按照包围关系,这8个轮廓处于不同等级,例如:

  • 第0级:0,6,7。
  • 第1级:1。
  • 第2级:2。
  • 第3级:3。
  • 第4级:4,5。

输出的hierarchy为:

1
2
3
4
5
6
7
8
0 : 6 -11-1
1 : -1 -120
2 : -1 -131
3 : -1 -142
4 : 5 -1-13
5 : -1 4-13
6 : 7 0-1-1
7 : -1 6-1-1

以第一行为例进行解释,轮廓0位于第0级:

  • 其下一个轮廓(必须为同一级)为轮廓6(按照序号大小寻找)。
  • 其上一个轮廓(必须为同一级)没有,所以为-1。
  • 其子轮廓为轮廓1。
  • 其没有父轮廓,所以为-1。

2.1.2.参数int mode详解

👉RETR_EXTERNAL=0:只检测最外围的轮廓。

1
2
3
4
//hierarchy:
0 : 1 -1-1-1
1 : 2 0-1-1
2 : -1 1-1-1

👉RETR_LIST=1:检测所有的轮廓,但是不建立等级关系。这就意味着这个模式下不存在父轮廓和子轮廓,即hierarchy[i][2]hierarchy[i][3]均为-1。

1
2
3
4
5
6
7
8
9
//hierarchy:
0 : 1 -1-1-1
1 : 2 0-1-1
2 : 3 1-1-1
3 : 4 2-1-1
4 : 5 3-1-1
5 : 6 4-1-1
6 : 7 5-1-1
7 : -1 6-1-1

👉RETR_CCOMP=2:检测所有的轮廓,但只建立两个等级关系,即原论文中提到的外边界和洞边界

上图中,外边界(第0级)有6,7,4,2,0,1;洞边界(第1级)有5,3。

1
2
3
4
5
6
7
8
9
//hierarchy:
0 : 1 -1-1-1
1 : 2 0-1-1
2 : 4 13-1
3 : -1 -1-12
4 : 6 25-1
5 : -1 -1-14
6 : 7 4-1-1
7 : -1 6-1-1

因为外边界为第0级,所以不存在父轮廓。

👉RETR_TREE=3:输出结果见2.1.1部分。

2.1.3.参数int method详解

👉CHAIN_APPROX_NONE=1:保存边界上所有连续的轮廓点到参数OutputArrayOfArrays contours

👉CHAIN_APPROX_SIMPLE=2:仅保存轮廓的拐点到参数OutputArrayOfArrays contours

👉CHAIN_APPROX_TC89_L1=3CHAIN_APPROX_TC89_KCOS=4引自论文:C. -. Teh and R. T. Chin, “On the detection of dominant points on digital curves,” in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 11, no. 8, pp. 859-872, Aug. 1989, doi: 10.1109/34.31447.。在此不再详述。

2.2.cv::drawContours

cv::drawContours通常搭配cv::findContours一起使用,用于将检测的轮廓绘制出来。

1
2
3
4
5
6
7
8
9
10
11
void drawContours( 
	InputOutputArray image, 
	InputArrayOfArrays contours,
	int contourIdx, 
	const Scalar& color,
	int thickness = 1, 
	int lineType = LINE_8,
	InputArray hierarchy = noArray(),
	int maxLevel = INT_MAX, 
	Point offset = Point() 
);

参数详解:

  1. InputOutputArray image:在此图像上绘制轮廓。
  2. InputArrayOfArrays contours:检测到的轮廓。
  3. int contourIdx:指定要绘制的轮廓的序号。如果为负值,则绘制所有的轮廓。
  4. const Scalar& color:绘制轮廓所用的颜色。
  5. int thickness = 1:绘制轮廓所用的线宽。
  6. int lineType = LINE_8:绘制轮廓所用的线类型。
  7. InputArray hierarchy = noArray():即cv::findContours输出的hierarchy。
  8. int maxLevel = INT_MAX:限制所绘制轮廓的级别。例如maxLevel=3,则只绘制第0,1,2级的轮廓。如果maxLevel=0,则绘制某一特定序号的轮廓。
  9. Point offset = Point():轮廓的偏移量。

如果参数int thickness<0,则填充轮廓包围的部分:

3.代码地址

  1. 轮廓发现

4.参考资料

  1. findContours函数参数详解
  2. opencv cv.findContours 函数详解 图像轮廓层级 图像轮廓检索方式详解