OpenCV4学习笔记(26)——轮廓拟合直线、寻找极值点、点多边形检测

今天周日终于是没课的一天,所以兴致勃勃的开始学习图像的特征提取及描述这方面的内容,然后肝了一天,才研究了级联检测器和一些特征提取描述算法的皮毛层次的内容。。。而且ORB特征算法虽然在OpenCV里有封装好了的检测器,可是其内部包含了很多特征检测的知识,甚至提取特征和描述特征都使用了其他不同的特征算法相结合,深入点拆解开来研究的话这个内容真是不少的。。。搞到晚上十点多才有时间来整理下之前的笔记,唯一的感觉就是,是我太菜了。。。。。。。。。。。。。。
好了不吐槽了,假期那么久在家没剪头发,只要还没秃,我就还能学!!!

那今天要整理的笔记内容是关于轮廓拟合直线、寻找极值点、点多边形检测这三个知识点,下面一一来记录。

1.轮廓拟合直线
有时候对于从图像中找到的轮廓,可能因为某些莫名其妙的原因导致它绘制出来后并不是一条直线,也许是拍摄图像存在扭曲,又或许是拍摄的物体本身有突出或凹陷部分,使得我们获取到的轮廓不是直线。于是我们可以通过轮廓拟合直线来获得与该轮廓相关联的直线,然后又可以进行其他的进一步操作。
在OpenCV中提供了一个APIfitLine()来进行轮廓的直线拟合,下面看一下它的参数列表:
第一个参数points:输入的点集,可以是二维或三维;包括但不限于轮廓点集;

第二个参数line:拟合的直线;若输入为二维点集,则输出Vec4f类型,包含元素为[ X方向梯度dx, Y方向梯度dy,拟合点X坐标,拟合点Y坐标 ];若输入为三维点集,则输出Vec6f类型,包含元素为[ X方向梯度dx, Y方向梯度dy,Z方向梯度dz,拟合点X坐标,拟合点Y坐标,拟合点Z坐标 ];由斜率和一个点就可以唯一的确定一条直线;

第三个参数distType:可选的计算距离方式;拟合直线时,要使输入的各个点到拟合直线的距离和最小化;有以下几种可选类型:
(1)DIST_USER :自定义距离类型
(2)DIST_L1: distance = |x1-x2| + |y1-y2|
(3)DIST_L2 :简单欧氏距离
(4)DIST_C :distance = max( |x1-x2| , |y1-y2| )
(5)DIST_L12 : L1-L2 metric: distance = 2( sqrt( 1 + x * x / 2 ) - 1))
(6)DIST_FAIR :distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
(7)DIST_WELSCH:distance = c2/2(1-exp(-(x/c)2)), c = 2.9846
(8)DIST_HUBER : distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345

第四个参数param:距离参数,跟所选的距离类型有关,可以直接设置为0,函数本身会自动选择最优化的值;

第五、六两个参数用于表示拟合直线所需要的径向和角度精度,通常情况下两个值均被设定为0.01

通过上面这个API就可以将轮廓拟合成直线,但是我们知道,输出的直线只有一个点,这样是没办法把他绘制出来的,所以我们还需要再求出另外一个位于该直线上的点。在输出参数line中,还包含了该直线在x、y方向上的变化率,我们可以通过这两个方向的变化率来求取直线的斜率k。然后通过直线的点斜式方程:y = kx + b ,以及输出的一个已知的点来求取直线的截距b。这样我们就可以得到一条完整的点斜式直线方程。相关代码如下:

		Vec4f oneline;
		fitLine(contours[i], oneline, DIST_L1, 0, 0.01, 0.01);
		float k, b;
		k = oneline[1] / oneline[0];				//   dy / dx    求出拟合直线的斜率
		b = oneline[3] - k * oneline[2];		//   y=kx+b	 求出拟合直线的截距

接下来就再通过寻找极值点的方法,来求取另外一个位于该直线上的点。

2.寻找极值点
我们通过遍历当前轮廓上的所有点,来比较所有点的x坐标,并找出最小的x坐标值 minx 和最大的x坐标值 maxx ,然后通过上述求出的直线点斜式方程来分别求取对应的最小y坐标 miny、最大y坐标maxy。

