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| 第四个参数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 = https://www.it610.com/article/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 contours; vector 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);

运行效果如下图:
OpenCV4学习笔记(26)——轮廓拟合直线、寻找极值点、点多边形检测
文章图片

好了今天笔记整理到此结束,明天再继续吧。
【OpenCV4学习笔记(26)——轮廓拟合直线、寻找极值点、点多边形检测】PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

    推荐阅读