我们在前面的《图像的颜色选择》、《图像的感兴趣区域》中提到了对车道线的检测。
通过对原始行车图像进行颜色选择和感兴趣区域的提取,得到了如下的车道线:
我们的车道线当然是一左一右两条线。那怎样从这个车道线图像中提取出这2条线呢?
这就要谈到“霍夫变换(Hough Transfrom)”。霍夫变换是1972年提出来的,最开始就是用来在图像中过检测直线,后来扩展能检测圆、曲线等。
我们在初中数学中了解到,一条直线可以用如下的方程来表示:y=kx+b,k是直线的斜率,b是截距。
图像是一个个离散的像素点构成的,如果在图像中有一条直线,那也是一系列的离散点构成的。那么怎样检测这些离散的点构成了直线呢?
我们再看上面的直线方程:y=kx+b,(x,y)就是点。我们转换下变成:b=-kx+y。我们是不是也可以把(k,b)看作另外一个空间中的点?这就是k-b参数空间。
我们看到,在x-y图像空间中的一个点,变成了k-b参数空间中的一条直线,而x-y图像空间中的2点连成的直线,变成了k-b参数空间中的一个交点。
如果x-y图像空间中有很多点在k-b空间中相交于一点,那么这个交点就是我们要检测的直线。这就是霍夫变换检测直线的基本原理。
当然,有一个问题需要注意,图像空间中如果一条直线是垂直的,那么斜率k是没有定义的(或者说无穷大)。为了避免这个问题,霍夫变换采用了另一个参数空间:距离-角度参数空间。
我们在中学中学过,平面上的一个点也可以用距离-角度来定义,也就是极坐标:
那么在图像中,每一个点都可以用距离和角度来表达:
但是,使用距离-角度后,点(x,y)与距离,角度的关系变成了:
于是,在新的距离-角度参数空间中,图像中的一个点变成了一个正弦曲线,而不是k-b参数空间中的直线了。这些正弦曲线的交点就是图像空间中我们要检测的直线了。
对于最开始的图像,我们先用Canny进行边缘检测,较少图像空间中需要检测的点数量:
lane = cv2.imread("final_roi.png")
# 高斯模糊,Canny边缘检测需要的
lane = cv2.GaussianBlur(lane, (5, 5), 0)
# 进行边缘检测,减少图像空间中需要检测的点数量
lane = cv2.Canny(lane, 50, 150)
cv2.imshow("lane", lane)
cv2.waitKey()
import numpy as np
rho = 1 # 距离分辨率
theta = np.pi / 180 # 角度分辨率
threshold = 10 # 霍夫空间中多少个曲线相交才算作正式交点
min_line_len = 10 # 最少多少个像素点才构成一条直线
max_line_gap = 50 # 线段之间的最大间隔像素
lines = cv2.HoughLinesP(lane, rho, theta, threshold, maxLineGap=max_line_gap)
line_img = np.zeros_like(lane)
for line in lines:
for x1, y1, x2, y2 in line:
cv2.line(line_img, (x1, y1), (x2, y2), 255, 1)
cv2.imshow("line_img", line_img)
cv2.waitKey()