47_Opencv中的轮廓查找 cv::findContours()

一个轮廓对应一系列点,这些点以某种方式表示图像中的一条曲线。OpenCV中,轮廓用标准模板库向量vector<>表示,最常见的是用一系列二维顶点(vector<cv::Point>或vector<cv::Point2f>表示。

函数cv::findContours()从二维图像中计算轮廓,它处理的图像可以是从cv::Canny()函数得到的有边缘像素的图像,或是从cv::threshold()及cv::adaptiveThreshold()函数得到的二值图像。

1. 查找轮廓 cv::findContours

cv::findContours()函数原型:

void cv::findContours(
	cv::InputOutputArray image,  // input binary 8-bit single channel
	cv::InputOutputArrayOfArrays contours,  // vector of vectors or points
	cv::OutputArray hierarchy,  // (optional) topology information
	int mode,  // contour retrieval mode
	int method,  // approximation method
	cv::Point offset = cv::Point()  // (optional) offset every point
);

void cv::findContours(
	cv::InputOutputArray image,  // input binary 8-bit single channel
	cv::OutputArrayOfArrays contours,  // vector of vectors or pointd
	int mode,  // contour retrieval mode
	int method,  // approximation method
	cv::Point offset = cv::Point()  // (optional) offset every point;
);

参数image是输入图像,必须是8位单通道图像,应该被转化成二值的。cv::findContours()函数会改变该参数,所以如果该图像将来还有用,应该复制之后再传给cv::findContours()。

参数contours是一组数组,多数情况下是一个或多个标准模板库vector。该参数是找到的轮廓。例如在一个轮廓vector中,contours[i]是一条轮廓,而contours[i][j]是轮廓contours[i]上的一个点。

参数hierarchy是可选项,如果给出了该参数,hierarchy 将输出所有轮廓的树结构。这个参数是一个数组,每条轮廓对应数组中的一个值。数组中的每个值都是一个四元数组,每个元素代表一个与当前结点有特定链接的结点,每个元素代表的含义如下:

索引含义
0同级的下一条轮廓
1同级的前一条轮廓
2下级的第一个子结点
3上级的父节点

参数mode表示期望的轮廓提取方式,有4种:

  • cv::RETR_EXTERNAL:只检索最外层轮廓,并且该轮廓不与其他轮廓连接。
  • cv::RETR_LIST:检索所有轮廓并保存到表中。
  • cv::RETR_CCOMP:检索所有的轮廓,并将他们组织成双层结构。
  • cv::RETR_TREE:检索所有轮廓并重新建立网状轮廓结构。

参数method表示轮廓如何表达,可选择的方式有:

  • cv::CHAIN_APPROX_NONE:将轮廓编码中的所有点转换为点,这个操作会产生大量的点,每个点都将成为前一个点的8个邻点之一,不会减少返回的点数。
  • cv::CHAIN_APPROX_SIMPLE:压缩水平、垂直、斜的部分,只保留最后一个点,许多特殊情况下,这一操作将大大减少返回的点数。极端例子是,对于一个沿着x-y方向的矩形,只返回4个点。
  • cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC89_KCOS:使用Teh-chin链逼近算法中的一个。Teh-Chin算法是一种更复杂且计算密集型的算法,用于减少返回的点数。运行T-C不需要额外的参数。

参数offset是可选项,如果给出这个参数,返回的轮廓中所有的点会根据参数值发生偏移。通常用于两种情况下:一当希望从兴趣区域中提取的轮廓用原图坐标系表达时,二当希望从原图中提取的轮廓用图像子区域坐标系表达时。

2. 绘制轮廓 cv::drawContours

查找完轮廓后最常用的功能是在屏幕上绘制检测到的轮廓,可以使用函数cv::drawContours()函数完成。函数原型:

void cv::drawContours(
	cv::InputOutputArray image,  // will draw on input image
	cv::InputArrayOfArrays contours,  // vector of vectors or pointd
	int contourIdx,  // contour to draw (-1 is all)
	const cv::Scalar &color,  // color for contours
	int thickness = 1,  // thickness for contour lines
	int lineType = 8,  // connectedness ('4' or '8')
	cv::InputArray hierarchy = cv::noArray(),  // optional from find contours
	int maxLevel = INT_MAX,  // max descent in hierarchy
	cv::Point offset = cv::Point()  // (optional) offset all points
);

参数image,待绘制轮廓的图像。

参数contour是要绘制的轮廓,该参数的类型与cv::findContours()的输出contour相同,是储存在列表中的点。

参数contourIdx用于告诉cv::drawContours()需要绘制的是contours参数中的某一条轮廓还是全部轮廓,如果是一个正数,则对应的轮廓将被绘制,如果为负数,所有轮廓都将被绘制。

参数color、thickness、lineType的功能与其他用于绘制的函数中的对应参数功能相同,分别表示绘制的颜色,绘制的线的粗细,绘制线的类型(四联通/八连通/AA线)。

参数hierarchy对应cv::findContours()函数输出的层次。参数hierarchy和参数maxLevel共同起作用。maxLevel限制将在图上绘制的轮廓层次深度,maxLevel=0表示只绘制第0层的轮廓,设为其他非0正数,表示绘制最高层以下相同数量层级的轮廓。如果希望在连接成分时只显示最外层轮廓,很有帮助。

参数offset可选,当轮廓坐标系被转换成执行坐标系或其他局部坐标系的时候,这个特性很有用。

使用VS2010+opencv2.4.9时,32位测试时总是会崩溃,提示堆被破坏,并且函数得到的轮廓也有问题,用64位不会崩溃,得到的轮廓也是对的。

3. 使用实例:查找轮廓并逐条绘制

#include <opencv.hpp>
#include <algorithm>
#include <iostream>
#include <stdio.h>

using namespace std;
using namespace cv;

struct AreaCmp{
	AreaCmp(const vector<float>& _areas):areas(&_areas){}
	bool operator()(int a,int b) const {return (*areas)[a] > (*areas)[b];}
	const vector<float>* areas;
};

int main(int argc, char *argv[])
{
	Mat img,img_edge,img_color;
	img = cv::imread(argv[1],cv::IMREAD_GRAYSCALE);
	if(img.empty())
	{
		std::cout << "Load image fail," << argv[1] << std::endl;
		getc(stdin);
		return -1;
	}
	cv::threshold(img,img_edge,128,255,cv::THRESH_BINARY);
	cv::namedWindow("Image after threshold",cv::WINDOW_NORMAL);
	cv::imshow("Image after threshold",img_edge);
	std::vector< std::vector<cv::Point> > contours;  // 修正 由cv::vector -> std::vector
	vector<cv::Vec4i> hierarchy;

    // 查找轮廓
	cv::findContours(img_edge,contours,hierarchy,cv::RETR_LIST,cv::CHAIN_APPROX_SIMPLE);
	cout << "\nTotal contours detected:" << contours.size() << endl;

	vector<int> sortIdx(contours.size());
	vector<float> areas(contours.size());
	for(int n=0;n<(int)contours.size();n++)
	{
		sortIdx[n] = n;
		areas[n] = contourArea(contours[n],false);  // 计算轮廓面积
	}

	// sort contours so that largest contours go first
	std::sort(sortIdx.begin(),sortIdx.end(),AreaCmp(areas));

	for(int n=0;n<(int)sortIdx.size();n++)
	{
		int idx = sortIdx[n];
		cv::cvtColor(img,img_color,cv::COLOR_GRAY2BGR);
        // 绘制轮廓
		cv::drawContours(img_color,contours,idx,cv::Scalar(0,0,255),2,8,hierarchy,0);  // 逐条绘制轮廓
		cout << "Contour #" << idx << ": area=" << areas[idx] << ", nvertices=" << contours[idx].size() << endl;
		cv::namedWindow(argv[0],cv::WINDOW_NORMAL);
		cv::imshow(argv[0],img_color);
		int k;
		if((k = cv::waitKey()&255) == 27)
			break;
	}

	std::cout << "Finished all contours\n" ;
	getc(stdin);
	return 0;
}

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值