本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
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()
);
参数详解:
InputArray image
:输入图像,需为8-bit单通道图像。因为算法是基于二值图像进行轮廓查找,所以这里将像素值不为0的视为1,像素值为0的视为0。OutputArrayOfArrays contours
:输出找到的轮廓。每个轮廓上的点都分别存为一个vector,例如:std::vector<std::vector<cv::Point> >
。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个轮廓没有前一个、后一个、子或父轮廓,则对应的值会是一个负值。
int mode
:轮廓的检索模式,共有5种:RETR_EXTERNAL=0
RETR_LIST=1
RETR_CCOMP=2
RETR_TREE=3
RETR_FLOODFILL=4
int method
:定义轮廓的近似方法。共有4种:CHAIN_APPROX_NONE=1
CHAIN_APPROX_SIMPLE=2
CHAIN_APPROX_TC89_L1=3
CHAIN_APPROX_TC89_KCOS=4
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=3
和CHAIN_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()
);
参数详解:
InputOutputArray image
:在此图像上绘制轮廓。InputArrayOfArrays contours
:检测到的轮廓。int contourIdx
:指定要绘制的轮廓的序号。如果为负值,则绘制所有的轮廓。const Scalar& color
:绘制轮廓所用的颜色。int thickness = 1
:绘制轮廓所用的线宽。int lineType = LINE_8
:绘制轮廓所用的线类型。InputArray hierarchy = noArray()
:即cv::findContours
输出的hierarchy。int maxLevel = INT_MAX
:限制所绘制轮廓的级别。例如maxLevel=3,则只绘制第0,1,2级的轮廓。如果maxLevel=0,则绘制某一特定序号的轮廓。Point offset = Point()
:轮廓的偏移量。
如果参数int thickness
<0,则填充轮廓包围的部分: