OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标

OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标

1、寻找轮廓

声明:在寻找图像轮廓之前需要对图像进行阈值分割或者Canny、拉普拉斯等边缘检测算子处理。

寻找轮廓的算子:

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

(1)单通道图像,可以是灰度图,常用的是二值图像;

(2)contours是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素。其定义为“vector<vector<Point>> contours”;

(3)hierarchy也是一个向量,向量内每个元素保存了一个包含4个int整型的数组。其定义为“vector<Vec4i> hierarchy”,其中<Vec4i>是Vec<int,4>的别名,定义了一个“向量内每一个元素包含了4个int型变量”的向量。

        hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第 i个轮廓的一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。如果当前轮廓没有对应的后一个轮廓、前一个轮廓、父轮廓或内嵌轮廓的话,则hierarchy[i][0] ~hierarchy[i][3]的相应位被设置为 默认值-1。

(4)轮廓的检索方式,opencv提供了四种方法,解释如下:

                 CV_RETR_EXTERNAL:只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略;

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

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

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

(5)轮廓的近似方法,opencv提供了四种方法,解释如下:

                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 近似算法。

(6)Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,并且Point还可以是负值!

详细解释可参考: findContours函数参数详解

2、绘制轮廓

绘制轮廓的算子:

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() );

 (1)目标图像;

(2)所有的输入轮廓。每个轮廓都被存储为一个点向量,其定义与findContours的参数2相同;

(3)指示要绘制哪条轮廓的索引数。如果它是负的,就画出所有的轮廓线;

(4)指定绘制轮廓的颜色;

(5)指定轮廓线的粗细。如果它是负的,则绘制轮廓内部(经测试,为负值情况下轮廓内部完全被涂成设定的轮廓线颜色);

(6)指定轮廓线的类型,参考LineTypes,一般设定为8,即LINE_8;

(7)层次结构的可选信息。只在你想画某些指定的轮廓的时候才需要进行设置;

(8)绘制轮廓的最大数量。如果它是0,则只绘制指定的轮廓线。如果为1,则绘制轮廓线和所有嵌套轮廓线。如果它是2,则绘制轮廓线、所有嵌套轮廓线、所有嵌套到嵌套轮廓线等。该参数只有在存在层次结构时才被考虑在内。(与轮廓检索方式即findContours的参数4有关

(9)轮廓点的偏移量。

3、提取轮廓重心坐标

说明:形状轮廓重心坐标的提取主要是利用图像的矩来计算。

什么是图像的矩?把图像的像素看做密度函数f(x,y),对该像素点求期望E,即是图像的矩(原点矩)。公式如下:

原点矩:

 图像的矩主要表征了图像区域的几何特征,又称几何矩,由于具有旋转、平移、尺度等不变的特兴奋,所以又称为不变矩。一般来说,一阶矩零阶矩可以计算某个形状的重心,二阶矩可以计算形状的方向

关于矩以及图像矩的详细解释可以参考:矩、中心矩、质心、patch方向

提取重心坐标方法:

			//声明一个图像的矩
			Moments M;
			//计算要绘制轮廓的矩
			M = moments(contours[i]);
			//求取轮廓重心的X坐标
			double cX = double(M.m10 / M.m00);
			//求取轮廓重心的Y坐标
			double cY = double(M.m01 / M.m00);

关于m10、m01、m00的解释:

关于重心坐标的求解过程的详细解释可以参考: 中心距求解

4、示例

示例代码:

	Mat img = imread("C:\\Users\\SUNSONG\\Desktop\\图片\\Image_02.PNG");
	Mat gray_img, thresh_img;
	//灰度
	cvtColor(img, gray_img, COLOR_BGR2GRAY);
	threshold(gray_img, thresh_img, 0, 255, THRESH_TRIANGLE);
	//开运算
	Mat ellipse = getStructuringElement(MORPH_ELLIPSE, Size(13, 13));
	morphologyEx(thresh_img, thresh_img, MORPH_OPEN, ellipse, Point(-1, -1), 2);

	************以上为图像处理过程****************************************

	//寻找轮廓
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy1;
	findContours(thresh_img, contours, hierarchy1, RETR_LIST, CHAIN_APPROX_NONE, Point());

	//获取某一轮廓重心点
	Moments M;
	M = moments(contours[0]);
	double cX = double(M.m10 / M.m00);
	double cY = double(M.m01 / M.m00);
	//绘制轮廓
	drawContours(img, contours, 0, Scalar(0, 255, 0), 2, 8, hierarchy1);
	//显示轮廓重心并提取坐标点
	circle(img, Point2d(cX, cY), 1, Scalar(0, 255, 0), 2, 8);
	putText(img, "center", Point2d(cX - 20, cY - 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 1, 8);
	cout << "重心坐标:" << cX << " " << cY << endl << endl;

运行结果:

 

遇到的问题:

在寻找边界过程中,findContours算子寻找的轮廓中总是包含图像的整体轮廓(如下图所示),给寻找边界带来了很大干扰,目前还不知如何解决这个问题?????

猜你喜欢

转载自blog.csdn.net/Kevin_Sun777/article/details/111200877