[Computer Vision]霍夫变换直线检测

霍夫变换直线检测

理论

直线的斜截式 (y=mx+b),在正常的图像坐标系(这里可以先把图像空间看作是连续的)中,(x,y)是变量,(m,b)是参数,即坐标轴分别为(x,y)轴。现在,我们可以设定一个直线的参数空间,换一种看法,将坐标轴换为(m,b),于是有(b=y-mx)(x,y)成了参数,这样,在图像空间中的一条直线,就对应了参数空间中的一个点,例如直线是(y=x+1),对应参数空间中的点((1,1))如图1所示。

那么如果是在图像空间中的一个点呢?对应到直线的参数空间里是什么呢?

如果在图像空间中只有一个点(例如((1,1))),那这个点在哪条直线上呢?穿过这个点的直线都满足要求。将(x,y)看作是参数,所以现在有(b=y-mx->b=1-m)也就是说图像空间中的一个点对应参数空间中的一条直线。

那么如果在图像空间中,有多个点,那么在参数空间中就有多条直线,如图2所示

参数空间中4条直线,代表着图像空间中,4个点所在的所有可能直线的参数,其中最多有3条直线交在了1点,这表示,若选取交点作为直线参数,那么这条直线可以通过图像空间中的3个点。因此我们选择这条直线作为这4个点的拟合直线。这就是投票机制,选择一个可以让尽可能多的点通过的直线。这样可以避免一些噪声点的影响。

算法

1.离散化参数空间
2.创建一个累加器数组A(m,b),每个元素代表参数空间中的一个点,即图像空间中直线的参数
3.将累加器数组A(m,c)全部置为0
4.for each image edge(x_i,y_i):
    for each element in A(m,c):
        if (m,c)在直线 c = -x_im + yi上:
            A(m,c) = A(m,c)+1 #意味着直线经过这个点
5.在A(m,c)中寻找局部最大值,即局部直线相交最多的点,因为是局部最大值,所以可以找到多条直线

似乎到这里就找到了一个算法,但是仔细想想,累加器应该设置为多大呢?m和b都可以任意取值,那这个累加器可太大了。

我们还有另一种直线的参数表达方式(xcos heta+ysin heta= ho),而(0<= heta<=2pi,0<= ho<= ho_{max}),我们的问题现在变成了给定点((x_i,y_i))寻找直线(( ho, heta)),现在参数范围有限了,我们比较容易设计累加器数组了。

同之前的分析,图像空间中的一个点,变成了参数空间中的一个余弦曲线(不分正弦余弦了),而图像空间中的一条直线变成参数空间中的一个点。那和之前还是一样,拟合多个点,找参数空间中最多余弦曲线相交的点即可。如图3所示

算法

图4为图像空间的点到参数空间的曲线

python opencv测试代码

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('board.jpg')#读入图片
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#灰度图像 
gray = cv2.GaussianBlur(gray,(9,9),1.5)#先进行高斯模糊,防止噪点影响 
edges = cv2.Canny(gray,50,150)#边缘检测
plt.subplot(121),plt.imshow(edges,'gray')
plt.xticks([]),plt.yticks([])
#hough transform
lines = cv2.HoughLines(edges,0.5,np.pi/180,80)#霍夫变换检测直线
print(lines)

lines1 = lines[:,0,:]#提取为为二维
for rho,theta in lines1[:]: #将检测到的直线画出来
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a)) 
    cv2.line(img,(x1,y1),(x2,y2),(255,0,0),1)

plt.subplot(122),plt.imshow(img,)
plt.xticks([]),plt.yticks([])
plt.show()
原文地址:https://www.cnblogs.com/WAoyu/p/13070414.html