霍夫变换原理

本文转载自知乎专栏(四十八)通俗易懂理解——霍夫变换原理 - 知乎

霍夫变换主要用于,对付图片数据,能够提供一种方式找到直线/圆,广义上的霍夫变换可以找到你想要的任何你可以描述的特征。

学完霍夫变换,我认为精髓就在于把握特征变换。

一、霍夫变换(Hough)

  A-基本原理

一条直线可由两个点A=(X1,Y1)和B=(X2,Y2)确定(笛卡尔坐标)

另一方面,y = kx + q,也可以写成关于(k,q)的函数表达式(霍夫空间):

对应的变换可以通过图形直观表示:

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点

反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

再来看看A、B两个点,对应霍夫空间的情形:

一步步来,再看一下三个点共线的情况:

可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。

如果不止一条直线呢?再看看多个点的情况(有两条直线):

其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式选择由尽可能多直线汇成的点

看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是

的参数,给出对比图:

在极坐标系中表示直线的方程为ρ=xCosθ+ySinθ(ρ为原点到直线的距离),如图所示:

如上图,假定在一个8*8的平面像素中有一条直线,并且从左上角(1,8)像素点开始分别计算θ为0°、45°、90°、135°、180°时的ρ,图中可以看出ρ分别为1、(9√2)/2、8、(7√2)/2、-1,并给这5个值分别记一票,同理计算像素点(3,6)点θ为0°、45°、90°、135°、180°时的ρ,再给计算出来的5个ρ值分别记一票,此时就会发现ρ = (9√2)/2的这个值已经记了两票了,以此类推,遍历完整个8*8的像素空间的时候ρ = (9√2)/2就记了5票, 别的ρ值的票数均小于5票,所以得到该直线在这个8*8的像素坐标中的极坐标方程为 (9√2)/2=x*Cos45°+y*Sin45°,到此该直线方程就求出来了。(PS:但实际中θ的取值不会跨度这么大,一般是1度)。

也就是说我们遍历θ的值,从0-180°,并且同时代入(x,y)的值,求得对应的ρ。

找到0-180°中,哪个度数下的ρ值相同的数量最多。这反向说明了,在一个ρ和θ组成的函数中,符合的点数最多。

python实现:

#生成80*160的图片
def data_img(data,row=40,col=160):
    image = np.zeros((row*2,col))
    for i in range(len(data)):
        if abs(data[i][0]) > row:
            continue
        else:
            image[int(data[i][0]) + row][int(data[i][1])] = 255
    return image

def hough_detectline(img):
    thetas=np.deg2rad(np.arange(0,180))
    row,cols=img.shape
    diag_len=np.ceil(np.sqrt(row**2+cols**2))
    rhos=np.linspace(-diag_len,diag_len,int(2*diag_len))
    cos_t=np.cos(thetas)
    sin_t=np.sin(thetas)
    num_theta=len(thetas)
    #vote
    vote=np.zeros((int(2*diag_len),num_theta),dtype=np.uint64)
    #返回非0位置索引
    y_inx,x_inx=np.nonzero(img)
    #vote in hough space
    for i in range(len(x_inx)):
        x=x_inx[i]
        y=y_inx[i]
        for j in range(num_theta):
            rho=round(x*cos_t[j]+y*sin_t[j])+diag_len
            if isinstance(rho,int):
                vote[rho,j]+=1
            else:
                vote[int(rho),j]+=1
    return vote,rhos,thetas

#从霍夫矩阵中找到最长的一条线
def longest_line(accumulator,rhos,thetas):
    #look for peaks
    idx = np.argmax(accumulator)
    ##下面两句是寻找投票器最大值所对应的行与列,最大值对应的行就是rho的索引,对应的列就是theta的索引
    #可以用这句代替:row,col=np.unravel_index(idx,ccumulator.shape)
    #rho=rho[row],theta=theta[col]
    rho = rhos[int(idx/accumulator.shape[1])]
    theta = thetas[idx % accumulator.shape[1]]
    k=-np.cos(theta)/np.sin(theta)
    b=rho/np.sin(theta)
    return k,b

大神博客:霍夫变换 - 疯狂奔跑 - 博客园

参考博客:https://www.cnblogs.com/lvchaoshun/

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值