Hough变换直线检测

作者:云外阳光

链接:https://www.zhihu.com/question/35268803/answer/82100453
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

先组略的答一下,待整理:
y=k*x+b形式的直线方程没有办法表示x=c形式的直线(这时候,直线的斜率为无穷大)。所以实际应用中,利用极坐标的方式,将直线方程表示成:ρ=xcosθ+ysinθ , 其中p表示直角坐标系中原点到直线的距离,θ表示x1轴与p的夹角这样,图像平面上的一个点就对应到ρθ平面上的一条曲线上。要注意,同一条直线的点因为构成了一条直线,所对应的p和θ当然也就确定了。

~~~~~~~~~~~~~~~~~~~~~~~~~~~浪浪线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
再回答一次吧,由于前段时间要做一个空间三维直线的检测,把霍夫变换看了好久,终于看明白了一点(高手勿喷 )其实霍夫变换的原理很简单,理解起来就四个字“点线对偶”,而且MATLAB 已经有 hough houghline houghpeak,三个函数,分别对二维图片进行hough 、连线、提取峰值。但是这些程序只适用于二维图片的检测问题(直线、圆),为了要解决三维检测,只能硬着头皮 打开 函数,分析原理,后来发现三维直线检测由于需要三个累加参数,所以和圆检测的算法是类似的,只要建立一个三维累加空间,然后投票就ok了。所以我的问题就这么解决了,哈哈哈-----
~~~~~~~~~~~~~~~~~~~~~~~~~~~浪浪线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
现在来解决楼主的问题。楼主的问题我感觉可能是因为看错了p的意义,才可能问出这样的问题,第一次的回答中也已经解释过了。这次我想总体的说一下,我看霍夫变换的体会,其中参考了网上很多前辈的东西,由于当时直接下载下来了,所以都没有保存链接。
各位先祖,如有冒犯,请见谅啊------------
--------------------------------------------------------------------------------------------------------------------------------

1. 霍夫变换的原理

在数字图像中,往往存在着一些特殊形状的几何图形,像检测马路边一条直线,检测人眼的圆形等等,有时我们需要把这些特定图形检测出来,hough变换就是这样一种检测的工具。

Hough变换的原理是将特定图形上的点变换到一组参数空间上,根据参数空间点的累计结果找到一个极大值对应的解,那么这个解就对应着要寻找的几何形状的参数(比如说直线,那么就会得到直线的斜率k与常熟b,圆就会得到圆心与半径等等)。

关于hough变换,核心以及难点就是关于就是有原始空间到参数空间的变换上。以直线检测为例,假设有一条直线L,原点到该直线的垂直距离为p,垂线与x轴夹角为θ ,那么这条直线是唯一的,且直线的方程为 ρ=xcosθ+ysinθ , 如下图所示:

