小言_互联网的博客

在OpenCV里实现霍夫直线检测1

568人阅读  评论(0)

在前面已经学习很多轮廓检测,可以分离出很多对象的边缘,接着下来就要利用这些边缘来做一些有意义的事情。比如下图:

从上图可以看到,这是一个马路的照片,为什么要研究这个照片呢?因为近年来人工智能发展得非常快,正在大力推进无人驾驶汽车,要让汽车在车道中间行驶,那么就需要通过摄像头拍摄马路的照片,然后把马路上的车道标识线识别出来,就可以判断汽车是否在车道中间了。那么你也许会问怎么才能判断呢?首先要解决的问题,就是车道两条白线识别出来。经过前面的轮廓分析,可以把边缘选择出来,接着下来就是判断边缘是否在同一条直线上面。由前面可知,这些边缘都是由一些点组成,如下图:

可见在图像空间里,我们就是判断这些点是否在同一条直线上,判断的方法很多种,不过在图像处理里比较常用的是霍夫直线检测,因为它优点是抗干扰能力强,对图像中直线的殘缺部分、噪声以及其它共存的非直线结构不敏感,能容忍特征边界描述中的间隙,并且相对不受图像噪声的影响。

 

霍夫直线检测使用的工具是霍夫变换,简单地来说,它就是把直角坐标系里的直线进行升维表示。它是怎么样的升法呢,其实在直角坐标系里的直线在霍夫空间里是用点来表示,意味着同一条直线上的多个线段在霍夫空间里都会在一个点上,同时也会发现同一条直线上的点,也全在一个点上。那么就是说,判断图像里的点是否在同一条直线上,就相当于判断所有的点在霍夫空间里是否在同一个点上,也就是在霍夫空间里表示的值是否全部相等,这样就可以把相等的点放在一起,把不相等的点分开,这样就可以把图像里的点进行归类,每一类里如果有多于两点,就是表示这些点在同一条直线上面。

 

从上面可以看出,直线到点的投影过程,就是霍夫变换的过程。那么怎么样来从直角坐标系转化为点表示呢?可以看下图:

从上图可以看见,在直角坐标系里的一条直线,可以用(θ,ρ)来表示,这样就把直线与霍夫空间的点相关联起来了。同时从公式里也可以看到直角坐标系上的点与霍夫空间的变换关系,那么接着下来就是怎么样利用这个变换关系?我们来看一下(θ,ρ)的表示,现在来规定一下θ的表示范围,它表示范围是从-90度到90度,ρ是可以表示正负值。

 

假如给出三个点(1,1)、(2,2)、(3,3),要判断是否在一条直线上,在霍夫空间里是怎么样操作呢?接着下来,就来解决这个问题。我们知道在直角坐标系里过一点可以作出无数条直线,因此这无数条直接线在霍夫空间里就是一条曲线,因为无数条直线,就表示转换过来无数的点。如下图所示:

上图里过点(x1,y1)蓝点可以有无数条蓝色的直线,所以在右边的霍夫空间里表示为无数个蓝色的点,同理红色点也一样。在这里会发现这样一个现象,虽然每个点有无数条直线,但是这两个点的组成的直线在霍夫空间里变成相交的一点了。因此,直角坐标系上点是否同线,在霍夫空间里变成是否相交于一点。这一点就是伟大的发明点。

 

通过上面的分析,就可以把过三个点(1,1)、(2,2)、(3,3)的所有直线表示出来。但是还有一个困难问题,怎么样在数字世界里表示无限?目前看来是没有办法的,那么怎么办呢?那就是采取近似计算的方法。比如从-90度到90度,如果无限细分可以表示无限个数,如果近似地采用1度来步进,就只表示180个数。因此,我们只需要按照需求表示的精度,就可以划分不同的个数。在这里度数采用1度为步进,同样道理ρ的表示也要限制表示,一般采用1为步进,那么它的最大值根据点到直线的距离可知,最大值不会大于原点到坐标点的距离。有了这两个限制,就可以把霍夫空间进行分割,如下图:

有了角度θ范围和ρ的长度范围,那么就可以使用程序把这三点(1,1)、(2,2)、(3,3)所有的直线作出来,也就是相当于在霍夫空间里画出每一点的曲线。也就是把所有的点放到上面的表格里面,最后统计每一个格里有多少个点,因为每一格里的点都是在同一个直线上。其实这个过程就是枚举每一个直角坐标点的所有直线的过程,如果过每点有一条直线相同,肯定就会落在公共的一格里。到这里就完成了霍夫直线检测过程。

可以使用下面的例子来帮助理解:

#python 3.7.4,opencv4.1
#蔡军生 https://blog.csdn.net/caimouse/article/details/51749579
#
import cv2
import numpy as np
from scipy import signal

def hough_line(img):
  # Rho 和 Theta 范围
  thetas = np.deg2rad(np.arange(-90.0, 90.0))#角度从-90到90度
  width, height = img.shape
  print(width,height)
  diag_len = round(np.sqrt(width * width + height * height))   #图像最大极径距离
  print(diag_len)
  rhos = np.linspace(-diag_len, diag_len, diag_len * 2.0)

  # 计算所有角度值
  cos_t = np.cos(thetas)
  sin_t = np.sin(thetas)
  num_thetas = len(thetas)
  
  num_Rho = int(2 * diag_len) #计算极径细分个数
  # 霍夫空间分网格,以便统计每个投票数量
  accumulator = np.zeros((num_Rho, num_thetas), np.uint64)
  y_idxs, x_idxs = np.nonzero(img)  # (row, col) ,找出非零值的坐标

  # 对所有非0点进行投票
  for i in range(len(x_idxs)):
    x = x_idxs[i]
    y = y_idxs[i]

    for t_idx in range(num_thetas):
      # 计算每一个角度对应的rho值
      rho = round(x * cos_t[t_idx] + y * sin_t[t_idx]) + diag_len
      
      n = int(rho)
      accumulator[n, t_idx] += 1 #投票增加

  return accumulator, thetas, rhos
#
image = np.zeros((150,150))
image[10:140, 10:140] = np.eye(130)*255

accumulator, thetas, rhos = hough_line(image)

# 找出最大投票的索引
idx = np.argmax(accumulator) #argmax默认返回线性展开的索引值
m = idx // accumulator.shape[1] #计算第几行
rho = rhos[m]
theta = thetas[idx % accumulator.shape[1]] #计算第几列
print('m={}-{}'.format(m,idx % accumulator.shape[1]))
print("rho={0:.2f}, theta={1:.0f}".format(rho, np.rad2deg(theta)))

#显示投票结果
grayA = accumulator.astype(np.uint8)
grayA = cv2.equalizeHist(grayA)

cv2.imshow('accu', grayA)

#显示原图
cv2.imshow('image', image)

cv2.waitKey(0)
cv2.destroyAllWindows()

结果输出如下:

输入图片

rho=0.50, theta=-45

输出结果

显示投票结果,霍夫空间的分布情况

https://blog.csdn.net/caimouse/article/details/51749579

 


转载:https://blog.csdn.net/caimouse/article/details/102313828
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场