初级版无人驾驶车道线检测(下)(含代码)

  先推荐下自己的公众号——Joe学习笔记,公众号上会不定期更新一些文章,主要是自己平时学到的知识,内容包括自动驾驶、计算机视觉、人工智能和机器人技术。我会第一时间把文章更新在公众号上,欢迎大家订阅和分享!
在这里插入图片描述
  在初级版无人驾驶车道线检测(上)中已经完成了灰度图片转换、高斯滤波、边缘检测、感兴趣区域提取和直线检测,这部分主要介绍直线拟合。本文参考了github上的一个开源代码,觉得写的挺好的,看了之后挺有收获。上次录视频很紧张,再加上这次的代码不是很熟,就不录视频了。
在这里插入图片描述
  上一篇文章最后的检测的结果如上图所示。我们需要拟合左右两个车道线,所以第一步是根据斜率的正负把图中的线段分为左右两部分。代码如下:

# 1.根据斜率分左右线段
for line in lines:
    for x1, y1, x2, y2 in line:
        if y2 - y1 != 0 and x2 - x1 != 0:
            #计算斜率
            slope = (y2 - y1) / (x2 - x1)
            if slope > 0:
                right_lines.append(line)
            else:
                left_lines.append(line)

  此时左右线段可能包含少量的背景线段,需要删去这些错误的线段。由于错误的线段较少,线段斜率的平均值接近车道线的斜率。因此计算线段斜率的平均值,并求得每条线段斜率和平均值的差,设置一个阈值,将差值大于阈值的线段删去。由于需要对左右线段各进行一次操作,为了方便,写成函数的形式,代码如下:

#去除斜率差异较大的线段
def clean_lines(lines, threshold):
    #计算斜率
    slope = [(y2 - y1) / (x2 - x1) for line in lines for x1, x2, y1, y2 in line]
    while(len(slope) > 0):
        #计算斜率平均值
        mean = np.mean(slope)
        #计算差值
        diff = [abs(s - mean) for s in slope]
        #找出差值最大的下标
        index = np.argmax(diff)
        # 如果下标对应的差值大于threshold就删去该线段
        if diff[index] > threshold:
            slope.pop(index)
            lines.pop(index)
        else:
            break              

  以上代码在求斜率时,列表中写入循环比单独写出循环求斜率简洁很多,这种方法在之后的代码中也有出现。
  去除了错误的线段之后,就可以进行直线拟合。opencv中霍夫变换后保存的是线段的两个端点,首先将所有端点表示成元组(x,y)的列表,代码如下:

left_points = [(x1, y1) for line in left_lines for x1, y1, x2, y2 in line]
left_points = left_points + [(x2, y2) for line in left_lines for x1, y1, x2, y2 in line]
right_points = [(x1, y1) for line in right_lines for x1, y1, x2, y2 in line]
right_points = right_points + [(x2, y2) for line in right_lines for x1, y1, x2, y2 in line]   

  有了端点,接下来可以进行直线拟合,拟合直线后选取要画出的车道线的起点和终点的y坐标,求出车道线的4个端点。这需要进行2次操作因此也写成函数形式,代码如下:

#拟合直线,计算车道线端点
def cal_lane_vertices(point_list, y_start, y_end):
    x = [p[0] for p in point_list]
    y = [p[1] for p in point_list]
    # 一次函数拟合,fit为(k, b)
    fit = np.polyfit(y, x, 1)
    #得到x = ky + b
    fit_fn = np.poly1d(fit)
    x_start = int(fit_fn(y_start))
    x_end = int(fit_fn(y_end))
    return [(x_start, y_start), (x_end, y_end)]   

  求得了车道线端点,最后使用多边形来填充即可,最后的效果如下所示。
在这里插入图片描述
  总结一下直线拟合的步骤,如下图所示。
在这里插入图片描述
   视频效果——传送门
  在视频中,大部分的车道线拟合的比较好,但由于使用的是直线拟合,所以在车道弯曲的地方拟合的不是很好。最大的问题是视频的最后,出现了拟合失败的情况,出现这种问题的原因是地面的光照条件发生了变化,转换为灰度图像后车道线基本不可见。这是以后可以改进的方面。
  应用于视频的整体代码如下所示:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from moviepy.editor import VideoFileClip

