【小白】Open-CV 学习笔记 - 8.1 findContours()与drawContours()

查找图像的轮廓在图像处理及应用中扮演着重要的角色。那么什么是轮廓(contour)?《Learning OpenCV 3》中给出的定义是这样的——轮廓即是以某种方式表示图像中的曲线的点的列表。这种表示可以根据实际的情形不同而不同。表示一条曲线的方式有很多种。OpenCV中,轮廓是由STL风格的vector<>模板对象表示的,其中vector中的每个元素都编码了曲线上,下一点的位置信息。查找图像轮廓的函数是cv::findContours(),并通过cv::drawContours()将查找到的轮廓绘制到图像上

1.轮廓的查找——cv::findContours()

函数cv::findContour是从二值图像中来计算轮廓的,它可以使用cv::Canny()函数处理的图像,因为这样的图像含有边缘像素;也可以使用cv::threshold()或者cv::adaptiveThreshold()处理后的图像,其边缘隐含在正负区域的交界处。

findContours()的具体调用有两种方式,函数原型:

void findContours(
		InputOutputArray image, // 输入的8位单通道“二值”图像
		OutputArrayOfArrays contours, // 包含points的vectors的vector
		OutputArray hierarchy, // (可选) 拓扑信息
		int mode, // 轮廓检索模式
		int method, // 近似方法
		cv::Point offset = Point() // (可选) 所有点的偏移
	);
void findContours(
		InputOutputArray image, // 输入的8位单通道“二值”图像
		OutputArrayOfArrays contours, // 包含points的vectors的vector
		int mode, // 轮廓检索模式
		int method, // 近似方法
		Point offset = Point() //  (可选) 所有点的偏移
	);

参数解释

  • 第一个参数:image,图像必须为8-bit单通道图像,可以是灰度图,但更常用的是二值图像,图像中的非零像素将被视为10像素保留其像素值,故加载图像后会自动转换为二值图像。如果第四个参数mode为cv::RETR_CCOMP或cv::RETR_FLOODFILL,输入图像可以是32-bit整型图像(CV_32SC1)

  • 第二个参数:contours,检测到的轮廓,每个轮廓都是以点向量的形式进行存储即使用point类型的vector表示 ,定义为“vector<vector> contours”,是一个向量,向量内每个元素保存了一组由连续的Point点构成的点的集合的向量,一个元素包含一组Point点集,就是一个轮廓。 有多少轮廓,向量contours就有多少元素,例如contours[i]表示一条轮廓,而contours[i][j]表示contours[i]这条轮廓上的一个点Point。

  • 第三个参数:hierarchy,定义为“vector hierarchy”,hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。向量hiararchy内的元素和轮廓向量contours内的元素是一一对应的,向量的容量相同。每个轮廓contours[i]对应hierarchy中hierarchy[i][0]~hierarchy[i][3],,分别表示轮廓contours[i]的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为默认值-1。

  • 第四个参数:int型的mode,轮廓检索模式,将用何种方式来对轮廓进行提取,有四个可选的值::

    1. 取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1

    2. 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓, 所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1。

    3. 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层

    4. 取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

  • 第五个参数:int型的method,定义轮廓的近似方法,即轮廓如何呈现的方法,有三种可选的方法:

    1. 取值一:CV_CHAIN_APPROX_NONE 获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1 ,保存物体边界上所有连续的轮廓点到contours向量内

    2. 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours 向量内,拐点与拐点之间直线段上的信息点不予保留,压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息

    3. 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

  • 第六个参数:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值。

2. 轮廓的绘制——drawContours()

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:输入输出图像,即要绘制轮廓的图像,Mat类型即可

  • contours:使用findContours检测到的所有轮廓数据,每个轮廓被保存成一个point向量的形式存储,point类型的vector

  • contourIdx:指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓

  • color:绘制轮廓所用的颜色

  • thickness:绘制轮廓所用线条粗细度,如果值为负值,则在轮廓内部绘制 ,轮廓内部被填充

  • lineTpye:线条类型,轮廓线的邻域模式('4’邻域 或 '8’邻域)有默认值LINE_8,有如下可选类型
    在这里插入图片描述

  • hierarchy:可选层次结构信息 , (从 findContours得到)
    maxLevel= INT_MAX,//绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
    maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
    maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
    maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点用于绘制轮廓的最大等级

  • offset:可选轮廓便宜参数,用制定偏移量offset=(dx, dy)给出绘制轮廓的偏移量

3.实例

#include<opencv2\opencv.hpp>
#include<algorithm>
#include<iostream>
using namespace std;
using namespace cv;
int g_threshold = 50;
Mat  gray_src,binary_src;
void trackbar(int, void*) {
	threshold( gray_src,binary_src, g_threshold, 255, THRESH_BINARY);
	imshow("binary_src", binary_src);
	vector<vector<Point>> contours;
	findContours(binary_src, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
	binary_src = Scalar::all(0);
	drawContours(binary_src, contours, -1, Scalar::all(255));
	imshow("contours", binary_src);
	//使用Hu矩进行匹配
	//double comres;
	//comres = matchShapes(contours[0], contours[1], CV_CONTOURS_MATCH_I1, 0.0);
	//cout<<contours.size()<<"CV_CONTOURS_MATCH_I1 比较结果是"<< comres<<endl;
}
int main() {
	gray_src = imread("133.png", 0);
	if (gray_src.empty())
		cout << "read failed" << endl;
	namedWindow("gray_src", 1);
	createTrackbar("threshold", "gray_src", &g_threshold, 255, trackbar);
	//trackbar(0, 0);
	imshow("gray_src", gray_src);
		waitKey();
 
}

在这里插入图片描述