<img src="https://pic1.zhimg.com/8438230b64475611875361c294aa6a24_b.png" data-rawheight="289" data-rawwidth="534" class="origin_image zh-lightbox-thumb" width="534" data-original="https://pic1.zhimg.com/8438230b64475611875361c294aa6a24_r.png"> Hough变换直线检测_第1张图片
可以看到的是这条直线在极坐标系下只有一个(ρ,θ) 与之对应,随便改变其中一个参数的大小,变换到空间域上的这个直线将会改变。好了,再回来看看这个空间域上的这条直线上的所有点吧,你会发现,这条直线上的所有点都可以是在极坐标为(ρ,θ) 所表示的直线上的,为什么说是都可以在,因为其中随便的一个点也可以在其他的(ρ,θ) 所表示的直线上,就比如上述的(x,y)吧,它可以再很多直线上,准确的说,在经过这个点的直线上,随便画两条如下:<img src="https://pic3.zhimg.com/3ee88f15628a2caed5079ae36b76b18e_b.png" data-rawheight="297" data-rawwidth="512" class="origin_image zh-lightbox-thumb" width="512" data-original="https://pic3.zhimg.com/3ee88f15628a2caed5079ae36b76b18e_r.png">
可以看到,光是空间上的一个点在极坐标系下就可能在很多极坐标对所对应的直线上,具体有多少个极坐标对呢?那得看你的θ 的步长了,我们可以看到θ 无非是从0-360度(0−2π )变化,假设我们没10度一走取一个直线(这个点在这个直线上),那么我们走一圈是不是取了36条直线,也就对应36个极坐标对没错吧,那么这个极坐标对,画在坐标轴上是什么样子的呢?因为θ 是从0−2π ,并且一个点定了,如果一个θ 也定了,你想想它对应的直线的ρ 会怎么样,自然也是唯一的。那么这个点在极坐标下对应的(ρ,θ) 画出来一个周期可能就是这样的,以θ 为x轴的话:
<img src="https://pic4.zhimg.com/91c687225572c896b1b673887b0d2767_b.png" data-rawheight="240" data-rawwidth="572" class="origin_image zh-lightbox-thumb" width="572" data-original="https://pic4.zhimg.com/91c687225572c896b1b673887b0d2767_r.png"> Hough变换直线检测_第2张图片
ok前面说的是单单这一个点对应的极坐标系下的参数对,那么如果每个点都这么找一圈呢?也就是每个点在参数空间上都对应一系列参数对吧,现在把它们华仔同一个坐标系下会怎么样呢?为了方便,假设在这个直线上取3个点画一下:
<img src="https://pic2.zhimg.com/8637b31ddda51f147c081194ef7ac30d_b.png" data-rawheight="236" data-rawwidth="559" class="origin_image zh-lightbox-thumb" width="559" data-original="https://pic2.zhimg.com/8637b31ddda51f147c081194ef7ac30d_r.png">那么可以看到,首先对于每一个点,在极坐标下,会存在一个周期的曲线来表示通过这个点,其次,这三个极坐标曲线同时经过一个点,要搞清楚的是,极坐标上每一个点对(ρ,θ) 在空间坐标上都是对应一条直线的。好了,同时经过的这一个点有什么含义呢?它表示在空间坐标系下,有一条直线可以经过点1,经过点2,经过点3,这是什么意思?说明这三个点在一条直线上吧。反过来再来看这个极坐标系下的曲线,那么我们只需要找到交点最多的点,把它返回到空间域就是这个需要找的直线了。为什么是找相交最多的点,因为上面这只是三个点的曲线,当空间上很多点都画出来的时候,那么相交的点可能就不知上述看到的一个点了,可能有多个曲线相交点,但是有一点,势必是一条直线上的所有点汇成的交点是曲线相交次数最多的。 Hough变换直线检测_第3张图片那么可以看到,首先对于每一个点,在极坐标下,会存在一个周期的曲线来表示通过这个点,其次,这三个极坐标曲线同时经过一个点,要搞清楚的是,极坐标上每一个点对(ρ,θ) 在空间坐标上都是对应一条直线的。好了,同时经过的这一个点有什么含义呢?它表示在空间坐标系下,有一条直线可以经过点1,经过点2,经过点3,这是什么意思?说明这三个点在一条直线上吧。反过来再来看这个极坐标系下的曲线,那么我们只需要找到交点最多的点,把它返回到空间域就是这个需要找的直线了。为什么是找相交最多的点,因为上面这只是三个点的曲线,当空间上很多点都画出来的时候,那么相交的点可能就不知上述看到的一个点了,可能有多个曲线相交点,但是有一点,势必是一条直线上的所有点汇成的交点是曲线相交次数最多的。

