基于OpenCV实现车道线检测(自动驾驶 机器视觉)

目录
  • 0 前言
  • 1 车道线检测
  • 2 目标
  • 3 检测思路
  • 4 代码实现
    • 4.1 视频图像加载
    • 4.2 车道线区域
    • 4.3 区域
    • 4.4 canny 边缘检测
    • 4.5 霍夫变换(Hough transform)
    • 4.6 HoughLinesP 检测原理

0 前言

无人驾驶技术是机器学习为主的一门前沿领域,在无人驾驶领域中机器学习的各种算法随处可见,今天学长给大家介绍无人驾驶技术中的车道线检测。

1 车道线检测

在无人驾驶领域每一个任务都是相当复杂,看上去无从下手。那么面对这样极其复杂问题,我们解决问题方式从先尝试简化问题,然后由简入难一步一步尝试来一个一个地解决问题。车道线检测在无人驾驶中应该算是比较简单的任务,依赖计算机视觉一些相关技术,通过读取 camera 传入的图像数据进行分析,识别出车道线位置,我想这个对于 lidar 可能是无能为力。所以今天我们就从最简单任务说起,看看有哪些技术可以帮助我们检出车道线。

我们先把问题简化,所谓简化问题就是用一些条件限制来缩小车道线检测的问题。我们先看数据,也就是输入算法是车辆行驶的图像,输出车道线位置。

更多时候我们如何处理一件比较困难任务,可能有时候我们拿到任务时还没有任何思路,不要着急也不用想太多,我们先开始一步一步地做,从最简单的开始做起,随着做就会有思路,同样一些问题也会暴露出来。我们先找一段视频,这段视频是我从网上一个关于车道线检测项目中拿到的,也参考他的思路来做这件事。好现在就开始做这件事,那么最简单的事就是先读取视频,然后将其显示在屏幕以便于调试。

2 目标

检测图像中车道线位置,将车道线信息提供路径规划。

3 检测思路

  • 图像灰度处理
  • 图像高斯平滑处理
  • canny 边缘检测
  • 区域 Mask
  • 霍夫变换
  • 绘制车道线

4 代码实现

4.1 视频图像加载

import cv2
import numpy as np
import sys

import pygame
from pygame.locals import *

class Display(object):

    def __init__(self,Width,Height):
        pygame.init()
        pygame.display.set_caption('Drive Video')
        self.screen = pygame.display.set_mode((Width,Height),0,32)
    def paint(self,draw):
        self.screen.fill([0,0,0])

        draw = cv2.transpose(draw)
        draw = pygame.surfarray.make_surface(draw)
        self.screen.blit(draw,(0,0))
        pygame.display.update()

if __name__ == "__main__":
    solid_white_right_video_path = "test_videos/丹成学长车道线检测.mp4"
    cap = cv2.VideoCapture(solid_white_right_video_path)
    Width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    Height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    display = Display(Width,Height)

    while True:
        ret, draw = cap.read()
        draw = cv2.cvtColor(draw,cv2.COLOR_BGR2RGB)
        if ret == False:
            break
        display.paint(draw)
        for event in pygame.event.get():
                if event.type == QUIT:
                    sys.exit()

上面代码学长就不多说了,默认大家对 python 是有所了解,关于如何使用 opencv 读取图片网上代码示例也很多,大家一看就懂。这里因为我用的是 mac 有时候显示视频图像可能会有些问题,所以我们用 pygame 来显示 opencv 读取图像。这个大家根据自己实际情况而定吧。值得说一句的是 opencv 读取图像是 BGR 格式,要想在 pygame 中正确显示图像就需要将 BGR 转换为 RGB 格式。

4.2 车道线区域

现在这个区域是我们根据观测图像绘制出来,

def color_select(img,red_threshold=200,green_threshold=200,blue_threshold=200):
    ysize,xsize = img.shape[:2]

    color_select = np.copy(img)

    rgb_threshold = [red_threshold, green_threshold, blue_threshold]

    thresholds = (img[:,:,0] < rgb_threshold[0]) \
            | (img[:,:,1] < rgb_threshold[1]) \
            | (img[:,:,2] < rgb_threshold[2])
    color_select[thresholds] = [0,0,0]

    return color_select

效果如下:

4.3 区域

我们要检测车道线位置相对比较固定,通常出现车的前方,所以我们通过绘制,也就是仅检测我们关心区域。通过创建 mask 来过滤掉那些不关心的区域保留关心区域。

4.4 canny 边缘检测

