MATLAB基于传统方法的车道线检测实现

MATLAB基于传统方法的车道线检测实现

本文实现的是基于传统方法的车道线检测,所谓传统方法就是没有涉及到深度学习算法,基于直观的手段和数学知识来实现,后期会实现基于深度学习的车道线检测方法。

实现步骤:

  1. Canny边缘检测
  2. 手动分割路面区域
  3. 霍夫变换得到车道线
  4. 获取车道线并叠加到原始图像中

算法演示视频如下:

Canny边缘检测

Canny边缘检测就是检测出视频中出现的所有的线,如图1所示:

图1

基本原理:检测亮度的急剧变化(常见的就是大梯度,如从白色到黑色),在给定阈值下定义为边。

Canny检测的步骤

(1)对原始图像进行灰度化

Canny算法通常处理的图像为灰度图,因此如果摄像头获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。以RGB格式的彩图为例,通常灰度化采用的方法主要有:

    1. Gray=(R+G+B)/3;
    2. Gray=0.299R+0.587G+0.114B;(这种参数考虑到了人眼的生理特点)

(2)高斯滤波

滤波的主要目的是降噪,一般的图像处理算法都需要先进行降噪。而高斯滤波主要使图像变得平滑(模糊),同时也有可能增大了边缘的宽度。本文实现的代码采用 5*5 的高斯滤波器(正太分布核)对图像做卷积(平滑图像)。

高斯函数是一个类似与正态分布的中间大两边小的函数。

对于一个位置(m,n)的像素点,其灰度值(这里只考虑二值图)为f(m,n)。

那么经过高斯滤波后的灰度值将变为:

gσ(m,n)=12πσ2e−m2+n22σ2⋅f(m,n)

简单说就是用一个高斯矩阵乘以每一个像素点及其邻域,取其带权重的平均值作为最后的灰度值。

(3)用一阶偏导的有限差分来计算梯度的幅值和方向

边缘是什么?边缘就是灰度值变化较大的的像素点的集合。一道黑边一道白边中间就是边缘,它的灰度值变化是最大的,在图像中,用梯度来表示灰度值的变化程度和方向。

它可以通过点乘一个Sobel、Roberts、Prewitt等算子沿x轴和y轴检测边缘是水平的、垂直的或者是对角线,得到不同方向的梯度值gx(m,n),gy(m,n)

综合梯度通过以下公式计算梯度值和梯度方向:

G(m,n)=gx(m,n)2+gy(m,n)2

θ=arctangy(m,n)gx(m,n)

(5)非极大值抑制

图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但不能说明该点就是边缘(这仅仅是属于图像增强的过程)。在Canny算法中,非极大值抑制是进行边缘检测的重要步骤,简单说就是寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉许多非边缘的点,因为如果一个像素点属于边缘,那么这个像素点在梯度方向上的梯度值是最大的,否则不是边缘,将灰度值设为0

(6)使用上下阀值来检测边缘

非极大值抑制后可以确认强像素在最终边缘映射中。但还要对弱像素进行进一步分析确定它是边缘还是噪声。

通过设置两个阀值(threshold),如图2所示,分别为maxVal和minVal。

通过如下方式来确定是否为边缘:

  1. 大于maxVal的都被检测为边缘
  2. 小于minVal的都被检测为非边缘
  3. 对于大于minVal而小于maxVal的像素点,如果与确定为边缘的像素点邻接,则判定为边缘;否则为非边缘。

Canny边缘检测实现代码

def do_canny(frame):

    # Converts frame to grayscale because we only need the luminance channel for detecting edges - less computationally expensive

    gray = cv.cvtColor(frame, cv.COLOR_RGB2GRAY)

    # Applies a 5x5 gaussian blur with deviation of 0 to frame - not mandatory since Canny will do this for us

    blur = cv.GaussianBlur(gray, (5, 5), 0)

    # Applies Canny edge detector with minVal of 50 and maxVal of 150

    canny = cv.Canny(blur, 50, 150)

    return canny

二、手动分割路面区域

图3

Canny边缘检测出了所有的边缘(图3左),很显然,我们需要的是表示道路的线(图3右),因此需要排除其他的线条。

我们采用手动指定一个三角形来分割出路面区域,去除其它干扰边缘。

图4

具体的说就是以原始的图片建立直角坐标系,指定三角形的三个顶点,保留三角形区域中的边缘线条,去除其他多余的线条,如图4所示,实现代码如下:

