通过对原始行车图像进行颜色选择和感兴趣区域的提取,得到了如下的车道线:

EDlines直线检测算法_Hough


我们的车道线当然是一左一右两条线。那怎样从这个车道线图像中提取出这2条线呢?

这就要谈到“霍夫变换(Hough Transfrom)”。霍夫变换是1972年提出来的,最开始就是用来在图像中过检测直线,后来扩展能检测圆、曲线等。

我们在初中数学中了解到,一条直线可以用如下的方程来表示:y=kx+b,k是直线的斜率,b是截距。

图像是一个个离散的像素点构成的,如果在图像中有一条直线,那也是一系列的离散点构成的。那么怎样检测这些离散的点构成了直线呢?

我们再看上面的直线方程:y=kx+b,(x,y)就是点。我们转换下变成:b=-kx+y。我们是不是也可以把(k,b)看作另外一个空间中的点?这就是k-b参数空间。

EDlines直线检测算法_图像空间_02


我们看到,在x-y图像空间中的一个点,变成了k-b参数空间中的一条直线,而x-y图像空间中的2点连成的直线,变成了k-b参数空间中的一个交点。

如果x-y图像空间中有很多点在k-b空间中相交于一点,那么这个交点就是我们要检测的直线。这就是霍夫变换检测直线的基本原理。

当然,有一个问题需要注意,图像空间中如果一条直线是垂直的,那么斜率k是没有定义的(或者说无穷大)。为了避免这个问题,霍夫变换采用了另一个参数空间:距离-角度参数空间。

我们在中学中学过,平面上的一个点也可以用距离-角度来定义,也就是极坐标:

EDlines直线检测算法_EDlines直线检测算法_03


那么在图像中,每一个点都可以用距离和角度来表达:

EDlines直线检测算法_图像空间_04


但是,使用距离-角度后,点(x,y)与距离,角度的关系变成了:

ρ=xcosθ+ysinθ ρ = x c o s θ + y s i n θ


于是,在新的距离-角度参数空间中,图像中的一个点变成了一个正弦曲线,而不是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()

EDlines直线检测算法_EDlines直线检测算法_05

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()

EDlines直线检测算法_Hough_06