def process_img(img):
    #转换为灰度图像
    img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    #cv2.imshow("img_gray", img_gray)

    #高斯滤波
    img_gaussian = cv2.GaussianBlur(img_gray, (11, 11), 0.0)
    #cv2.imshow("img_gaussian", img_gaussian)

    #Canny算法,边缘检测
    img_edge = cv2.Canny(img_gaussian, 90, 180)
    #cv2.imshow("img_edge", img_edge)

    mask = np.zeros_like(img_edge)
    vertices = np.array([[220, 670], [610, 410], [650, 410], [1100, 670]])
    cv2.fillPoly(mask, [vertices], 255)
    img_edge_roi = cv2.bitwise_and(img_edge, mask)

    #直线检测
    lines = cv2.HoughLinesP(img_edge_roi, 1, np.pi/180, 20, minLineLength = 10, maxLineGap = 10)
    #print(lines.shape)

    img_lane = draw_lanes(img, lines, color=[0, 0, 255])
    return img_lane

#去除斜率差异较大的线段
def clean_lines(lines, threshold):
    #计算斜率
    slope = [(y2 - y1) / (x2 - x1) for line in lines for x1, x2, y1, y2 in line]
    while(len(slope) > 0):
        #计算斜率平均值
        mean = np.mean(slope)
        #计算差值
        diff = [abs(s - mean) for s in slope]
        #找出差值最大的下标
        index = np.argmax(diff)
        # 如果下标对应的差值大于threshold就删去该线段
        if diff[index] > threshold:
            slope.pop(index)
            lines.pop(index)
        else:
            break

#拟合直线,计算车道线端点
def cal_lane_vertices(point_list, y_start, y_end):
    x = [p[0] for p in point_list]
    y = [p[1] for p in point_list]
    # 一次函数拟合,fit为(k, b)
    fit = np.polyfit(y, x, 1)
    #得到x = ky + b
    fit_fn = np.poly1d(fit)
    x_start = int(fit_fn(y_start))
    x_end = int(fit_fn(y_end))
    return [(x_start, y_start), (x_end, y_end)]

#画车道线
def draw_lanes(img, lines, color = [0, 0, 255]):
    #左线段列表
    right_lines = []
    #右线段列表
    left_lines = []
    # 1.根据斜率分左右线段
    for line in lines:
        for x1, y1, x2, y2 in line:
            if y2 - y1 != 0 and x2 - x1 != 0:
                #计算斜率
                slope = (y2 - y1) / (x2 - x1)
                if slope > 0:
                    right_lines.append(line)
                else:
                    left_lines.append(line)
    if (len(left_lines) <= 0 or len(right_lines) <= 0):
        return img

    #2.去除斜率差异过大的线段
    clean_lines(right_lines, 0.1)
    clean_lines(left_lines, 0.1)

    #3.直线拟合
    left_points = [(x1, y1) for line in left_lines for x1, y1, x2, y2 in line]
    left_points = left_points + [(x2, y2) for line in left_lines for x1, y1, x2, y2 in line]
    right_points = [(x1, y1) for line in right_lines for x1, y1, x2, y2 in line]
    right_points = right_points + [(x2, y2) for line in right_lines for x1, y1, x2, y2 in line]

    #计算车道线端点
    left_vertices = cal_lane_vertices(left_points, img.shape[0] - 50, 450)
    right_vertices = cal_lane_vertices(right_points, img.shape[0] - 50, 450)

    #以矩形的形式拟合
    vertices = np.array([left_vertices[0], left_vertices[1], right_vertices[1], right_vertices[0]])
    cv2.fillPoly(img, [vertices], color)
    return img

output = 'project_video_lane_detect.mp4'
clip = VideoFileClip("project_video.mp4")
out_clip = clip.fl_image(process_img)
out_clip.write_videofile(output, audio=False)

  最后把代码、测试视频和测试图片放在百度网盘。
链接:https://pan.baidu.com/s/1PtyZ9NChCpsjbS7NbnXOKg
提取码:uh9k

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值