def calculate_coordinates(frame, parameters):

    slope, intercept = parameters

    # Sets initial y-coordinate as height from top down (bottom of the frame)

    y1 = frame.shape[0]

    # Sets final y-coordinate as 150 above the bottom of the frame

    y2 = int(y1 - 150)

    # Sets initial x-coordinate as (y1 - b) / m since y1 = mx1 + b

    x1 = int((y1 - intercept) / slope)

    # Sets final x-coordinate as (y2 - b) / m since y2 = mx2 + b

    x2 = int((y2 - intercept) / slope)

    return np.array([x1, y1, x2, y2])

实现代码

def do_segment(frame):

    height = frame.shape[0]

    # Creates a triangular polygon for the mask defined by three (x, y) coordinates

    polygons = np.array([

                            [(0, height), (800, height), (380, 290)]

                        ])

    #(0, height)对应图4中的点A,(800, height)对应图4中的点B,(380, 290)对应图4中的点C,由于是认为指定,所以存在误差。

    mask = np.zeros_like(frame)

    # Allows the mask to be filled with values of 1 and the other areas to be filled with values of 0

    cv.fillPoly(mask, polygons, 255)

    # A bitwise and operation between the mask and frame keeps only the triangular area of the frame

    segment = cv.bitwise_and(frame, mask)

    return segment

三、霍夫变换得到车道线

想象一下,人类在一副图片中找出一条直线或者圆,相信对于大家应该都是一件相当容易的事,但是对于计算机来说,一副图像所呈现的只是灰度值从0-255的庞大矩阵而已,它可不容易知道复制的矩阵中哪些是直线哪些不是,霍夫变换便是帮助计算机'看到'图像中的直线或圆的一种算法

1.基本思想

将传统的图像从x 、y轴坐标系变换到参数空间(m, b)或者霍夫空间(Hough space)中,通过在参数空间(parameter space)或可称为累加空间(accumulator space)中计算局部最大值从而确定原始图像直线或圆所在位置。

2.常见的霍夫变换

  • 基于笛卡尔坐标空间的霍夫变换
  • 基于极坐标空间的霍夫变换

(1)基于笛卡尔坐标空间的霍夫变换

平面直角坐标中,一条直线的表示通常用y=m0x+b0表示,其中m0表示的是直线的斜率b0表示的是直线的截距,一条直线上的点所使用的是同一个m0b0,因此我们可以设想一下,如果有一个坐标轴体系是以m0为横轴,b0为竖轴,形成以(m0,b0)为参数的参数空间,是不是在平面坐标中同一条直线上的点在参数空间表示为一个点呢,霍夫变换即是基于这种思想而诞生的,如图5所示。

图5

图6

从数学上来解释两者的转换,如图6所示。

让我们换一种思维来更深层次理解一下霍夫变换,在xy笛卡尔坐标轴上的任意一点(x,y)随着x斜率m0和截距b0的改变,在参数空间各种m0b0的组合将会呈现为一条直线,如图 7所示,为点A(1,6)在参数空间的投影

图7

现在我们在xy笛卡尔坐标轴做另一点B(2, 8)的参数空间投影,如图 8所示

图8

可以发现,在参数空间中两直线交于粉色点位(2, 4),这里所体现的信息为:点A、B两点连接的直线斜率为m0=4,截距为b0=4,因此,现在我们可以理解霍夫变换其实就是计算参数空间累加点的值大小,值越大越说明这个点的参数m0b0所代表的直线置信度越高。

但是,基于笛卡尔坐标空间的霍夫变换存在一种特殊情况:当线垂直时梯度无穷大,无法在霍夫空间中表示出来。为了解决这个问题,我们在笛卡尔坐标系中用极坐标法表示直线。对应到霍夫空间也做对应变化。

(2)基于极坐标空间的霍夫变换

极坐标是指在平面内由极点极轴极径组成的坐标系,其中假设有一点P(ρ,θ),其中参数ρ表示极径,即极点O到P点的距离,参数θ表示极角,即OX到OP的角度。

假设有一条直线,原点到该直线的垂直距离为 ρ ,垂线与x轴的夹角为 θ ,那么这条直线是唯一的,且直线方程为:

d=xcos(θ)+ysin(θ)

和直角坐标系类似,霍夫空间中相交的曲线越多,交点表示的线在笛卡尔坐标系对应的点越多。我们在霍夫空间中定义交点的最小阈值来检测线,霍夫变换跟踪了帧中的每个点的霍夫空间交点,如果交点数量超过了阈值就确定一条对应参数 θ 和 d的线

之所以用阈值来确定参数,是因为在现实的应用场景中,许多直线并不是非常精细,或多或少存在偏差,导致参数空间各曲线不能交于精确的一点,因此我们需要将参数空间分块,分块的步长则为单位长度的ρθ,其次计算单位区域内累加的交点数量,将大于阈值(threshold)的区域值认定为直线存在,存储其参数(ρ,θ)