这样我们就得到了该拟合直线的两个极值点,而且这两个点均位于该直线上,就可以通过这两个点来绘制拟合直线了。

相关代码如下:

	int minx, miny, maxx, maxy;
		minx = contours[i][0].x;
		maxx = contours[i][0].y;
		//遍历该轮廓中的所有点,找到最大和最小的x坐标
		for (int t = 0; t < contours[i].size(); t++)
		{
			if (minx > contours[i][t].x)
			{
				minx = contours[i][t].x;
			}
			if (maxx < contours[i][t].x)
			{
				maxx = contours[i][t].x;
			}
		}
		miny = k * minx + b;
		maxy = k * maxx + b;

		Point min(minx, miny);
		Point max(maxx, maxy);

3.点多边形检测

点多边形检测,可以准确的得到一个点和轮廓多边形的距离,或者判断该点和轮廓的位置关系。可以用来进行物体移动检测,识别物体是否在某轮廓内,例如停车检测,提取停车位的轮廓,并提取车辆的目标点,通过识别车辆目标点与车位轮廓的位置关系,判断车是否停靠好了。

OpenCV中提供了pointPolygonTest()这个API来进行点多边形检测,其参数列表如下:
第一个参数contour:输入的轮廓点集;
第二个参数pt:需要判断的点;
第三个参数measureDist:布尔值,如果为true,则返回该点和轮廓的距离,如果该点是轮廓多边形上的点,则距离是零,如果是多边形内部的点则距离是正数,如果是多边形外部的点则距离是负数;如果为false,则返回+1,0,-1,分别表示点在轮廓内部、轮廓上、轮廓外部。
相关代码如下:

		Point min(minx, miny);
		Point max(maxx, maxy);
		int flag_min = pointPolygonTest(contours[i], min, false);
		int flag_max = pointPolygonTest(contours[i], max, false);

轮廓拟合直线、寻找极值点、点多边形检测这几个知识点合起来就可以绘制出轮廓的拟合直线,并判断直线上的点与轮廓的位置关系。完整代码如下:

/******************************轮廓拟合直线、寻找极值点、点多边形检测*****************************/
	Mat src = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\twolines.png");
	Mat src_gray, src_gaus, src_binary;
	//canny算子突出边缘并二值化,方便寻找轮廓
	Canny(src, src_binary, 80, 160);

	//膨胀处理,连接边缘
	Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	dilate(src_binary, src_binary, k);

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(src_binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

	for (int i = 0; i < contours.size(); i++)
	{
		//寻找该轮廓的最小外接正矩形,如果长和宽中的较大者过大或者过小,则过滤掉该轮廓
		Rect rect = boundingRect(contours[i]);
		int m = max(rect.width, rect.height);
		if (m < 30 || m>150) continue;

		Vec4f oneline;
		fitLine(contours[i], oneline, DIST_L1, 0, 0.01, 0.01);
	
		float k, b;
		k = oneline[1] / oneline[0];				//   dy / dx    求出拟合直线的斜率
		b = oneline[3] - k * oneline[2];		//   y=kx+b	 求出拟合直线的截距

		
		int minx, miny, maxx, maxy;
		minx = contours[i][0].x;
		maxx = contours[i][0].y;
		//遍历该轮廓中的所有点,找到最大和最小的x坐标
		for (int t = 0; t < contours[i].size(); t++)
		{
			if (minx > contours[i][t].x)
			{
				minx = contours[i][t].x;
			}
			if (maxx < contours[i][t].x)
			{
				maxx = contours[i][t].x;
			}
		}
		miny = k * minx + b;
		maxy = k * maxx + b;

		
		Point min(minx, miny);
		Point max(maxx, maxy);
		int flag_min = pointPolygonTest(contours[i], min, false);
		int flag_max = pointPolygonTest(contours[i], max, false);
		
		line(src, Point(minx, miny), Point(maxx, maxy), Scalar(0, 0, 255), 1, 8, 0);
	}
	imshow("src", src);

运行效果如下图:
在这里插入图片描述

好了今天笔记整理到此结束,明天再继续吧。

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

发布了36 篇原创文章 · 获赞 43 · 访问量 1800

猜你喜欢

转载自blog.csdn.net/weixin_45224869/article/details/104888172