基本原理
- 霍夫变换,是将座标由直角座标系变换到极座标系,然后再根据数学表达式检测某些形状(如直线和圆)的方法。当直线上的点变换到极座标中的时候,会交于一定的r、t的点。这个点即为要检测的直线的参数。通过对这个参数进行逆变换,我们就可以求出直线方程。
基本步骤
- 我们用边缘图像来对边缘像素进行霍夫变换。
canny边缘检测步骤
- 进行高斯滤波
- 两个方向上进行sobel滤波
- 将两个方向上sobel滤波之后的两个矩阵变换为一个梯度幅度矩阵和一个梯度方向矩阵
- 利用梯度幅度矩阵和梯度方向矩阵进行非极大值抑制
- 对上一步进行非极大值抑制之后的梯度幅度矩阵进行阈值滞后,这便是canny边缘检测的完整步骤
- 在霍夫变换后获取值的直方图并选择最大点。
- 对极大点的r和t的值进行霍夫逆变换以获得检测到的直线的参数。
详细步骤
- 求出图像的对角线长rmax;
- 在边缘点(x,y)处,t取遍[0,179],根据下式执行霍夫变换:
rho=xcos(t)+ysin(t)
,做一个180×rmax
大小的表,将每次按上式计算得到的表格(t,r)处的值加1。换句话说,这就是在进行投票。票数会在一定的地方集中。
- 在该表中,如果遍历到的像素的投票数大于其8近邻的像素值,则它不变。如果遍历到的像素的投票数小于其8近邻的像素值,则设置为0。
- 极大值点(r,t)通过下式进行逆变换:
y=−xcos(t)/sin(t)+rx/sin(t)=−ysin(t)/cos(t)+r/cos(t)
- 对于每个局部最大点,使
y=0−H−1,x=0−W−1
,然后执行上式中的逆变换,并在输入图像中绘制检测到的直线。请将线的颜色设置为红色(R,G,B)=(255,0,0)
。
def Hough_Line_step1(edge):
## Voting
def voting(edge):
H, W = edge.shape
drho = 1
dtheta = 1
# get rho max length
rho_max = np.ceil(np.sqrt(H ** 2 + W ** 2)).astype(np.int)#计算大于等于该值的最小整数,这里需要解释一下,rho_max即为sqrt(a**2+b**2)
#是因为后面的公式rho=asint+bcost,可以用高中知识轻易证明,rho的绝对值要小于rho_max
# hough table#计算霍夫表格
hough = np.zeros((rho_max * 2, 180), dtype=np.int)#生成霍夫表格的尺#寸,因此可以看到这里hough的高度为二倍的rho_max,也就很容易理解后面的hough[rho+rho_max,theta]+=1了
# get index of edge
ind = np.where(edge == 255)#取得边缘像素点的索引
## hough transformation
for y, x in zip(ind[0], ind[1]):
for theta in range(0, 180, dtheta):
# get polar coordinat4s
t = np.pi / 180 * theta#依次计算角度的弧度值
rho = int(x * np.cos(t) + y * np.sin(t))#依次计算不同弧度值对应的不同的rho值
# vote
hough[rho + rho_max, theta] += 1#进行投票,票数在一定地方集中
out = hough.astype(np.uint8)
return out
# voting
out = voting(edge)
return out
第一步完成后,需要对投票结果进行非极大值抑制,和canny边缘检测最后一步的阈值滞后中的落入高阈值与低阈值区间的处理方法极为相似,即抽取八邻域与中间点做比较,如果有比中间像素值大的情况,中间像素值置为0
# non maximum suppression
def non_maximum_suppression(hough):
rho_max, _ = hough.shape #得到hough图像的尺寸
## non maximum suppression
for y in range(rho_max):
for x in range(180):
# get 8 nearest neighbor
x1 = max(x - 1, 0)#非常巧妙的处理,兼顾了边缘和中央区域
x2 = min(x + 2, 180)#非常巧妙的处理,兼顾了边缘和中央区域
y1 = max(y - 1, 0)#非常巧妙的处理,兼顾了边缘和中央区域
y2 = min(y + 2, rho_max - 1)#非常巧妙的处理,兼顾了边缘和中央区域
if np.max(hough[y1:y2, x1:x2]) == hough[y, x] and hough[y, x] != 0:
pass
# hough[y,x] = 255
else:
hough[y, x] = 0
return hough
最后一步,也就是根据特征点把线画在原图上的一步,叫rough反变换
def inverse_hough(hough, img):#将经历第一步第二步过程的rough图像和原图像作为参数输入进来
H, W, _ = img.shape#原图像尺寸
rho_max, _ = hough.shape#hough图像尺寸
out = img.copy()#复制一个
# get x, y index of hough table
ind_x = np.argsort(hough.ravel())[::-1][:20]#按行重组倒序取前20个值得索引坐标
ind_y = ind_x.copy()
thetas = ind_x % 180#取得角度
rhos = ind_y // 180 - rho_max / 2#取得rhos值,这个不必解释,很好理解,和前面的第一个函数结合起来看非常明显
# each theta and rho#坐标系转换
for theta, rho in zip(thetas, rhos):
# theta[radian] -> angle[degree]
t = np.pi / 180. * theta#算出t
# hough -> (x,y)
for x in range(W):
if np.sin(t) != 0:
y = - (np.cos(t) / np.sin(t)) * x + (rho) / np.sin(t)#依据x逐次算出y的坐标
y = int(y)
if y >= H or y < 0:
continue
out[y, x] = [0, 0, 255]#在图中表示出来
for y in range(H):#依据y逐步算出x的坐标
if np.cos(t) != 0:
x = - (np.sin(t) / np.cos(t)) * y + (rho) / np.cos(t)
x = int(x)
if x >= W or x < 0:
continue
out[y, x] = [0, 0, 255]#将其表示出来
out = out.astype(np.uint8)
return out
下面我们来总结一下霍夫检测直线的具体步骤
- 进行高斯滤波
- 两个方向上进行sobel滤波
- 将两个方向上sobel滤波之后的两个矩阵变换为一个梯度幅度矩阵和一个梯度方向矩阵
- 利用梯度幅度矩阵和梯度方向矩阵进行非极大值抑制
- 对上一步进行非极大值抑制之后的梯度幅度矩阵进行阈值滞后
至此,canny边缘检测任务完成,hough变换是建立在canny边缘检测的基础上的
- 建立hough矩阵,具体构建方法见上述说明。细节:
hough
角度0-180
,rho_max
为2sqrt(a**2+b**2)
,依次遍历进行投票
- 对hough矩阵进行非极大值抑制(8像素领域)
- 排序输出,hough前n个值取其rho及角度thetas,由这两个值反向推的x,y坐标
至此,hough检测的任务便完成了
网友评论