有关边缘检测也是计算机视觉。首先利用梯度变化来检测图像中的边,如何识别图像的梯度变化呢,答案是卷积核。卷积核是就是不连续的像素上找到梯度变化较大位置。我们知道 sobal 核可以很好检测边缘,那么 canny 就是 sobal 核检测上进行优化。

# 示例代码,作者丹成学长:Q746876041
def canny_edge_detect(img):
    gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
    kernel_size = 5
    blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

    low_threshold = 180
    high_threshold = 240
    edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

    return edges

4.5 霍夫变换(Hough transform)

霍夫变换是将 x 和 y 坐标系中的线映射表示在霍夫空间的点(m,b)。所以霍夫变换实际上一种由繁到简(类似降维)的操作。当使用 canny 进行边缘检测后图像可以交给霍夫变换进行简单图形(线、圆)等的识别。这里用霍夫变换在 canny 边缘检测结果中寻找直线。

# 示例代码,作者丹成学长:Q746876041
mask = np.zeros_like(edges)

    ignore_mask_color = 255
    # 获取图片尺寸
    imshape = img.shape
    # 定义 mask 顶点
    vertices = np.array([[(0,imshape[0]),(450, 290), (490, 290), (imshape[1],imshape[0])]], dtype=np.int32)
    # 使用 fillpoly 来绘制 mask
    cv2.fillPoly(mask, vertices, ignore_mask_color)
    masked_edges = cv2.bitwise_and(edges, mask)
    # 定义Hough 变换的参数
    rho = 1
    theta = np.pi/180
    threshold = 2

    min_line_length = 4 # 组成一条线的最小像素数
    max_line_gap = 5    # 可连接线段之间的最大像素间距
    # 创建一个用于绘制车道线的图片
    line_image = np.copy(img)*0 

    # 对于 canny 边缘检测结果应用 Hough 变换
    # 输出“线”是一个数组,其中包含检测到的线段的端点
    lines = cv2.HoughLinesP(masked_edges, rho, theta, threshold, np.array([]),
                                min_line_length, max_line_gap)

    # 遍历“线”的数组来在 line_image 上绘制
    for line in lines:
        for x1,y1,x2,y2 in line:
            cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)

    color_edges = np.dstack((edges, edges, edges)) 

import math
import cv2
import numpy as np

"""
Gray Scale
Gaussian Smoothing
Canny Edge Detection
Region Masking
Hough Transform
Draw Lines [Mark Lane Lines with different Color]
"""

class SimpleLaneLineDetector(object):
    def __init__(self):
        pass

    def detect(self,img):
        # 图像灰度处理
        gray_img = self.grayscale(img)
        print(gray_img)
        #图像高斯平滑处理
        smoothed_img = self.gaussian_blur(img = gray_img, kernel_size = 5)
        #canny 边缘检测
        canny_img = self.canny(img = smoothed_img, low_threshold = 180, high_threshold = 240)
        #区域 Mask
        masked_img = self.region_of_interest(img = canny_img, vertices = self.get_vertices(img))
        #霍夫变换
        houghed_lines = self.hough_lines(img = masked_img, rho = 1, theta = np.pi/180, threshold = 20, min_line_len = 20, max_line_gap = 180)
        # 绘制车道线
        output = self.weighted_img(img = houghed_lines, initial_img = img, alpha=0.8, beta=1., gamma=0.)

        return output
    def grayscale(self,img):
        return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    def canny(self,img, low_threshold, high_threshold):
        return cv2.Canny(img, low_threshold, high_threshold)

    def gaussian_blur(self,img, kernel_size):
        return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)

    def region_of_interest(self,img, vertices):
        mask = np.zeros_like(img)   

        if len(img.shape) > 2:
            channel_count = img.shape[2]
            ignore_mask_color = (255,) * channel_count
        else:
            ignore_mask_color = 255

        cv2.fillPoly(mask, vertices, ignore_mask_color)

        masked_image = cv2.bitwise_and(img, mask)
        return masked_image
    def draw_lines(self,img, lines, color=[255, 0, 0], thickness=10):
        for line in lines:
            for x1,y1,x2,y2 in line:
                cv2.line(img, (x1, y1), (x2, y2), color, thickness)

    def slope_lines(self,image,lines):
        img = image.copy()
        poly_vertices = []
        order = [0,1,3,2]

        left_lines = []
        right_lines = []
        for line in lines:
            for x1,y1,x2,y2 in line:

                if x1 == x2:
                    pass
                else:
                    m = (y2 - y1) / (x2 - x1)
                    c = y1 - m * x1

                    if m < 0:
                        left_lines.append((m,c))
                    elif m >= 0:
                        right_lines.append((m,c))

        left_line = np.mean(left_lines, axis=0)
        right_line = np.mean(right_lines, axis=0)

        for slope, intercept in [left_line, right_line]:

            rows, cols = image.shape[:2]
            y1= int(rows) 

            y2= int(rows*0.6)

            x1=int((y1-intercept)/slope)
            x2=int((y2-intercept)/slope)
            poly_vertices.append((x1, y1))
            poly_vertices.append((x2, y2))
            self.draw_lines(img, np.array([[[x1,y1,x2,y2]]]))

        poly_vertices = [poly_vertices[i] for i in order]
        cv2.fillPoly(img, pts = np.array([poly_vertices],'int32'), color = (0,255,0))
        return cv2.addWeighted(image,0.7,img,0.4,0.)

    def hough_lines(self,img, rho, theta, threshold, min_line_len, max_line_gap):
        lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)
        line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)
        line_img = self.slope_lines(line_img,lines)
        return line_img

    def weighted_img(self,img, initial_img, alpha=0.1, beta=1., gamma=0.):

        lines_edges = cv2.addWeighted(initial_img, alpha, img, beta, gamma)
        return lines_edges

    def get_vertices(self,image):
        rows, cols = image.shape[:2]
        bottom_left  = [cols*0.15, rows]
        top_left     = [cols*0.45, rows*0.6]
        bottom_right = [cols*0.95, rows]
        top_right    = [cols*0.55, rows*0.6] 

        ver = np.array([[bottom_left, top_left, top_right, bottom_right]], dtype=np.int32)
        return ver

