opencv学习——霍夫变换原理

最近的项目用到了霍夫变换,感觉自己只是会调用函数,并不清楚原理,所以写这篇记录一下

 

霍夫变换

中心思想是通过坐标变换来检测直线,后来经过改进,就可以检测椭圆等

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

原始空间到参数空间的变换

假设有一条直线L,原点到该直线的垂直距离为p,垂线与x轴夹角为θ ,那么这条直线是唯一的,且直线的方程为 ρ=xcosθ+ysinθ , 如下图所示。

可以看到的是这条直线在极坐标系下只有一个(ρ,θ) 对应,随便改变其中一个参数的大小,变换到空间域上的这个直线将会改变。

再回来看看这个空间域上的这条直线上的所有点,这条直线上的所有点都可以是在极坐标为(ρ,θ) 所表示的直线上的

其中随便的一个点也可以在其他的(ρ,θ) 所表示的直线上。比如上述的(x,y)吧,它可以再很多直线上,准确的说,在经过这个点的直线上,随便画两条如下:

可以看出,空间上的一个点 在极坐标系下 就可能在 很多个极坐标对 所对应的直线上。

具体有多少个极坐标对呢?考虑θ 的步长,θ 从0-360度(0−2π )变化。假设每10度取一个θ值,即取一个直线(这个点在这个直线上),走一圈即取了36条直线,对应36个极坐标对

那么这个极坐标对,画在坐标轴上是什么样子的呢?因为θ 是从0−2π ,并且一个点定了,如果一个θ 也定了,ρ 自然也是唯一的。那么这个点在极坐标下对应的(ρ,θ) 画出来一个周期可能就是这样的,以θ 为x轴的话:

ok前面说的是单单这一个点对应的极坐标系下的参数对,那么如果每个点都这么找一圈呢?也就是每个点在参数空间上都对应一系列参数对吧,现在把它们画在同一个坐标系下会怎么样呢?

为了方便,假设在这个直线上取3个点画一下:

首先对于每一个点,在极坐标下,会存在一个周期的曲线来表示通过这个点。其次,这三个极坐标曲线同时经过一个点

要搞清楚的是,极坐标上每一个点对(ρ,θ) 在空间坐标上都是对应一条直线的。

同时经过的这一个点有什么含义呢?

它表示在空间坐标系下,有一条直线可以经过点1,经过点2,经过点3,这是什么意思?说明这三个点在一条直线上吧。

反过来再来看这个极坐标系下的曲线,那么我们只需要找到交点最多的点,把它返回到空间域就是这个需要找的直线了。

为什么是找相交最多的点,因为上面这只是三个点的曲线,当空间上很多点都画出来的时候,那么相交的点可能就不止上述看到的一个点了,可能有多个曲线相交点,但是有一点,势必是一条直线上的所有点汇成的交点是曲线相交次数最多的。

再来分析这个算法。可以看到hough变换就是参数映射变换。对每一个点都进行映射,并且每一个映射还不止一次。

(ρ,θ) 都是存在步长的,像一个点映射成一个(ρ,θ) ,以θ 取步长为例,当θ 取得步长大的时候,映射的(ρ,θ) 对少些,反之则多。

但是我们有看到,映射后的点对是需要求交点的,上述画出来的曲线是连续的,然而实际上因为θ 步长的存在,他不可能是连续的,像图1一样,是离散的。

那么当θ 步长取得比较大的时候,还想有很多交点是不可能的,因为这个时候是离散的曲线然后再去相交

所以说θ 步长不能太大,理论上是越小效果越好,因为越小,越接近于连续曲线,也就越容易相交,但是越小带来的问题就是需要非常多的内存,计算机不会有那么多内存给你的,并且越小,计算量越大,想想一个点就需要映射那么多次,每次映射是需要计算的,耗时的。

那么再想想对于一副图像所有点都进行映射,随便假设一副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了。

 


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

 

 

 

 

 

传统Hough变换 : 枚举统计法

 

找到通过足够多数量的像素点的所有直线,它分析每个单独的像素,并识别出所有的可能经过它的直线。

当同一条直线穿过许多点,便意味着这条线的存在足够明显。

 

累加器

累加器就是用来记录某条直线被识别了多少次,霍夫曼最直接的想法是,计算所有可能的直线,找出重复数量最多的那几条,重复次数就是识别的阈值。

Hough transform演算步骤(以直线为例):

直线公式: y = ax - b

称(x,y)为图像空间的坐标,(a,b)为参数空间

1. 在图像上找出所有可能的特征点
2. 对于每个特征点
    1. 对于每个a,计算通过(x,y)的所有直线(a,b)    
    2. 在累加器的(a,b)位置上加一
   
   重复步骤2,直到所有的特征点都计算完毕
1. 找到累加器里的最大值
2. 将每一个极大值,映射回图片上代表的每一条直线

 

极坐标
OpenCV实际采用的是极坐标(r,s),以左上角为原点:

    r = xcos(s) + ysin(s)
   
代码:

 // 选择特征点 (x, y)
 int x = 50, y = 30;
 // 计算通过它的所有直线
 for (int i = 0; i < 180; i++) {
  double s = i * PI/180. ;
  double r = x*cos(s) + y*sin(s);
  // j对应的r从 -100 到 100
  int j = static_cast<int>(r + 100.5);
  
  std::cout << i << "," <<j<<std::endl;
  // 填入累加器,极坐标 (j代表r,i代表s)
  acc.at<uchar>(j,i)++;
 }

性能 由上面的数学原理可知,Hough转换是一个运算耗时,需要大量内存的算法

 

概率Hough变换

为了改进原算法,提出了一个优化方案,即概率霍夫曼变换

在原算法的基础上做了少许的修改

1. 不再逐行扫描像素点,而是随机挑选
2. 某个直线达到投票值后,扫描并移除所有经过的点,这次扫描结束后还可以得到线段长度
3. 增加两个参数:线段最小长度、组成连续线段的最大像素间隔

算法复杂度增加,但是参与投票的像素点少了,补偿了整个算法的复杂度。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值