Opencv-Python处理车道线检测

利用Opencv和Python结合完成车道线检测

1 前言

去年对Opencv系统学习了一段时间,后面没有继续更新博客,但自己也有继续学习啦,哈哈,最近做了一个小项目,利用图像处理算法解决车道线检测。但目前自己深知这只是个基础的初级状态,还有很多不足的地方,后面会更新一篇利用深度神经网络完成车道线检测的项目,检测效果比这里要好很多,这里先把图像处理算法的完成流程和经过介绍清楚。
本方法有较大的局限性,适合在前方车辆较少的情况下,且路面车道线较清晰完整,因为用hough变换的方法,因此仅对于直线车道线检测较为理想。

2 项目目标

对一段行车记录仪的视频图像采集到的行驶中的视频,利用cv2的视频按帧分割,对每一帧进行算法处理,检测出车道线用颜色填充在车道内。标记车道线后合成视频即可,但在实际应用中需要实时跟随车道、车道线偏离时产生预警等应用,因此需要根据实际车辆位置与车道线判定是否偏离车道线。

3 具体步骤

1、CCD视频摄像机校准
2、读视频,转成按每一帧读取,图像预处理
3、图像灰度化
4、高斯平滑,减少边缘干扰
5、利用canny算子,进行边缘检测
6、设定感兴趣区域,减少运算量
7、利用hough变换,进行直线检测
8、将检测成功的直线和原图像融合

4 具体实现

1、相机校准

CCD相机校准这里就不介绍了,后面会有一篇专门介绍相机校准方法的博文。

2、视频读入和输出

前面介绍过Opencv的读写方法,这里说的简单些。

import  cv2
img = cv2.imread('./test4')
3、转化为灰度图

当光线柔和时,公路与车道线的亮度和色调相差较大时可以用转换成灰度图,效果显著;当日照光线强烈时,黄色车道线容易与路面亮度相近时,转成灰度图再提取边缘的方法并不显著,可以考虑转换成HSV空间。
OpenCV定义了,图像的原点(0,0)在图片的左上角,横轴为X,朝右,纵轴为Y,朝下,如下图所示。

在这里插入图片描述
上面说的高亮度的情况如下图所示。路面车印较多,且车道线消失较多,黄色车道线与公路亮度相近,转为灰度图后几乎难以显示出黄色车道线,所以下图中视觉处理的环境条件较差。

在这里插入图片描述

4、灰度处理

调用OpenCV中提供的cvtColor()函数,能够方便地对图像进行灰度处理。如果路面的亮度值很大,可以转换成HSV色度空间。这里以灰度处理为例。

#取图灰度化
grap = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
5、利用Canny算子进行边缘处理

为了突出车道线,我们对灰度化后的图像做边缘处理。
“边缘”就是图像中明暗交替较为明显的区域。
车道线通常为白色或黄色,地面通常为灰色或黑色,因此车道线的边缘处会有很明显的明暗交替。
常用的边缘提取算法有Canny算法和Sobel算法,它们只是计算方式不同,但实现的功能类似。可以根据实际要处理的图像,选择算法。哪种算法达到的效果更好,就选哪种。
以Canny算法为例,选取特定的阈值后,对灰度图像进行处理,即可得到的边缘提取的效果图。

#Canny边缘算法
canny_lthreshold = 150
canny_hthreshold = 250
edges = cv2.Canny(blur_gray,canny_lthreshold,canny_hthreshold)

在这里插入图片描述

6、设置ROI区域

提取边缘后,发现图像中有很多无用的边缘信息,我们可以定义一个感兴趣的区域,将无用的边缘信息过滤掉。
对每个像素点的坐标值进行遍历,如果发现当前点的坐标不在三角区域内,则将该点涂“黑”,即将该点的像素值置为0。
这个就是常提到的掩膜,可以看这个链解中的介绍,我觉得讲的很清晰。
https://www.cnblogs.com/skyfsm/p/6894685.html
截取ROI区域的功能,可以封装为一个函数,方便我们调用。

#定义一个和输入图像同样大小的全黑图像mask,这个mask也称掩膜
def roi_mask(img,vertics):
	mask = np.zero_like(img)
	#根据输入图像的通道数,忽略的像素点是多通道的白色,还是单通道的白色
	if len(img.shape) > 2:
		channel_count = img.shape[2]
		mask_color = (255,)*channel_count
	else:
		mask_color = 255
	cv2.fillPoly(mask,[vertics],mask_color)
	masked_img = cv2.bitwise_and(img,mask)
	return masked_img

将感兴趣的区域用多边形的方式圈出,输入到提取感兴趣区域的函数中,即可完成对边缘的清理。