4.6 HoughLinesP 检测原理

接下来进入代码环节,学长详细给大家解释一下 HoughLinesP 参数的含义以及如何使用。

lines = cv2.HoughLinesP(cropped_image,2,np.pi/180,100,np.array([]),minLineLength=40,maxLineGap=5)

第一参数是我们要检查的图片 Hough accumulator 数组

第二个和第三个参数用于定义我们 Hough 坐标如何划分 bin,也就是小格的精度。我们通过曲线穿过 bin 格子来进行投票,我们根据投票数量来决定 p 和 theta 的值。2 表示我们小格宽度以像素为单位 。

我们可以通过下图划分小格,只要曲线穿过就会对小格进行投票,我们记录投票数量,记录最多的作为参数

  • 如果定义尺寸过大也就失去精度,如果定义格子尺寸过小虽然精度上来了,这样也会打来增长计算时间。
  • 接下来参数 100 表示我们投票为 100 以上的线才是符合要求是我们要找的线。也就是在 bin 小格子需要有 100 以上线相交于此才是我们要找的参数。
  • minLineLength 给 40 表示我们检查线长度不能小于 40 pixel
  • maxLineGap=5 作为线间断不能大于 5 pixel

4.6.1 定义显示车道线方法

def disply_lines(image,lines):
    pass

通过定义函数将找到的车道线显示出来。

line_image = disply_lines(lane_image,lines)

4.6.2 查看探测车道线数据结构

def disply_lines(image,lines):
    line_image = np.zeros_like(image)
    if lines is not None:
        for line in lines:
            print(line)

先定义一个尺寸大小和原图一样的矩阵用于绘制查找到车道线,我们先判断一下是否已经找到车道线,lines 返回值应该不为 None 是一个矩阵,我们可以简单地打印一下看一下效果

[[704 418 927 641]]
[[704 426 791 516]]
[[320 703 445 494]]
[[585 301 663 381]]
[[630 341 670 383]]

4.6.3 探测车道线

看数据结构[[x1,y1,x2,y2]] 的二维数组,这就需要我们转换一下为一维数据[x1,y1,x2,y2]

def disply_lines(image,lines):
    line_image = np.zeros_like(image)
    if liness is not None:
        for line in lines:
            x1,y1,x2,y2 = line.reshape(4)
            cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)
    return line_image

line_image = disply_lines(lane_image,lines)
cv2.imshow('result',line_image)

4.6.4 合成

有关合成图片我们是将两张图片通过给一定权重进行叠加合成。

4.6.5 优化

探测到的车道线还是不够平滑,我们需要优化,基本思路就是对这些直线的斜率和截距取平均值然后将所有探测出点绘制到一条直线上。

def average_slope_intercept(image,lines):
    left_fit = []
    right_fit = []
    for line in lines:
        x1, y1, x2, y2 = line.reshape(4)
        parameters = np.polyfit((x1,x2),(y1,y2),1)
        print(parameters)

