C++ cv::findContours()函数的用法(附带实例)
在 OpenCV 中通过使用 findContours() 函数,只需几个简单步骤就可以检测出物体的轮廓,十分方便。
findContours() 函数声明如下:
另外,绘制轮廓函数 drawContours 经常和查找轮廓函数 findContours 联合使用。这个好理解,查找出轮廓后,通常需要把轮廓绘制出来。函数 drawContours 声明如下:
下面用效果图对比一下 findContours 函数中各参数取不同值时,向量 contours 和 hierarchy 的内容如何变化,以及有何异同。
【实例】对比 findContours 函数的不同效果。
1) 新建一个控制台工程,工程名是 test。
2) 打开 main.cpp,输入如下代码:
我们先绘制出 contours 向量内所有的像素点,然后调用 drawContours 绘制出轮廓。通过调整第 4 个参数 mode(轮廓的检索模式)、第 5 个参数 method(轮廓的近似方式)以及不同的偏移量 Point(),就可以得到不同的效果。
3) 保存工程并运行,结果说明如下:
① 当输入 2 时,mode 取值 RETR_EXTERNAL,method 取值 CHAIN_APPROX_NONE,即只检测最外层轮廓,内层的轮廓被忽略,并且保存轮廓上的所有点。轮廓如下图所示。
contours 向量内所有点集如下图所示。Contours向量中保存了所有轮廓上的所有点,图像表现得跟轮廓一致。
hierarchy 向量如下图所示:
重温一下 hierarchy 向量,向量中每个元素的 4 个整型变量(hierarchy[i][0]、hiearchy[i][1]、hiearchy[i][2] 和 hiearchy[i][3])分别表示与当前轮廓平级的后一个轮廓的索引编号、与当前轮廓平级的前一个轮廓的索引编号、当前轮廓的子轮廓的索引编号和当前轮廓的父轮廓的索引编号。如果没有对应项,则 hierarchy[i][x] = -1。该参数配置下,hierarchy 向量内有 3 个元素,分别对应 3 个轮廓。以第 2 个轮廓(对应向量内第 1 个元素)为例,内容为 [2,0,-1,-1],2 表示当前轮廓的后一个轮廓的编号为 2,0 表示当前轮廓的前一个轮廓编号为 0,其后两个 -1 表示为空,因为只有最外层轮廓这一个等级,所以不存在父轮廓和内嵌轮廓。
② 当输入 3 时,mode 取值 RETR_LIST,method 取值 CHAIN_APPROX_SIMPLE,即检测所有轮廓,但各轮廓之间彼此独立,不建立等级关系,并且仅保存轮廓上的拐点信息。轮廓如下图所示。
contours 向量内所有点集如下图所示。contours 向量中所有的拐点信息得到了保留,但是拐点与拐点之间直线段的部分被省略了。
hierarchy 向量(截取一部分)如下图所示:
该参数配置下,检测出了较多轮廓。第 1 个、第 2 个整型值分别指向上一个和下一个轮廓编号。由于本次配置 mode 取值 RETR_LIST,各轮廓间各自独立,不建立等级关系,因此第 3 个、第 4 个整型参数为空,值设为 -1。
③ 当输入 4 时,mode 取值 RETR_TREE,method 取值 CHAIN_APPROX_NONE,即检测所有轮廓,轮廓间建立外层、内层的等级关系,并且保存轮廓上的所有点。轮廓如下图所示。
contours 向量内所有点集如下图所示。所有内外层轮廓都被检测到,contours 点集组成的图形跟轮廓表现一致。
hierarchy 向量(截取一部分)如下图所示:
该参数配置要求检测所有轮廓,每个轮廓都被划分等级,包括最外围、第一内围、第二内围等,所以除第 1 和个最后一个轮廓外,其他轮廓都具有不为 -1 的第 3 个、第 4 个整型参数,分别指向当前轮廓的父轮廓、内嵌轮廓的索引编号。
findContours() 函数声明如下:
void cv::findContours (InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point() );参数说明如下:
- image:表示单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像。
- contours:表示轮廓的集合,用于输出轮廓集,该参数是一个向量,并且是一个双重向量,即向量内每个元素保存了一组由连续的 Point 点构成的集合向量,每一组 Point 点集就是一个轮廓,有多少轮廓,向量 contours 就有多少元素。
- hierarchy:表示可选的输出向量,包含所抽取的图片拓扑信息,有多少条轮廓,hierarchy 中就存放多少个元素。对于存储在 contours 中的第 i 条轮廓 contours[i],向量容器 hierarchy 中的元素 hierarchy[i][0]、hiearchy[i][1]、hiearchy[i][2] 和 hiearchy[i][3] 分别表示与当前轮廓平级的后一个轮廓的索引编号、与当前轮廓平级的前一个轮廓的索引编号、当前轮廓的子轮廓的索引编号、当前轮廓的父轮廓的索引编号。如果没有对应项,则 hierarchy[i][x] =-1。
-
mode:表示轮廓提取模式,取值如下:
- CV_RETR_EXTERNAL:只提取最外层的轮廓,内围轮廓将被忽略。
- CV_RETR_LIST:检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立。这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以 hierarchy 向量内所有元素的第 3、第 4 个分量都会被置为 -1。
- CV_RETR_CCOMP:提取所有轮廓,并且将其组织为两层的层级结构:顶层为连通域的外围边界,次层为洞的内层边界。如果在内层洞中还有其他的轮廓,那么这个内层洞的轮廓也将被存储到顶层中。
- CV_RETR_TREE:提取所有轮廓,并且构建一个嵌套式的轮廓存储。
-
method:定义轮廓的近似方法,取值如下:
- CV_CHAIN_APPROX_NONE:保存物体边界上所有连续的轮廓点到 contours 向量内。
- CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点存入 contours 向量内,拐点与拐点之间直线段上的信息点不予保留。
- CV_CHAIN_APPROX_TC89_L1:使用 teh-Chinl chain 近似算法。
- CV_CHAIN_APPROX_TC89_KCOS:使用 teh-Chinl chain 近似算法。
- offset:表示点的偏移量,即所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且 Point 可以是负值。
另外,绘制轮廓函数 drawContours 经常和查找轮廓函数 findContours 联合使用。这个好理解,查找出轮廓后,通常需要把轮廓绘制出来。函数 drawContours 声明如下:
void cv::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());各个参数的含义如下:
- image 表示目标图像;
- contours 表示输入的轮廓组,每一组轮廓由点 vector 构成;
- contourIdx 指明画第几个轮廓,如果该参数为负值,就画全部轮廓;
- color 为轮廓的颜色;
- thickness 为轮廓的线宽,如果为负值或 CV_FILLED,就表示填充轮廓内部;
- lineType 为线型;
- hierarchy 为轮廓结构信息;
- maxLevel 表示绘制轮廓的最高级别,这个参数只在 hierarchy 有效的时候才有效。当 maxLevel=0 时,绘制与输入轮廓同一等级的所有轮廓,即绘制输入轮廓和与其相邻的轮廓;当 maxLevel=1 时,绘制与输入轮廓同一等级的所有轮廓与其子节点;当 maxLevel=2 时,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点。参数 offset 表示可选轮廓偏移参数。
下面用效果图对比一下 findContours 函数中各参数取不同值时,向量 contours 和 hierarchy 的内容如何变化,以及有何异同。
【实例】对比 findContours 函数的不同效果。
1) 新建一个控制台工程,工程名是 test。
2) 打开 main.cpp,输入如下代码:
#include <opencv2/opencv.hpp> using namespace cv; #include<iostream> using namespace std; #include <opencv2/highgui/highgui_c.h> #include <opencv2/core.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <iostream> #include <stdio.h> int main(int argc, char *argv[]) { int op; printf("enter 1--4:"); scanf_s("%d", &op); Mat imageSource = imread("test.jpg", 0); imshow("Source Image", imageSource); Mat image; GaussianBlur(imageSource, image, Size(3, 3), 0); Canny(image, image, 100, 250); vector<vector<Point>> contours; vector<Vec4i> hierarchy; if(op==1) findContours(image, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point()); else if(op==2) findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point()); else if(op==3) findContours(image, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point()); else if(op==4) findContours(image, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point()); Mat imageContours = Mat::zeros(image.size(), CV_8UC1); // 绘制 Mat Contours = Mat::zeros(image.size(), CV_8UC1); // 绘制 for (int i = 0; i < contours.size(); i++) { // contours[i]代表的是第 i 个轮廓, contours[i].size()代表的是第 i 个轮廓上所有的像素点数 for (int j = 0; j < contours[i].size(); j++) { // 绘制出 contours 向量内所有的像素点 Point P = Point(contours[i][j].x, contours[i][j].y); Contours.at<uchar>(P) = 255; } // 输出 hierarchy 向量内容 char sz[256]; sprintf(sz, "The %d-th element of Vector hierarchy:", i); string str = sz; cout << str << endl << hierarchy[i] << endl << endl; } // 绘制轮廓 drawContours(imageContours, contours, i, Scalar(255), 1, 8, hierarchy); imshow("Contours Image", imageContours); // 轮廓 imshow("Point of Contours", Contours); // 向量 contours 内保存的所有轮廓点集 waitKey(0); return 0; }在上述代码中,我们分别设置了 4 种不同参数的 findContours 调用,根据用户输入的 1~4 来决定执行哪个 findContours。findContours 调用后轮廓就查找出来了。
我们先绘制出 contours 向量内所有的像素点,然后调用 drawContours 绘制出轮廓。通过调整第 4 个参数 mode(轮廓的检索模式)、第 5 个参数 method(轮廓的近似方式)以及不同的偏移量 Point(),就可以得到不同的效果。
3) 保存工程并运行,结果说明如下:
① 当输入 2 时,mode 取值 RETR_EXTERNAL,method 取值 CHAIN_APPROX_NONE,即只检测最外层轮廓,内层的轮廓被忽略,并且保存轮廓上的所有点。轮廓如下图所示。

