轮廓发现介绍
轮廓的定义
一个轮廓代表一系列的点(像素),这一系列的点构成一个有序的点集,所以可以把一个轮廓理解为一个有序的点集。
轮廓发现定义
轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。
所以边缘提取的阈值选定会影响最终轮廓发现结果
相关API
findContours发现轮廓
函数作用:
在OpenCV中,提供了一个函数返回或者输出一个有序的点集或者有序的点集的集合(指多个有序的点集),函数findContour是从二值图像中来计算轮廓的,它可以使用Canny()函数处理的图像,因为这样的图像含有边缘像素;也可以使用threshold()或者adaptiveThreshold()处理后的图像,其边缘隐含在正负区域的交界处。这个函数的声明如下:
void findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode,int method, Point offset = Point() );
函数参数:
- image:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
- contours:vector<vector>类型,是一个向量,并且是一个双重向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓。有多少轮廓,向量contours就有多少元素。
- hierarchy:vector 类型, Vec4i是Vec<int,4>的别名,即容器内每一个元素都是一个包含了4个int型变量的向量,所以从定义上看,hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。
- 补充说明:向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。
- mode:int类型的,定义轮廓的检索模式:
- method:int类型,定义轮廓的近似方法:
- Point:偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值。
注意事项:
显然,从函数名可以看出“寻找轮廓”的意思。
我们可以通过边缘检测算法得到边缘二值图或者前景二值图,二值图的边缘像素或者前景像素就可以被看出是由多个轮廓(点集)组成的。
函数findContours的作用就是将二值图的边缘像素或者前景像素拆分成多个轮廓,便于分开讨论每一个轮廓,其中参数image代表一张二值图,contours代表输出的多个轮廓。
对于该函数的C++API,对一个轮廓的描述用vector,那么多个轮廓(多个点集)如何表示呢?即参数contours是什么数据结构呢?
在C++API中,用vector<vector>描述多个轮廓,即将多个轮廓存在一个vector中。
drawContours绘制轮廓
函数功能:
OpenCV中提供了一个函数来绘制findContours所找到的多个轮廓
函数原型:
void drawContours( InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness = 1, int lineType = 8, InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point() )
其参数解释如下:
- image: 代表输入的图像矩阵,将轮廓画在该图上;
- contours:是得到的一系列点的集合,代表多个轮廓;
- contourIdx:是一个索引,代表绘制contours中的第几个轮廓;
- color:被填充的颜色,单色可以设置为Scalar(255)等;
- thickness: 所画Contour的线条粗细,如果该参数值小于0,则表示填充整个轮廓内的区域;
- lineType: 线的连通性;
- hierarchy:可选层次信息结构,这里面是findContours所的到的基于Contours的层级信息;
- maxLevel: 绘制轮廓的最大等级。如果等级为0,绘制单独的轮廓。如果为1,绘制轮廓及在其后的相同的级别下轮廓。如果值为2,所有的轮廓。如果等级为2,绘制所有同级轮廓及所有低一级轮廓,诸此种种。如果值为负数,函数不绘制同级轮廓,但会升序绘制直到级别为abs(max_level)-1的子轮廓
- offset:照给出的偏移量移动每一个轮廓点坐标.当轮廓是从某些感兴趣区域(ROI)中提取的然后需要在运算中考虑ROI偏移量时,将会用到这个参数。
代码示例
#include <iostream> #include <math.h> #include <opencv2/opencv.hpp> #include <opencv2/highgui.hpp> #include <opencv2/highgui/highgui_c.h> using namespace std; using namespace cv; Mat src, dst; const char* output_win = "findcontours-demo"; int threshold_value = 100; int threshold_max = 255; RNG rng; void Demo_Contours(int, void*); int main(int argc, char** argv) { src = imread("./test2.jpg"); if (src.empty()) { printf("could not load image...\n"); return -1; } namedWindow("input-image", CV_WINDOW_AUTOSIZE); namedWindow(output_win, CV_WINDOW_AUTOSIZE); imshow("input-image", src); cvtColor(src, src, CV_BGR2GRAY); const char* trackbar_title = "Threshold Value:"; createTrackbar(trackbar_title, output_win, &threshold_value, threshold_max, Demo_Contours); Demo_Contours(0, 0); waitKey(0); return 0; } void Demo_Contours(int, void*) { Mat canny_output; vector<vector<Point>> contours; vector<Vec4i> hierachy; Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false); imshow("canny_output", canny_output); findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); dst = Mat::zeros(src.size(), CV_8UC3); RNG rng(12345); for (size_t i = 0; i < contours.size(); i++) { Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0)); } imshow(output_win, dst); }