这里学长定义两个数组 left_fit 和 right_fit 分别用于存放左右两侧车道线的点,我们打印一下 lines 的斜率和截距,通过 numpy 提供 polyfit 方法输入两个点我们就可以得到通过这些点的直线的斜率和截距。

[   1. -286.]
[   1.03448276 -302.27586207]
[  -1.672 1238.04 ]
[   1.02564103 -299.
[   1.02564103 -299.

def average_slope_intercept(image,lines):
    left_fit = []
    right_fit = []
    for line in lines:
        x1, y1, x2, y2 = line.reshape(4)
        parameters = np.polyfit((x1,x2),(y1,y2),1)
        # print(parameters)
        slope = parameters[0]
        intercept = parameters[1]
        if slope < 0:
            left_fit.append((slope,intercept))
        else:
            right_fit.append((slope,intercept))
        print(left_fit)
        print(right_fit)

我们输出一下图片大小,我们图片是以其左上角作为原点 0 ,0 来开始计算的,所以我们直线从图片底部 700 多向上绘制我们无需绘制全部可以截距一部分即可。

def make_coordinates(image, line_parameters):
    slope, intercept = line_parameters
    y1 = image.shape[0]
    y2 = int(y1*(3/5))
    x1 = int((y1 - intercept)/slope)
    x2 = int((y2 - intercept)/slope)
    # print(image.shape)
    return np.array([x1,y1,x2,y2])

所以直线开始和终止我们给定 y1,y2 然后通过方程的斜率和截距根据y 算出 x。

averaged_lines = average_slope_intercept(lane_image,lines);
line_image = disply_lines(lane_image,averaged_lines)
combo_image = cv2.addWeighted(lane_image,0.8, line_image, 1, 1,1)

cv2.imshow('result',combo_image)

到此这篇关于基于OpenCV实现车道线检测(自动驾驶 机器视觉)的文章就介绍到这了,更多相关OpenCV 车道线检测内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-11-25

基于opencv实现车道线检测

基于opencv的车道线检测,供大家参考,具体内容如下 原理: 算法基本思想说明: 传统的车道线检测,多数是基于霍夫直线检测,其实这个里面有个很大的误区,霍夫直线拟合容易受到各种噪声干扰,直接运用有时候效果不好,更多的时候通过霍夫直线检测进行初步的筛选,然后再有针对性的进行直线拟合,根据拟合的直线四个点坐标,绘制出车道线,这种方式可以有效避免霍夫直线拟合不良后果,是一种更加稳定的车道线检测方法,在实际项目中,可以选择两种方法并行,在计算出结果后进行叠加或者对比提取,今天分享的案例主要是绕开了霍夫

opencv车道线检测的实现方法

车道线检测,需要完成以下功能: 图像裁剪:通过设定图像ROI区域,拷贝图像获得裁剪图像 反透视变换:用的是室外采集到的视频,没有对应的变换矩阵.所以建立二维坐标,通过四点映射的方法计算矩阵,进行反透视变化.后因ROI区域的设置易造成变换矩阵获取困难和插值得到的透视图效果不理想,故没应用 二值化:先变化为灰度图,然后设定阈值直接变成二值化图像. 形态学滤波:对二值化图像进行腐蚀,去除噪点,然后对图像进行膨胀,弥补对车道线的腐蚀. 边缘检测:canny变化.sobel变化和laplacian变化中选

python+opencv实现车道线检测

python+opencv车道线检测(简易实现),供大家参考,具体内容如下 技术栈:python+opencv 实现思路: 1.canny边缘检测获取图中的边缘信息: 2.霍夫变换寻找图中直线: 3.绘制梯形感兴趣区域获得车前范围: 4.得到并绘制车道线: 效果展示: 代码实现: import cv2 import numpy as np def canny(): gray = cv2.cvtColor(lane_image, cv2.COLOR_RGB2GRAY) #高斯滤波 blur = c

python opencv实现图片缺陷检测(讲解直方图以及相关系数对比法)

一.利用直方图的方式进行批量的图片缺陷检测(方法简单) 二.步骤(完整代码见最后) 2.1灰度转换(将原图和要检测对比的图分开灰度化) 灰度化的作用是因为后面的直方图比较需要以像素256为基准进行相关性比较 img = cv2.imread("0.bmp") #原图灰度转换 gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) #循环要检测的图,均灰度化 for i in range(1, 6): t1=cv2.cvtColor(cv2.imread

Python Opencv实现单目标检测的示例代码

一 简介 目标检测即为在图像中找到自己感兴趣的部分,将其分割出来进行下一步操作,可避免背景的干扰.以下介绍几种基于opencv的单目标检测算法,算法总体思想先尽量将目标区域的像素值全置为1,背景区域全置为0,然后通过其它方法找到目标的外接矩形并分割,在此选择一张前景和背景相差较大的图片作为示例. 环境:python3.7 opencv4.4.0 二 背景前景分离 1 灰度+二值+形态学 轮廓特征和联通组件 根据图像前景和背景的差异进行二值化,例如有明显颜色差异的转换到HSV色彩空间进行分割. 1

基于python OpenCV实现动态人脸检测

本文实例为大家分享了python动态人脸检测的具体代码,供大家参考,具体内容如下 直接上代码: 按Q退出 import cv2 import numpy as np cv2.namedWindow("test") cap = cv2.VideoCapture(0) #加载摄像头录制 # cap = cv2.VideoCapture("test.mp4") #打开视频文件 success, frame = cap.read() # classifier = cv2.C

Python Opencv任意形状目标检测并绘制框图

opencv 进行任意形状目标识别,供大家参考,具体内容如下 工作中有一次需要在简单的图上进行目标识别,目标的形状不固定,并且存在一定程度上的噪声影响,但是噪声影响不确定.这是一个简单的事情,因为图像并不复杂,现在将代码公布如下: import cv2 def otsu_seg(img): ret_th, bin_img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU) return ret_th, bin_img d

python opencv 检测移动物体并截图保存实例

最近在老家找工作,无奈老家工作真心太少,也没什么面试机会,不过之前面试一家公司,提了一个有意思的需求,检测河面没有有什么船只之类的物体,我当时第一反应是用opencv做识别,不过回家想想,河面相对的东西比较少,画面比较单一,只需要检测有没有移动的物体不就简单很多嘛,如果做街道垃圾检测的话可能就很复杂了,毕竟街道上行人,车辆,动物,很多干扰物,于是就花了一个小时写了一个小的demo,只需在程序同级目录创建一个img目录就可以了 # -*-coding:utf-8 -*- __author__ =

使用OpenCV对车道进行实时检测的实现示例代码

项目介绍 下图中的两条线即为车道: 我们的任务就是通过 OpenCV 在一段视频(或摄像头)中实时检测出车道并将其标记出来.其效果如下图所示: 这里使用的代码来源于磐怼怼大神,此文章旨在对其代码进行解释. 实现步骤 1.将视频的所有帧读取为图片: 2.创建掩码并应用到这些图片上: 3.图像阈值化: 4.用霍夫线变换检测车道: 5.将车道画到每张图片上: 6.将所有图片合并为视频. 代码实现 1.导入需要的库 import os import re import cv2 import numpy

python+opencv轮廓检测代码解析

首先大家可以对OpenCV有个初步的了解,可以参考:简单了解OpenCV 轮廓(Contours),指的是有相同颜色或者密度,连接所有连续点的一条曲线.检测轮廓的工作对形状分析和物体检测与识别都非常有用. 在轮廓检测之前,首先要对图片进行二值化或者Canny边缘检测.在OpenCV中,寻找的物体是白色的,而背景必须是黑色的,因此图片预处理时必须保证这一点. import cv2 #读入图片 img = cv2.imread("1.png") # 必须先转化成灰度图 gray = cv2

python+opencv实现霍夫变换检测直线

本文实例为大家分享了python+opencv实现霍夫变换检测直线的具体代码,供大家参考,具体内容如下 python+opencv实现高斯平滑滤波 python+opencv实现阈值分割 功能: 创建一个滑动条来控制检测直线的长度阈值,即大于该阈值的检测出来,小于该阈值的忽略 注意:这里用的函数是HoughLinesP而不是HoughLines,因为HoughLinesP直接给出了直线的断点,在画出线段的时候可以偷懒 代码: # -*- coding: utf-8 -*- import cv2

Python OpenCV利用笔记本摄像头实现人脸检测

本文实例为大家分享了Python OpenCV利用笔记本摄像头实现人脸检测的具体代码,供大家参考,具体内容如下 1.安装opencv 首先参考其他文章安装pip. 之后以管理员身份运行命令提示符,输入以下代码安装opencv pip install --user opencv-python 可以使用以下代码测试安装是否成功 #导入opencv模块 import cv2 #捕捉帧,笔记本摄像头设置为0即可 capture = cv2.VideoCapture(0) #循环显示帧 while(Tru