再来分析这个算法。可以看到hough变换就是参数映射变换。对每一个点都进行映射,并且每一个映射还不止一次,(ρ,θ) 都是存在步长的,像一个点映射成一个(ρ,θ) ,以θ 取步长为例,当θ 取得步长大的时候,映射的(ρ,θ) 对少些,反之则多,但是我们有看到,映射后的点对是需要求交点的,上述画出来的曲线是连续的,然而实际上因为θ 步长的存在,他不可能是连续的,像上上个图一样,是离散的。那么当θ 步长取得比较大的时候,你还想有很多交点是不可能的,因为这个时候是离散的曲线然后再去相交,所以说θ 步长不能太大,理论上是越小效果越好,因为越小,越接近于连续曲线,也就越容易相交,但是越小带来的问题就是需要非常多的内存,计算机不会有那么多内存给你的,并且越小,计算量越大,想想一个点就需要映射那么多次,每次映射是需要计算的,耗时的。那么再想想对于一副图像所有点都进行映射,随便假设一副100*100的图像(很小吧),就有10000个点,对每个点假设就映射36组(ρ,θ) 参数(此时角度的步长是10度了,10度,已经非常大的一个概念了),那么总共需要映射360000次,在考虑每次映射计算的时间吧。可想而知,hough是多么耗时耗力。所以必须对其进行改进。首先就是对图像进行改进,100*100的图像,10000个点,是不是每个点都要计算?大可不必,我们只需要在开始把图像进行一个轮廓提取,一般使用canny算子就可以,生成黑白二值图像,白的是轮廓,那么在映射的时候,只需要把轮廓上的点进行参数空间变换,为什么提轮廓?想想无论检测图像中存在的直线呀圆呀,它们都是轮廓鲜明的。那么需要变换的点可能就从10000个点降到可能1000个点了,这也就是为什么看到许多hough变换提取形状时为什么要把图像提取轮廓,变成二值图像了。


继续算法,分析这么多,可想而知那么一个hough变换在算法设计上就可以如下步骤:
(1)将参数空间(ρ,θ) 量化,赋初值一个二维矩阵M,M(ρ,θ) 就是一个累加器了。
(2)然后对图像边界上的每一个点进行变换,变换到属于哪一组(ρ,θ) ,就把该组(ρ,θ) 对应的累加器数加1,这里的需要变换的点就是上面说的经过边缘提取以后的图像了。
(3)当所有点处理完成后,就来分析得到的M(ρ,θ) ,设置一个阈值T,认为当M(ρ,θ)>T ,就认为存在一条有意义的直线存在。而对应的M(ρ,θ) 就是这组直线的参数,至于T是多少,自己去式,试的比较合适为止。
(4)有了M(ρ,θ) 和点(x,y)计算出来这个直线就ok了。


2. 霍夫变换函数:hough;houghpeaks;houghlines

图像处理工具箱提供了三个与霍夫变换有关的函数。函数hough实现了前面讨论的概念,函数houghpeaks寻找霍夫变换的峰值(累加单元的高计数),函数houghlines以来自其他两个函数的结果为基础在原始图像中提取线段。

1. 函数hough

函数hough支持任意的默认语法:

[H, theta, rho] = hough(f)

还支持完整的语法形式:

[H, theta, rho] = hough(f, 'ThetaRes', val1, 'RhoRes', val2)

其中,H是霍夫变换矩阵,theta(以度计)和rho是ρ和θ值向量,在这些值上产生霍夫变换。输入f是二值图像,val1是0到90的标量,指定了沿θ轴霍夫变换的间距(默认是1),val2是0



霍夫变换的说明

在这个例子中,我们用简单的合成图像来说明hough函数的机理:

>> f = zeros(101, 101);

>> f(1, 1) = 1; f(101, 1) = 1; f(1, 101) = 1;

>> f(101, 101) = 1; f(51, 51) = 1;

图显示了我们的测试图像,下面使用默认值计算并显示霍夫变换的结果:

>> H = hough(f)

>> Imshow(H,[])

显示了结果,以熟悉的方法使用imshow函数来显示。在带有标度轴的较大图中显现霍夫变换常常更有用。

在接下来的代码片段中,我们调用带有三个参数的hough函数。然后把向量theta和rho作为附加输入参量传递给imshow,从而控制水平轴和垂直轴的标度。我们还要把'InitialMagnification'选项传递给带有值'fit'的imshow函数,因此,整个图像将被强迫在图形窗口中进行装配。axis函数被用来打开轴标记,并使其显示填充图的矩形框。最后,xlabel和ylabel函数用希腊字母LaTeX字体符号在轴上标值:

>> [H, theta, rho] = hough(f);

>> imshow(H, [], 'XData', theta, 'YData', rho ,'InitialMagnification', 'fit')

>> axis on, axis normal

>> xlabel('\theta'), ylabel('\rho')

