[CV] 霍夫变换直线检测
霍夫变换直线检测
理论
直线的斜截式 \(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\theta+ysin\theta=\rho\),而\(0<=\theta<=2\pi,0<=\rho<=\rho_{max}\),我们的问题现在变成了给定点\((x_i,y_i)\)寻找直线\((\rho,\theta)\),现在参数范围有限了,我们比较容易设计累加器数组了。
同之前的分析,图像空间中的一个点,变成了参数空间中的一个余弦曲线(不分正弦余弦了),而图像空间中的一条直线变成参数空间中的一个点。那和之前还是一样,拟合多个点,找参数空间中最多余弦曲线相交的点即可。如图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()