但是,分块的步长对检测的精准度也有影响,分的太细,计算代价就会上升,分的太大,计算的准确率就会下降,因此现在通用的常用做法是:ρ步长设为单像素单位,θ步长设为π180,并且现实场景中的应用也会使用一种Mask掩模的做法,提取我们感兴趣的图像区域,以此来大大减少计算量。

霍夫变换代码

hough = cv.HoughLinesP(segment, 2, np.pi / 180, 100, np.array([]), minLineLength = 100, maxLineGap = 50)

霍夫变换画出的车道线

四、获取车道线并叠加到原始图像中

这一步是将彩色图像与我们上一步绘制的车道线图像进行比例的融合

首先,综合所有线,求得左右两条车道线的平均斜率和截距

实现代码:

def calculate_lines(frame, lines):

    # Empty arrays to store the coordinates of the left and right lines

    left = []

    right = []

    # Loops through every detected line

    for line in lines:

        # Reshapes line from 2D array to 1D array

        x1, y1, x2, y2 = line.reshape(4)

        # Fits a linear polynomial to the x and y coordinates and returns a vector of coefficients which describe the slope and y-intercept

        parameters = np.polyfit((x1, x2), (y1, y2), 1)

        slope = parameters[0]

        y_intercept = parameters[1]

        # If slope is negative, the line is to the left of the lane, and otherwise, the line is to the right of the lane

        if slope < 0:

            left.append((slope, y_intercept))

        else:

            right.append((slope, y_intercept))

    # Averages out all the values for left and right into a single slope and y-intercept value for each line

    left_avg = np.average(left, axis = 0)

    right_avg = np.average(right, axis = 0)

    # Calculates the x1, y1, x2, y2 coordinates for the left and right lines

    left_line = calculate_coordinates(frame, left_avg)

    right_line = calculate_coordinates(frame, right_avg)

    return np.array([left_line, right_line])

然后,将原始彩色图像与我们刚画的车道线图像进行比例的融合。

