1、算法思想
边缘检测比如canny算子可以识别出图像的边缘,但是实际中由于噪声和光照不均匀等因素,很多情况下获得的边缘点是不连续的,必须通过边缘连接将他们转换为有意义的边缘。Hough变化是一个重要的检测间断点边界形状的方法,它通过将图像坐标空间变化到参数空间来实现直线和曲线的拟合。
霍夫变换于1962年由Paul Hough 首次提出,后于1972年由Richard Duda和Peter Hart推广使用,经典霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。
Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough直线检测的基本原理在于利用点与线的对偶性,在我们的直线检测任务中,即图像空间中的直线与参数空间中的点是一一对应的,参数空间中的直线与图像空间中的点也是一一对应的。这意味着我们可以得出两个非常有用的结论:
1)图像空间中的每条直线在参数空间中都对应着单独一个点来表示;
2)图像空间中的直线上任何一部分线段在参数空间对应的是同一个点。
因此Hough直线检测算法就是把在图像空间中的直线检测问题转换到参数空间中对点的检测问题,通过在参数空间里寻找峰值来完成直线检测任务,也即把检测整体特性转化为检测局部特性。
2、算法原理
1)图像空间和参数空间
霍夫变换的数学理解是“换位思考”,比如一条直线y=a*x+b有两个参数,在给定坐标系下,这条直线就可以用a和b进行完整的表述。如果我们把x和y看作参数,把a和b看作变量的话,那么图像空间下的坐标点(x1,y1)对应着参数空间里的一条直线q=-x1*k+y1, 图像空间直线上的点(x1,y1)就是参数空间的斜率和截距,其中k,q为参数空间的自变量。
2)参数空间转换过程
下面用不同空间下的点和线的变换过程示例说明。
一条直线可由两个点A=(X1,Y1)和B=(X2,Y2)确定(笛卡尔坐标)。
另一方面,y=kx+q也可以写成关于(k,q)的函数表达式(霍夫空间):
对应的变换可以通过图形直观表示:
变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。
反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):
再来看看A、B两个点,对应霍夫空间的情形:
再看一下三个点共线的情况:
可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。
如果不止一条直线呢?再看看多个点的情况(有两条直线):
其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。
到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?
k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示。(参考文件里大佬博客里面的图错了,下图是正确的极坐标表示方法,并且给出了辅助线几何解释)
在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是[ρ, θ]的参数,给出对比图:
从上面可以看到,参数空间的每个点(ρ,θ)都对应了图像空间的一条直线,或者说图像空间的一个点在参数空间中就对应为一条曲线。这样就把在图像空间中检测直线的问题转化为在极坐标参数空间中找通过点(r,θ)的最多正弦曲线数的问题。霍夫空间中,曲线的交点次数越多,所代表的参数越确定,画出的图形越饱满。
霍夫直线检测就是把图像空间中的直线变换到参数空间中的点,通过统计特性来解决检测问题。具体来说,如果一幅图像中的像素构成一条直线,那么这些像素坐标值(x, y)在参数空间对应的曲线一定相交于一个点,所以我们只需要将图像中的所有像素点(坐标值)变换成参数空间的曲线,并在参数空间检测曲线交点就可以确定直线了。
下面给出霍夫变换的算法步骤:
总结:使用霍夫变换检测直线具体步骤:
1.彩色图像->灰度图
2.去噪(高斯核)
3.边缘提取(梯度算子、拉普拉斯算子、canny、sobel)
4.二值化(判断此处是否为边缘点,就看灰度值==255)
5.映射到霍夫空间(准备两个容器,一个用来展示hough-space概况,一个数组hough-space用来储存voting的值,因为投票过程往往有某个极大值超过阈值,多达几千,不能直接用灰度图来记录投票信息)
6.取局部极大值,设定阈值,过滤干扰直线
7.绘制直线、标定角点
3、代码测试
函数原型:
CV_EXPORTS_W void HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn = 0, double stn = 0, double min_theta = 0, double max_theta = CV_PI );
测试代码如下:
int main() { Mat src_img; Mat dst_img, cdst, cdst2; src_img = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic18.bmp"); imshow("原图", src_img); Canny(src_img, dst_img, 50, 200, 3); imshow("Canny图", dst_img); cdst = src_img.clone(); cdst2 = dst_img.clone(); vectorlines; HoughLines(dst_img, lines, 1, CV_PI / 180, 100, 0, 0); for (size_t i = 0; i < lines.size(); i++) { float rho = lines[i][0], theta = lines[i][1]; Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a * rho, y0 = b * rho; pt1.x = cvRound(x0 + 1000 * (-b)); pt1.y = cvRound(y0 + 1000 * (a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * (a)); line(cdst, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA); line(cdst2, pt1, pt2, Scalar(255, 255, 255), 1, LINE_AA); } imshow("detected lines", cdst); imshow("detected lines2", cdst2); waitKey(0); }
测试效果图如下,canny边缘检测有不连续的边缘,霍夫变换直线检测可以连接不连续的直线边缘。
4、参考文献
1、《数字图像处理与机器视觉》
第二版。 张铮、徐超、任淑霞、韩海玲等编著。
2、霍夫变换直线检测(Line Detection)原理及示例
https://blog.csdn.net/leonardohaig/article/details/87907462
3、霍夫变换
https://www.cnblogs.com/php-rearch/p/6760683.html
4、霍夫变换-----特征提取
https://blog.csdn.net/m0_37264397/article/details/72729423
5、霍夫线变换
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.html
6、霍夫圆变换
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.html
7、经典霍夫变换(Hough Transform)
https://blog.csdn.net/YuYunTan/article/details/80141392
8、霍夫变换(Hough Transform)的原理以及代码(Matlab&C)实现
https://blog.csdn.net/ljwcdtj/article/details/89091060
个人博客,转载请注明。
https://www.cnblogs.com/pingwen/p/12506240.html