首页 > 编程知识 正文

图像处理算法 之 Hough变换

时间:2023-05-04 12:19:57 阅读:41110 作者:4937

Hough变换一、标准Hough线变换(SHT ) 1.1原理1.2 SHT步骤1.3缺点二、渐进概率Hough变换(PPHT ) 2.1原理及步骤2.2缺点三、Hough圆变换3.1原理及步骤3.2缺点四、实验代码

一、标准Hough线转换(SHT ) 1.1原理

标准Hough变换(标准Hough变换)。

图像平面上的一直线可以用斜交式y=ax b表示,即可以变换为a-b平面上的一点,但由于倾斜的区间从-变化到,因此为当直线接近竖直时,此时在a-b平面中是难以表示的,因此通过极坐标形式能够更加方便地表示

如图(a )所示,在图像平面中,一个点可以用横纵轴表示; 另一方面,图像平面中的一直线可以由距原点的距离和垂线的角度表示,将其变换为-平面时,如图(c )所示,为图像平面中的一条直线可由-平面中的一个点表示。 如因此图像平面中的一个点可以由穿过它的直线族在-平面中对应的点组成的曲线来表示(c )所示,虚线对应于(b )中直线1、2、3、4的交点。-平面中两条曲线的交点即为图像平面中的两个点共线的一条直线

1.2 SHT步骤,明确了标准的讲究钻石线变换的步骤:

首先,对图像img进行边缘检测,得到二值边缘图像edge_img

将edge_img中每个非0点变换为-平面中的曲线

转换方法如下。

对于图像平面上的一个非0点(x0,y0 ),使从0变化为2,分别利用下式算出对应的值,

得到一系列的点,这些点构成点(x0,y0 )在-平面上对应的曲线。

-加上所有结果,图像平面内的直线显示为-平面内的局部极大值。

根据设定的阈值筛选局部极大值,得到最终结果

1.3缺点标准执着的拨线转换(SHT )无法提取线的端点,OpenCV函数原型

//函数HoughLines的原型要求是voidHoughLines(inputarrayimage,//输入图像,单通道二值图像OutputArray lines,//输出直线矢量,为两个)表示一直线,原点为像素双精度theta,//角度分辨率,单位为弧度int threshold,//阈值,用于筛选p-平面的极大值,实际上至少包含在返回的直线中的点的数量//srn和stn是多尺度Hough转换(MHT )的参数double stn=0)//其中rho和theta两个分辨率参数的作用是多尺度Hough转换(MHT )

二、渐进概率Hough变换(PPHT ) 2.1的原理和渐进概率Hough变换(progressiveprobabilistichoughtransform )是SHT的改进,可以大大缩短计算时间,计算直线的端点。

其主要原理是在Edge图像中选择随机像素,并且根据SHT方法将这些像素点转换为累积平面(p-)。 如果阈值允许在累积平面中排除直线,则沿该直线搜索边图像以确定是否存在一条或多条有限长度的线。 以及在该直线的所有像素被从Edge图像中除去这样,算法返回有限长度的直线。

该算法可以概括为:

制作输入边缘边缘边缘图像(IMG1)的副本(IMG2)。

用从IMG2随机选择的像素更新累加器。

从IMG2中删除像素。

如果修正的蓄能器(BINX )有最大值较低的阈值,则进入第1点。

沿着BINX中指定的线搜索IMG1,以找到间隔连续或间隙不超过预定阈值的最长像素段。

从IMG2中删除段中的像素,并清除BINX。

如果检测到的线段长于给定的最小长度,请添加到输出列表中。

转移到第二点

2.2缺点该算法的一个问题是多次运行可能会产生不同的结果。 当许多线条共享像素时会发生这种情况。 如果两条直线相交,检测的第一条直线将移除公共像素(及其周围的部分),并在另一条直线上产生凹陷。 如果多条线相交,许多像素可能会错过最后一行,累加器投票可能无法达到阈值。

OpenCV函数:

要求voidhoughlinesp (inputarrayimage,//输入图像为单通道的二值图像OutputArray lines,//输出直线矢量,为4通道矢量,分别为线段的两者(x0、y0、x1、y1 ) )斗

ble rho, //距离分辨率,单位为像素double theta, //角度分辨率,单位为弧度int threshold, // 阈值,用于筛选p-θ平面的极大值,实际指明了返回的直线至少包含的点的数量double minLineLength=0, // 线段长度阈值,double maxLineGap=0 //线段之间的间隔阈值 ) 三、Hough圆变换 3.1 原理及步骤

Hough圆变换的原理与前文的Hough线变换相似,Hough线变换是在p-θ平面中不断累加,最后寻找极大值点,Hough圆变换也可以通过类似的方式寻找,需要在三维空间中累加体积,三维分别是圆心的横坐标、纵坐标、半径,但是这样时间复杂度和空间复杂度会非常大,所以实际实现时没有采用这种方法。OpenCV中的Hough圆变换是通过Hough梯度法实现的。

首先看一张图:

可以看到圆上的每一个点(橙色点)都存在一条线(蓝色虚线)相交于圆心(红点),而这些线就是各个点的梯度方向(蓝色箭头)所在的直线。圆心是它们的交点,这就与Hough线变换中的累加平面非常相似了,此时将所有直线都累加起来,圆心就是一个局部极大值点!

所以Hough圆变换就可以通过下面的方法实现了:
① 同样的,首先对图像进行边缘检测,得到edge_img;
② 对edge_img中的每一个非0点计算局部梯度(可以通过Sobel算子计算)
③ 沿着每个点的梯度所在的直线在累加平面中进行累加,同时记录非0点的位置
④ 根据阈值选取累加平面中的局部极大值点作为候选圆心
⑤ 对于每个圆心都有一个与它相关的非0点的列表(步骤③中记录的),计算这些非0点与该圆心的距离,从中选取出最优值作为半径
⑥ 如果有足够数量的点构成圆且该圆心与其它圆心间距超过阈值,就保留这个圆
⑦ 最终就能够得到检测结果

3.2 缺点

①累加器阈值过低时速度会非常慢,因为需要考虑每个非0点
②对每个圆心只能选一个圆,同心圆时只能检测到一个
③Sobel计算的是局部梯度,所以会出现噪音,稳定性略低

OpenCV函数:

//HoughCircles中调用了Canny函数,因此不需要另外对图像进行边缘检测void HoughCircles(InputArray image, //输入图像,要求是8位图像,即灰度图,因为需要计算梯度,使用灰度图更精准,与HoughLines中要求二值图像不同OutputArray circles, //输出,为矩阵或者向量,矩阵的话就是F32C3的数组,三个通道分别为圆心坐标和半径;向量的话就是vector<Vec3f>类型 int method, //实际设置为cv::HOUGH_GRADIENTdouble dp, //累加图像的分辨率,需要大于等于1double minDist, // 圆之间的最小距离阈值double param1=100, //用于Canny中的高阈值,Canny的低阈值设为它的一半double param2=100 //累加器阈值,与HoughLines中的threshold类似int minRadius = 0,//最小半径阈值int maxRadius = 0//最大半径阈值) 四、实验代码 void hough_test(){Mat src = imread("../Images/10.jpg");if (src.empty()) { cout << "read the image failed!!!!!!" << endl;}//读入灰度图,用于hough圆变换检测Mat src_circles = imread("../Images/11.jpg");Mat src_circles_gray;cv::cvtColor(src_circles, src_circles_gray, cv::COLOR_BGR2GRAY);Mat src_PPHT;src.copyTo(src_PPHT);Mat edge_img;vector<Vec2f> hough_lines;vector<Vec4f> hough_P_lines;vector<Vec3f> hough_circles;//图像的宽和高int w = src.cols;int h = src.rows;//Canny滤波Canny(src, edge_img, 50, 150);//SHT 标准hough线变换HoughLines(edge_img, hough_lines, 1, CV_PI/180, 130);//PPHT 渐进hough线变换HoughLinesP(edge_img, hough_P_lines, 1, CV_PI / 180, 130, 20, 100);//Hough圆变换HoughCircles(src_circles_gray, hough_circles, cv::HOUGH_GRADIENT, 2, 50, 100, 100, 10, 300);//在原图上绘制SHT检测出的直线for (auto& line_iter : hough_lines){float rho = line_iter[0];float theta = line_iter[1];Point point1, point2;point1.x = rho*cos(theta) + w*sin(theta);point1.y = rho*sin(theta) - w*cos(theta);point2.x = rho*cos(theta) - w*sin(theta);point2.y = rho*sin(theta) + w*cos(theta);line(src, point1, point2, Scalar(0, 255, 0), 2,8);}//绘制PPHT检测到的直线for (auto& line_iter : hough_P_lines){line(src_PPHT, Point(line_iter[0], line_iter[1]), Point(line_iter[2], line_iter[3]),Scalar(0,0,255),2,8);}//绘制Hough圆变换检测到的圆for (auto& circles : hough_circles){circle(src_circles, Point(circles[0], circles[1]), circles[2], Scalar(0,0,255), 2);}namedWindow("src_SHT", WINDOW_NORMAL);imshow("src_SHT", src);namedWindow("src_PPHT", WINDOW_NORMAL);imshow("src_PPHT", src_PPHT);namedWindow("edge_img", WINDOW_NORMAL);imshow("edge_img", edge_img);namedWindow("circles_img", WINDOW_NORMAL);imshow("circles_img", src_circles);waitKey(0);}

实验结果如图:

参考资料:
《学习OpenCV3》
https://sikasjc.github.io/2018/04/20/Hough/
https://blog.csdn.net/qq_37059483/article/details/77891698


最后,如有错误的地方还请大佬指正,同时欢迎一起讨论,转载请注明出处

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。