显示了标上值之后的结果。三条曲线(直线也可考虑为曲线)在±45°处的交点指出:f中有两组三个共线的点。两条曲线在(ρ,θ)=(0,-90)、(-100,-90)、(0,0)和(100,0)处的交点指出:有4组位于垂直线和水平线上的共线点。

<img src="https://pic4.zhimg.com/6df5179bbf98e75f6c50ee31c0b45057_b.png" data-rawheight="499" data-rawwidth="576" class="origin_image zh-lightbox-thumb" width="576" data-original="https://pic4.zhimg.com/6df5179bbf98e75f6c50ee31c0b45057_r.png"> Hough变换直线检测_第4张图片
(发现水印,对就是你,好像就是在这个连接看到的,哈哈)
2. 函数houghpeaks

线检测和连接用的霍夫变换的第一步是用高的计数寻找累加单元(工具箱文本把高的计数单元作为峰值)。因为存在霍夫变换参数空间中的量化和典型图像的边缘并不是很完美的直线这样的事实,霍夫变换的峰值倾向于相比霍夫变换单元更多。函数houghpeaks用任意默认语法来寻找指定的峰值数:

peaks = houghpeaks(H, NumPeaks)

或者使用完整的语法形式:

peaks = houghpeaks(..., 'Threshold', val1, 'NHoodSize', val2)

其中,"…"指出来自默认语法和peaks的输入是持有峰值行和列坐标的Q×2大小的矩阵。Q的范围是0到NumPeaks,H是霍夫变换矩阵。参数val1是非负的标量,指定了H中的什么值被考虑为峰值;val1可以从0到Inf变化,默认值是0.5*max(H(:))。参数val2是奇整数的两元素矢量,指定量围绕峰值的邻域大小。当鉴别出峰值之后,邻域中的元素被置为0。默认是由最小奇数值组成的两元素矢量大于或等于size(H)/50。这个过程的基本思想是:通过把发现峰值的直接邻域中的霍夫变换单元置0来清理峰值。我们在例10.6中说明函数houghpeaks。



3. 函数houghlines

一旦一组候选的峰值在霍夫变换中被识别出来,如果存在与这些峰值相关的有意义的线段,剩下的就是决定线的起始点和终点。函数houghlines用默认的语法执行这个任务:

lines = houghlines(f, theta, rho, peaks)

或者使用完整的语法形式:

lines = houghlines(..., 'FillGap', val1, 'MinLength', val2)

其中,theta和rho是来自函数hough的输出,peaks是函数houghpeaks的输出。输出lines是结构数组(可能检测到多条直线),长度等于找到的线段数。结构中的每个元素可以看成一条线,并含有下列字段:

point1:两元素向量[r1, c1],指定了线段起点的行列坐标。

point2:两元素向量[r2, c2],指定了线段终点的行列坐标。

theta:与线相关的霍夫变换的以度计量的角度。

rho:与线相关的霍夫变换的ρ轴位置。

其他参数如下:

val1是正的标量,指定了与相同的霍夫变换相关的两条线段的距离。当两条线段之间的距离小于指定的值时,函数houghlines把线段合并为一条线段(默认的距离是20个像素)。参数val2是正的标量,指定合并的线是保留还是丢弃。如果合并的线比val2指定的值短,就丢弃(默认值是40)。

用霍夫变换检测和连接线

在这个例子中,我们用函数hough、houghpeaks和houghlines寻找图所示二值图像f的一组线段。首先,我们用比默认值更好的角间距(用0.2代替1.0)计算和显示霍夫变换:

>> [H, theta, rho] = hough(f, 'ThetaResolution', 0.2);

>> imshow(H, [], 'XData', theta, 'YData', rho, 'InitialMagnification', 'fit')

>> axis on, axis normal

>> xlabel('\theta'), ylabel('\rho')

下一步,我们用函数houghpeaks寻找5个有意义的霍夫变换的峰值:

>> peaks = houghpeaks(H, 5);

>> hold on

>> plot(theta(peaks(:, 2)), rho(peaks(:, 1)), ...

'linestyle', 'none', 'marker', 's', 'color', 'w')

