车道线检测——直线

车道线检测——直线

参考文章:(https://mp.weixin.qq.com/s/BpCF8n4wNaNZE-UF_SONzg) 。 笔者在实现过程中根据自己理解做了部分改动。

本文主要讲述智能驾驶领域的应用之一——使用传统机器学习方法检测(直线)车道线,编程语言是:python。

智能驾驶汽车的车载摄像头相对于水平路面是固定的,所以可以较容易找到感兴趣区域(Region of Interest)。

处理步骤:

一、载入图像,灰度处理,并用canny算子提取边缘:

1、我们使用opencv库读入图像,此时图像即转化为一二维数组,数值为0-255,每个数组元素包含三个通道,分别对应B、G、R,即blue、green、red三原色。

2、为便于计算,我们根据实际工程需求,将载入的彩色图片进行灰度处理。坐标为(x,y)的像素点进行灰度化操作的具体计算公式如下:
在这里插入图片描述
3、我们使用canny算子,对灰度图进行边缘提取,canny算子源程序:(https://blog.csdn.net/weixin_38553390/article/details/103810626)。
canny算子实现基本需要四个步骤:
(1)高斯滤波对图像进行平滑处理;
(2)sobel算子计算图像的梯度幅值;
(3)对图像的梯度幅值进行非极大值抑制;
(4)滞后阈值处理进行边缘链接;

4、载入图片如下图所示
在这里插入图片描述
处理后的效果如下图所示
在这里插入图片描述
5、代码块如下

import cv2
import numpy as np

img=cv2.imread('D:\pycharm\exercise\spring festival holiday\lane_line.jpg')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
low_threshold=40
high_threshold=150
canny_image=cv2.Canny(gray,low_threshold,high_threshold)
cv2.imshow('canny_image',canny_image)
cv2.imwrite('D:\pycharm\exercise\spring festival holiday\lane_line2.jpg',canny_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

二、截取感兴趣区域

为了降低环境因素对结果的干扰,我们需要截取出感兴趣的区域(Region of Interest,简称ROI)。

截取ROI的方法,参考链接:(https://www.cnblogs.com/skyfsm/p/6894685.html),详细方法如下:
(1)建立与原图一样大小的mask图像,并将所有像素初始化为0;
(2)将mask图中的ROI区域的所有像素值设置为255;
(3)将该mask图像与经过canny算子处理过的图像进行与运算,得到的结果图只留下原始图感兴趣区域的图像了。也正如下图所示。
在这里插入图片描述
源程序参考:https://github.com/udacity/CarND-LaneLines-P1/blob/master/P1.ipynb
为了实现截取功能,可以将这部分程序封装一下OpenCV函数,定义为ROI函数,代码块如下:

def ROI(img,vertices):
    # 定义一个和输入图像同样大小的全黑图像mask,这个mask也称掩膜
    mask=np.zeros_like(img)
    if len(img.shape)>2:
        channel_count=img.shape[2]   # i.e. 3 or 4 depending on your image
        ignore_mask_color=(255,)*channel_count
    else:
        ignore_mask_color=255
    # [vertices]中的点组成了多边形,将在多边形内的mask像素点保留,
    cv2.fillPoly(mask,[vertices],ignore_mask_color)
    # 与mask做"与"操作,即仅留下多边形部分的图像
    masked_image=cv2.bitwise_and(img,mask)
    return masked_image

[rows,cols,channel]=img.shape
print(rows)          #像素的行数
print(cols)          #像素的列数

#图像像素行数 rows = canny_image .shape[0]  296行
#图像像素列数 cols = canny_image .shape[1]  526列
left_bottom=[0,rows]
right_bottom=[cols,rows]
apex=[cols/2, 170]
vertices = np.array([left_bottom, right_bottom, apex], np.int32)
roi_image = ROI(canny_image, vertices)
cv2.imshow('roi_image',roi_image)
cv2.imwrite('D:\pycharm\exercise\spring festival holiday\lane_line3.jpg',roi_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、使用霍夫变换检测出图像中的直线,并进行拟合

霍夫变换是一种特征检测方法,其原理和推导过程可以参看经典霍夫变换(Hough Transform)(https://blog.csdn.net/yuyuntan/article/details/80141392)。
在这里插入图片描述

霍夫变换的一般公式如下
HoughLinesP(image, rho, theta, threshold, lines=None, minLineLength=None, maxLineGap=None) 

image: 必须是二值图像,推荐使用canny边缘检测的结果图像; 
rho: 线段以像素为单位的距离精度,double类型的,推荐用1.0 
theta: 线段以弧度为单位的角度精度,推荐用numpy.pi/180 
threshold: 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。根据情况推荐先用100试试
lines:这个参数的意义未知,发现不同的lines对结果没影响,但是不要忽略了它的存在 
minLineLength:线段以像素为单位的最小长度,根据应用场景设置 
maxLineGap:同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段

由于霍夫变换只能检测有车道线的地方,这与工程需要输出的结果尚有差异,所以需要用右车道线上的点做一次最小二乘直线拟合,才能得到最终的左、右车道线的直线方程。

考虑到实际工程中左右车道线一般是平行的,所以可以认为左右车道线上最上和最下的点对应的y值,就是左右车道线的边界。基于以上思路,我们重新定义draw_lines()函数,将数据后处理过程写入该函数中。

rho=1.0
theta=np.pi/180
threshold=15
#lines=None
minLineLength=40
maxLineGap=20
lines = cv2.HoughLinesP(roi_image, rho, theta, threshold, np.array([]), minLineLength, maxLineGap)

#封装成一个绘画函数,实现把线段绘制在图像上的功能,以实现线段的可视化
'''
def draw_lines(img,lines,color=255,thickness=2):
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(img,(x1,y1),(x2,y2),color,thickness) #将线段绘制在img上
            
'''
def draw_lines(img,lines,color=255,thickness=2):
    left_lines_x=[]
    left_lines_y=[]
    right_lines_x=[]
    right_lines_y=[]
    line_y_min=170
    line_y_max=296
    for line in lines:
        for x1,y1,x2,y2 in line:
            if y1>line_y_min:
                line_y_min=y1
            if y2>line_y_min:
                line_y_min=y2
            if y1<line_y_max:
                line_y_max=y1
            if y2<line_y_max:
                line_y_max=y2
            k=(y2-y1)/(x2-x1)   #k为拟合直线的斜率
            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)
    #最小二乘法拟合
    '''
    np.polyfit函数:采用的是最小二次拟合,numpy.polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False),
    前三个参数是必须的,deg=1,代表需要拟合成一条一阶多项式。
    '''
    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.
    '''
    img=cv.line(img, pt1, pt2, color[, thickness[, lineType[, shift]]])
    第一个参数 img:要划的线所在的图像;
    第二个参数 pt1:直线起点
    第三个参数 pt2:直线终点
    第四个参数 color:直线的颜色            
    第五个参数 thickness=1:线条粗细
    '''
    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)

line_image=np.copy(img)  #复制一张原图,将线段绘制在这张图上
draw_lines(line_image,lines)

cv2.imshow('line_image',line_image)
cv2.imwrite('D:\pycharm\exercise\spring festival holiday\lane_line4.jpg',line_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

四、将算法应用于视频

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

output = 'test_video_output.mp4'

from moviepy.editor import VideoFileClip

clip1 = VideoFileClip("test_video.mp4")
white_clip = clip1.fl_image(process_image) 
%time white_clip.write_videofile(output , audio=False)

五、总结

1、以上方法不能有效处理带有弧度的曲线,这需要使用radon变换,并进行多项式拟合,这个咱们以后再讲’;

2、实际应用场景肯定会更加复杂,所以需要根据具体应用场景进行优化‘;

3、本算法无法应用在不同光照条件的场景中,鲁棒性较差;

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值