对画面进行裁剪可以有效滤除背景对车道线检测的影响,进行完裁剪后,考虑到车道线有明显的颜色特征,且与其他位置的颜色没有重合,考虑对地面进行颜色识别。主流的色彩空间有RGB、BGR、HSV等,经过测试,使用HSV颜色空间进行颜色提取最为有效。HSV采用色调(H),饱和度(S),明度(V)来表示颜色,其具有更平衡的色差感知,在实际应用中可以发现其对于颜色的识别更加精确。提取颜色之后,画面中可能存在一些噪声,为了保证阈值能够预留一定的空间,此处使用形态学方法对检测效果进行优化,对画面中的各个区块进行先腐蚀后膨胀的操作,即可过滤画面中存在的噪声。下图为初步进行颜色提取后得到的结果。


可以看到,右侧边线同为蓝色调,若想留出一定的阈值空间则需要使用其他方案将其去除,此处我们采用了类似于滑动窗格法进行优化,以实现对于车道线的精确提取。先从画面的底部进行处理,此处的车道线具有的信息最为可靠,从底部开始逐步向上进行处理,将底部识别到的车道线中点作为远端车道线的预期位置,将不在预期位置中的信息滤除,即可提取得到正确的车道线信息。


得到车道线信息后即可对其进行拟合,使用一次函数拟合即可得到车道线的趋势,适用于实际检测环境,若要得到车道线中点数组,可以使用三次函数进行拟合。

# -*- coding: utf-8 -*
import numpy as np
import cv2
import math 
import time
import matplotlib
from skimage import morphology                                                                                                                                                      
cap = cv2.VideoCapture('1.mp4')
step = 35 #滑动窗格的高度
block = 200 #滑动窗格的宽度
height = 280 #裁剪的高度
while True:
    start = time.time()
    _ , frame = cap.read()
    frame1 = frame
    frame = frame[1080 - height:1080]
    #dst = cv2.blur(frame,(5,5))
    dst = cv2.GaussianBlur(frame,(5,5),0) #对图像进行第一次高斯滤波
    #dst = cv2.medianBlur(frame,5) 
    hsv = cv2.cvtColor(dst,cv2.COLOR_BGR2HSV) #使用HSV颜色空间
    lower_blue = np.array([81,79,77])#设置需要识别的车道线颜色的阈值
    upper_blue = np.array([161,255,185])
    lower_white = np.array([30,0,195])
    upper_white = np.array([120,37,255])
    #dark_blue = np.uint8([[[12,22,121]]])
    #dark_blue = cv2.cvtColor(dark_blue,cv2.COLOR_BGR2HSV)

    mask = cv2.inRange(hsv,lower_blue,upper_blue) + cv2.inRange(hsv,lower_white,upper_white) #提取所需颜色
    mask = morphology.erosion(mask,morphology.square(11)) #形态学方法滤除噪声
    #cv2.imshow('mask1', mask)
    for i in range(0, height, step): #滑动窗格法优化
        mask1 = mask[height - (i + step):height - i]
        nonzero1 = np.nonzero(mask1)
        mid = np.median(nonzero1[1])
        if (math.isnan(mid)):
            continue
        mid = int(mid)
        #print(np.median(nonzero1[1]))
        mask[height - (i + step):height - i, 0 : mid - block] = 0 
        mask[height - (i + step):height - i, mid + block : 1920] = 0
        mask[height - (i + step * 2):height - (i + step), 0 : mid - block] = 0 
        mask[height - (i + step * 2):height - (i + step), mid + block : 1920] = 0
    out = frame
    nonzero = np.nonzero(mask[0: 240])
    x = nonzero[0]
    y = nonzero[1]
    if (x != [] and y != []):
        z1 = np.polyfit(x, y, 1)#用1次多项式拟合
        p1 = np.poly1d(z1)
        for t in range(x.min(), x.max()):
            y_ = np.int(p1(t))
            #print(y_)
            cv2.circle(out, (y_, t), 1, (0, 0, 255), 1, 8, 0) #绘制拟合好的曲线
    frame1[800:1080] = out
    #res = cv2.bitwise_and(frame,frame,mask=mask)
    cv2.imshow('frame',frame1)
    cv2.imshow('mask', mask)
    #cv2.imshow('res', res)
    k = cv2.waitKey(5) & 0xff
    #if k == 27:
    #    break
    end = time.time()
    seconds = end - start
    print("Time taken : {0} seconds".format(seconds)) 
    # 计算FPS,alculate frames per second
    fps  = 1 / seconds;
    print("Estimated frames per second : {0}".format(fps))

cv2.destroyAllWindows()
cap.release()


「雪霁融雾月,冰消凝夜雨」