contours 向量内所有点集如下图所示。Contours向量中保存了所有轮廓上的所有点,图像表现得跟轮廓一致。

hierarchy 向量如下图所示:

重温一下 hierarchy 向量,向量中每个元素的 4 个整型变量(hierarchy[i][0]、hiearchy[i][1]、hiearchy[i][2] 和 hiearchy[i][3])分别表示与当前轮廓平级的后一个轮廓的索引编号、与当前轮廓平级的前一个轮廓的索引编号、当前轮廓的子轮廓的索引编号和当前轮廓的父轮廓的索引编号。如果没有对应项,则 hierarchy[i][x] = -1。该参数配置下,hierarchy 向量内有 3 个元素,分别对应 3 个轮廓。以第 2 个轮廓(对应向量内第 1 个元素)为例,内容为 [2,0,-1,-1],2 表示当前轮廓的后一个轮廓的编号为 2,0 表示当前轮廓的前一个轮廓编号为 0,其后两个 -1 表示为空,因为只有最外层轮廓这一个等级,所以不存在父轮廓和内嵌轮廓。
② 当输入 3 时,mode 取值 RETR_LIST,method 取值 CHAIN_APPROX_SIMPLE,即检测所有轮廓,但各轮廓之间彼此独立,不建立等级关系,并且仅保存轮廓上的拐点信息。轮廓如下图所示。

contours 向量内所有点集如下图所示。contours 向量中所有的拐点信息得到了保留,但是拐点与拐点之间直线段的部分被省略了。

hierarchy 向量(截取一部分)如下图所示:

该参数配置下,检测出了较多轮廓。第 1 个、第 2 个整型值分别指向上一个和下一个轮廓编号。由于本次配置 mode 取值 RETR_LIST,各轮廓间各自独立,不建立等级关系,因此第 3 个、第 4 个整型参数为空,值设为 -1。
③ 当输入 4 时,mode 取值 RETR_TREE,method 取值 CHAIN_APPROX_NONE,即检测所有轮廓,轮廓间建立外层、内层的等级关系,并且保存轮廓上的所有点。轮廓如下图所示。

contours 向量内所有点集如下图所示。所有内外层轮廓都被检测到,contours 点集组成的图形跟轮廓表现一致。

hierarchy 向量(截取一部分)如下图所示:

该参数配置要求检测所有轮廓,每个轮廓都被划分等级,包括最外围、第一内围、第二内围等,所以除第 1 和个最后一个轮廓外,其他轮廓都具有不为 -1 的第 3 个、第 4 个整型参数,分别指向当前轮廓的父轮廓、内嵌轮廓的索引编号。