第一次尝试写博客,就当做写一次随笔吧!

最近在做一个小项目,需要检测目标轮廓,而且只需要内轮廓,OpenCV自带的轮廓检测函数findContours使用起来特别方便,利用轮廓之间层级关系即可实现内轮廓查找。

findContours函数的具体使用方法自行查看相关教程和手册,下面仅对本算法相关的关键点进行一些强调和补充,如有错误,还请批评指正。

findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset=Point());

参数列表中有个数据结构参数:hierarchy(译层次结构),hierarchy是一个向量,其元素个数与查找到的轮廓总数相同,每一个元素中包含4个int类型数据hierarchy[i][0]~hierarchy[i][3]。
分别表示:

  1. 表示同一级轮廓的下个轮廓的编号,如果这一级轮廓没有下一个轮廓,则为-1。
  2. 表示同一级轮廓的上个轮廓的编号,如果这一级轮廓没有上一个轮廓,则为-1。
  3. 表示该轮廓包含的下一级轮廓的第一个的编号,假如没有,则为-1。
  4. 表示该轮廓的上一级轮廓的编号,假如没有上一级,则为-1。

具体参考博客:findContour函数详解

参数:int mode:定义轮廓的检索模式,mode不同,轮廓之间的拓扑结构就不同,具体参数定义可参考其他博客。

本文在仅检测内轮廓时使用了mode:CV_RETR_CCOMP 其检测所有的轮廓,但所有轮廓只建立两个等级关系,即外轮廓和内轮廓,如果内轮廓中还含有其他轮廓,则内轮廓内的所有轮廓均归属于外轮廓,以此类推。


程序中所用测试图像如下:

findContours函数 opencv findcontours函数用法_opencv

测试图片需要在hsv颜色空间下通过红色阈值分割得到二值化图像,对应代码段中find_color()函数,代码如下:

void find_color(const cv::Mat& inputimage, cv::Mat& outputimage)
{
	cv::Mat imageHsv;
	cv::Mat Imagemask, mask1, mask2;

	cv::cvtColor(inputimage, imageHsv, COLOR_BGR2HSV);

	cv::inRange(imageHsv, Scalar(0, 82, 45), Scalar(12, 255, 255), mask1);       
	cv::inRange(imageHsv, Scalar(162, 82, 45), Scalar(180, 255, 255), mask2);
	//merge the two mask
	Imagemask = mask1 + mask2;
	outputimage = Imagemask.clone();

}

内轮廓查找代码如下,很简单:

int main()
{
	cv::Mat dstImage, SrcImage, drawImage;
	string path = "F:\\Image_design\\circle4.png";
	
	SrcImage = imread(path);							//source image
	if (!SrcImage.data) {
		std::cout << "Could not open or find the image" << std::endl;
		return -1;
	}
	
   //hsv颜色空间提取红色部分
	find_color(SrcImage, dstImage);					  
	cv::Mat markers = dstImage.clone();
	drawImage = cv::Mat::zeros(SrcImage.size(), SrcImage.type());

	std::vector< std::vector<cv::Point> > contours;
	std::vector<cv::Vec4i> hierarchy;
    //层级关系只选择两层
	cv::findContours(markers, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);  
	for (int i = 0; i < contours.size(); i++)
	{
		if (hierarchy[i][3] != -1 && contourArea(contours[i]) > 50)
		{
			drawContours(SrcImage, contours, i, Scalar(0, 255, 0), 2);
			drawContours(drawImage, contours, i, Scalar(0, 255, 0), 1);

			cout << "向量hierarchy的第" << to_string(i) << " 个元素内容为:" << endl << hierarchy[i] << endl << endl;
		}
	}

	cv::imshow("contours", SrcImage);
	cv::waitKey(0);
	return 0;
}

代码执行效果如下:

findContours函数 opencv findcontours函数用法_颜色空间_02

(绿色即为检测到的内轮廓)总结:其实只要理解了mode:CV_RETR_CCOMP 和hierarchy的实际意义,算法的关键点就是一句话:

if (hierarchy[i][3] != -1 ) 就保存该轮廓;

findContours函数 opencv findcontours函数用法_opencv_03


(选用CV_RETR_CCOMP时,上文测试图的层级结构数据)

因为层级关系就两层,当向量hierarchy元素中最后一位数据不为-1时,表示该轮廓存在外轮廓,那么此轮廓就是内轮廓了。