这里需要用到的函数cv.addWeighted(src1, alpha, src2, beta, gamma, dst=None, dtype=None,参数src1表示第一张图像或矩阵,alpha是它对应的权重(Weight),src2表示的则是第二副图像或矩阵,beta是它对应的权重,第五个参数gamma表示整体添加到数值,默认为0即可。

我们是按照0.9:1进行融合。

实现代码 output = cv.addWeighted(frame, 0.9, lines_visualize, 1, 1)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1542162.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

html 几行的空间分成3个区域

1.代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>三个区域示例</title> …

SpringBoot+Vue考试系统免费分享

源码说明&#xff1a; 这是一个开源的SpringBoot与Vue开发的在线考试系统。经过站长测试&#xff0c;系统稳定可用&#xff0c;允许重复考试。 环境&#xff1a; 需要安装的环境包括Node.js v14.21.3、JDK8、Maven以及MySQL 5.7。 前端部署教程&#xff1a; 执行 npm inst…

掌控历史:如何通过Git版本管理工具提升你的开发效率

先一览全局: git目录 一.打开git二.git bash的基础命令三.配置git四.仓库搭建五.文件操作和状态六.忽略文件七.gitee的使用1.添加公钥2.创建仓库 八.vs中使用git九.git分支常用命令十.文件差异比较十一.文件回溯和推进十二.合并冲突和消除十三.合并/压缩提交十四.远程仓库推拉十…

新160个crackme - 062-syllogism-crackme1

运行分析 需要破解Name和Serial PE分析 C程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida找到成功弹窗字符串&#xff0c;双击进入函数 分析关键函数&#xff0c;还需要分析sub_401368函数 分析sub_401368函数&#xff0c;发现是将Name第一位替换为空格&#…

带你0到1之QT编程:十六、三种框架自带Dialog,助你在开发一臂之力

此为QT编程的第十六谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …

信息安全工程师(12)网络攻击概述

前言 网络攻击&#xff08;Cyber Attacks&#xff0c;也称赛博攻击&#xff09;是指针对计算机信息系统、基础设施、计算机网络或个人计算机设备的任何类型的进攻动作。这些攻击旨在破坏、揭露、修改、使软件或服务失去功能&#xff0c;或在未经授权的情况下偷取或访问计算机数…

消息中间件---Kafka

一、什么是Kafka&#xff1f; Kafka是一个分布式流处理平台,类似于消息队列或企业消息传递系统&#xff1b; 流处理事什么呢&#xff1f; 流处理就是数据处理工作流&#xff0c;本质上是一种计算机编程范例。流处理是对接收到的新数据事件的连续处理。‌它涉及对从生产者到消…

spring boot(学习笔记第二十课) vue + spring boot前后端分离项目练习

spring boot(学习笔记第二十课) vue spring boot前后端分离项目练习 学习内容&#xff1a; 后端程序构建前端程序构建 1. 后端程序构建 前后端分离结构 前后端就是前端程序和后端程序独立搭建&#xff0c;通过Restful API进行交互&#xff0c;进行松耦合的设计。后端程序构建…

WebGL入门(一)绘制一个点

源码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><scr…

【开源免费】基于SpringBoot+Vue.JS教师工作量管理系统(JAVA毕业设计)

本文项目编号 T 043 &#xff0c;文末自助获取源码 \color{red}{T043&#xff0c;文末自助获取源码} T043&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

【C++】内联函数(inline function)详解

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:C: 探索C编程精髓&#xff0c;打造高效代码仓库 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、前言 语法: 在函数定义前加上关键字 inli…

学不会最短路问题?看这篇就够了

数据结构入门学习&#xff08;全是干货&#xff09;——图论问题之最短路径 1 最短路径问题概述 最短路径问题的定义 在一个网络&#xff08;图&#xff09;中&#xff0c;求解两个顶点之间所有路径中边的权值之和最小的路径。这条路径称为最短路径。 源点(Source)&#xff…

ClickHouse-Kafka Engine 正确的使用方式

Kafka 是大数据领域非常流行的一款分布式消息中间件&#xff0c;是实时计算中必不可少的一环&#xff0c;同时一款 OLAP 系统能否对接 Kafka 也算是考量是否具备流批一体的衡量指标之一。ClickHouse 的 Kafka 表引擎能够直接与 Kafka 系统对接&#xff0c;进而订阅 Kafka 中的 …

openEuler系统安装内网穿透工具实现其他设备公网环境远程ssh连接

目录 前言 1. 本地SSH连接测试 2. openEuler安装Cpolar 3. 配置 SSH公网地址 4. 公网远程SSH连接 5. 固定连接SSH公网地址 6. SSH固定地址连接测试 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊openEuler系统安装内网穿透工具实现其他…

深度学习之微积分预备知识点(2)

极限&#xff08;Limit&#xff09; 定义&#xff1a;表示某一点处函数趋近于某一特定值的过程&#xff0c;一般记为 极限是一种变化状态的描述&#xff0c;核心思想是无限靠近而永远不能到达 公式&#xff1a; 表示 x 趋向 a 时 f(x) 的极限。 知识点口诀解释极限的存在左…

语言RPA流程组件介绍--获取网页信息

&#x1f6a9;【组件功能】&#xff1a;获取浏览器中显示网页的网页标题、源代码、网址、编码等信息 配置预览 配置说明 获取 网页源代码/标题/网址/编码 iframe 支持T或# 若获取的信息是框架iframe中的信息&#xff0c;需要手动填写框架名称&#xff0c;框架使用方法:框架…

文档图像恢复

文档图像恢复是指通过技术手段对损坏或质量不佳的文档图像进行修复&#xff0c;以提高其可读性和可用性。这种修复可以包括去除图像的噪声、畸变、阴影、模糊等多种问题&#xff0c;使文档图像更清晰、易于阅读。 文档图像恢复通常使用各种图像处理技术&#xff0c;包括但不限…

一个基于Vue3 + Arco Design + Vite3 + Pinia开箱即用的高质量中后台管理系统(附源码)

前言 随着业务的发展与复杂性的增加&#xff0c;现有的中后台管理系统面临着越来越多的挑战&#xff0c;如开发效率低下、系统性能瓶颈、项目扩展性差等问题。这些问题不仅影响了开发者的日常工作&#xff0c;还可能成为项目长期发展的障碍。那么&#xff0c;是否有一款软件能…

LabVIEW提高开发效率技巧----利用第三方库和工具

LabVIEW开发不仅依赖于自身强大的图形化编程能力&#xff0c;还得益于其庞大的用户社区和丰富的第三方库。这些工具和库能够帮助开发者快速解决问题&#xff0c;提升开发效率&#xff0c;避免从头开始编写代码。 1. LabVIEW工具网络&#xff08;NI Tools Network&#xff09; …

一些硬件知识(二十二)

搅拌机的转子是裸露在外面的&#xff0c;因此有一个安全开关&#xff0c;当上杯放上去后会按压安全开关&#xff0c;这样可以启动转子&#xff0c;否则是无法启动转子的&#xff0c;所以有些设备不通电或者转子不动是因为安全开关损坏&#xff1a; 、如下图&#xff0c;装上杯子…