#图像像素行数 rows = canny_image .shape[0]  720行
#图像像素列数 cols = canny_image .shape[1]  1280列
left_bottom = [0, canny_image .shape[0]]
right_bottom = [canny_image .shape[1], canny_image .shape[0]]
apex = [canny_image .shape[1]/2, 310]
vertices = np.array([ left_bottom, right_bottom, apex ], np.int32)
roi_image = roi_mask(img, vertices)

输出的结果为:
在这里插入图片描述
可见边缘提取后的车道线,右边的虚线车道线并不完整,可以通过hough直线变换把直线车道线重新描绘出来。

7、Hough变换

Hough变换是一种特征匹配方法,其实现直线检测的机理可以参考这篇博文:https://blog.csdn.net/yuyuntan/article/details/80141392
OpenCV为我们提供了霍夫变换检测直线的函数,可以通过设置不同的参数,检测不同长度的线段。由于车道线存在虚线的可能,因此线段的检测长度不能设置地太长,否则短线段会被忽略掉。
Houhg直线检测函数

// Hough Line Detection
rho = 1
theta = np.pi/180
threshold = 15
min_line_lenght = 40
max_line_gap = 20
line_img = hough_lines(roi_image,rho,theta,threshold,min_line_lenght,max_line_gap)

封装一个画直线的函数

def draw_lines(img,lines,color = [0,0,255],thickness = 2):
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(img,(x1,y1),(x2,y2),color,thickness)

把画出的线和原图融合

import numpy as np
line_image = np.copy(img) # 复制一份原图,将线段绘制在这幅图上
draw_lines(line_image, lines, [255, 0, 0], 6)

在这里插入图片描述从图中可以看出来,右侧不连续的车道线也被检测出来,然而并没有把断开的车道线显示为连续的直线。所以需要优化一下画线函数。
考虑从以下两部分优化函数
1.计算左右车道线的直线方程
根据每个线段在图像坐标系下的斜率,判断线段为左车道线还是右车道线,并存于不同的变量中。随后对所有左车道线上的点、所有右车道线上的点做一次最小二乘直线拟合,得到的即为最终的左、右车道线的直线方程。最小二乘拟合讲解可参考:https://blog.csdn.net/nienelong3319/article/details/80894621
np.polyfit(X, Y, 1) #一次多项式拟合,相当于线性拟合
2.计算左右车道线的上下边界
考虑到现实世界中左右车道线一般都是平行的,所以可以认为左右车道线上最上和最下的点对应的y值,就是左右车道线的边界。

def draw_lines(img, lines, color=[255, 0, 0], thickness=2):
    left_lines_x = []
    left_lines_y = []
    right_lines_x = []
    right_lines_y = []
    line_y_max = 0
    line_y_min = 999
    for line in lines:
        for x1,y1,x2,y2 in line:
            if y1 > line_y_max:
                line_y_max = y1
            if y2 > line_y_max:
                line_y_max = y2
            if y1 < line_y_min:
                line_y_min = y1
            if y2 < line_y_min:
                line_y_min = y2
            k = (y2 - y1)/(x2 - x1)
            if k < -0.3:
                left_lines_x.append(x1)
                left_lines_y.append(y1)
                left_lines_x.append(x2)
                left_lines_y.append(y2)
            elif k > 0.3:
                right_lines_x.append(x1)
                right_lines_y.append(y1)
                right_lines_x.append(x2)
                right_lines_y.append(y2)
    #最小二乘直线拟合
    left_line_k, left_line_b = np.polyfit(left_lines_x, left_lines_y, 1)
    right_line_k, right_line_b = np.polyfit(right_lines_x, right_lines_y, 1)
 
    #根据直线方程和最大、最小的y值反算对应的x
    cv2.line(img,
             (int((line_y_max - left_line_b)/left_line_k), line_y_max),
             (int((line_y_min - left_line_b)/left_line_k), line_y_min),
             color, thickness)
    cv2.line(img,
             (int((line_y_max - right_line_b)/right_line_k), line_y_max),
             (int((line_y_min - right_line_b)/right_line_k), line_y_min),
             color, thickness)

由上,即可得到符合输出要求的两条直线方程的斜率、截距和有效长度。将后处理后的结果绘制在原图上。
在这里插入图片描述

8、视频处理

视频其实就是一帧帧连续不断的图像,使用读取视频的库,将视频截取成一帧帧图像,然后使用上面的灰度处理、边缘提取、感兴趣区域选择、霍夫变换和数据后处理,得到车道线检测结果,再将图片结果拼接成视频,就完成了视频中的车道线检测。
利用前文提到的车道线检测算法得到的视频处理结果如下:

初级车道线检测

5 结论

经过程序编写和调试,发现很多参数需要调整,而且路面的光照、树影、车道线完整度对检测结果影响较大。所以一套参数可能会有条件改变后不再适用的情况,所以鲁棒性较差。而且Hough变换检测直线的算法,在行驶在弯道上时效果较差,所以这套基本的入门算法需要完善和优化。下一篇中将尝试使用更高级的算法。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值