前边的操作计算和显示霍夫变换,并添加使用函数houghpeaks的默认设置寻找到的5个峰值位置。图显示了结果。例如,最左边较小的方形确定与房顶相关的累加单元,以工具箱的角度作为参考倾向于近似-74°,在图中是-16°。最后,我们使用函数houghlines寻找和连接线段,用函数imshow、hold on和plot在原始的二值图像上添加线段:

>> lines = houghlines(f, theta, rho, peaks);

>> figure, imshow(f), hold on

>> for k = 1:length(lines)

xy = [lines(k).point1 ; lines(k).point2];

plot(xy(:,1), xy(:,2), 'LineWidth', 4, 'Color', [.8 .8 .8]);

end

显示了使用检测到的叠加了较粗灰线的线段得到的结果。<img src="https://pic1.zhimg.com/57bc1fe9bbd8ddd14e1fa800786e1440_b.png" data-rawheight="233" data-rawwidth="496" class="origin_image zh-lightbox-thumb" width="496" data-original="https://pic1.zhimg.com/57bc1fe9bbd8ddd14e1fa800786e1440_r.png">Hough变换直线检测_第5张图片

完整示例:


1. f=imread('w2.tif');

2. figure(1)

3. imshow (f);title('The Original image')

4. level=graythresh(f);

5. f=im2bw(f);

6. [H, theta, rho] = hough(f, 'ThetaResolution', 0.2);

7. figure(2)

8. imshow(H, [], 'XData', theta, 'YData', rho, 'InitialMagnification', 'fit')

9. axis on, axis normal

10. hold on

11. xlabel('\theta'), ylabel('\rho')

12. peaks = houghpeaks(H,2);%检测2个峰值点

13. plot(theta(peaks(:, 2)), rho(peaks(:, 1)), ...

14. 'linestyle', 'none', 'marker', 's', 'color', 'w')

15. title('The peak point location')

16. lines = houghlines(f, theta, rho, peaks);

17. figure(3)

18. imshow(f), hold on

19. for k = 1:length(lines)

20. xy=[lines(k).point1 ; lines(k).point2];

21. plot(xy(:,1), xy(:,2), 'LineWidth', 4, 'Color', [.7 .7 .7]);

22. end

23. title('Hough-transformation result')


结果:

<img src="https://pic3.zhimg.com/ef0a475351f38cc3ef956f44ce1c3fc2_b.png" data-rawheight="525" data-rawwidth="580" class="origin_image zh-lightbox-thumb" width="580" data-original="https://pic3.zhimg.com/ef0a475351f38cc3ef956f44ce1c3fc2_r.png"> <img src="https://pic1.zhimg.com/1e170ac5857d7cecb4b466525acb9a54_b.png" data-rawheight="529" data-rawwidth="573" class="origin_image zh-lightbox-thumb" width="573" data-original="https://pic1.zhimg.com/1e170ac5857d7cecb4b466525acb9a54_r.png"> Hough变换直线检测_第6张图片
--------------------------------------------------------------------------------------------------------------------------------
如此牛逼的 霍夫,有什么不足呢?
啪啪啪===
---------------------------------------------------------------------------------------------------------------------------------
Hough变换直线检测的不足:

首先,Hough变换进行参数空间映射时需要对每个边缘点进行多次正弦曲线计算,这就大大的加重了算法的计算负担,同时也降低了其实时性。其次,在进行Hough变换之前需要对参数空间进行量化与离散化,但在离散化之后,参数空间中本来连续的函数被离散函数所取代,这就造成了变换之后得到的(A巧的值与原有值之间存在一定的误差。最后,上述方法对于参数空间中的离散点只进行一次投票,而在一次投票过程中常常出现伪峰值被判定为目标直线的情况。这H点明显的不足严重的制约了其适用范围

Hough变换圆检测的不足:

Hough变换需要将参数空间离散为一个三维空间,送样就使得算法所需的计算时间与存储空间过大,这一问题严重制约了该算法在实际检测中的应用。同时,该算法需要对圆形的半径进行提前预判,这就导致检测过程中可能出现漏检、误检的可能,这也降低了该算法对于圆形提取的精确性


你可能感兴趣的:(Algorithm)