传统方法车道线标注及相关知识

article/2025/10/2 22:10:22

目录

一、图像二值化处理

1.Sobel算子+绝对值

2.Sobel算子

3.倾斜角度

4.HLS颜色空间

5.二值图结合

二、车道线分割

1.仿射变换

2.车道线直方图

3.滑动窗口寻找车道线

4.车道线拟合

5.车道线区域标注


一、图像二值化处理

主要目的是通过二值化图像,使得车道线那部分图像被尽可能多地显示出来。这里使用4中方式得到不同的二值图。

1.Sobel算子+绝对值

转为灰度图后,将求得的x,y方向上的Sobel梯度(64位浮点数)取他们的绝对值,并进行归一化后,转为8位无符号数,根据设定的阈值筛选。

原因:标准的计算梯度的方式是下面第二种,通常,为了计算方便,也会直接取绝对值。

def abs_sobel_threshold(img, orient='x', thresh_min=2, thresh_max=1000000):gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)abs_sobel = Noneif orient == 'x':abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0))if orient == 'y':abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1))scaled_sobel = np.uint8(255 * abs_sobel / np.max(abs_sobel))binary_output = np.zeros_like(scaled_sobel)binary_output[(scaled_sobel >= thresh_min) & (scaled_sobel <= thresh_max)] = 255return binary_output

2.Sobel算子

这里是计算的实际的xy方向上的梯度大小,公式是G = np.sqrt( G(x)**2 + G(y)**2 )。同样也是进行归一化,转为8位无符号数。

def mag_threshold(img, sobel_kernel=3, mag_threshold=(50, 1000)):gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)gradmag = np.sqrt(sobel_x ** 2 + sobel_y ** 2)scale_factor = np.max(gradmag) / 255gradmag = (gradmag / scale_factor).astype(np.uint8)binary_out = np.zeros_like(gradmag)binary_out[(gradmag >= mag_threshold[0]) & (gradmag <= mag_threshold[1])] = 255return binary_out

3.倾斜角度

求出梯度后,通过arctan求得y和x之间的角度,并根据设定的角度的阈值过滤。这里设定了0.7和1.3之间,大约是45°-80°,也就是y方向梯度比x方向的梯度要大,但不能过大,如果y方向上梯度变化很大,x方向上梯度变化很小,可能是图中明暗交界非常明显的地方,以及车道线白线和车道交界的地方。根据下面第三张图可以看出来。主要保留了左边的黄色车道线。

def dir_threshold(img, sobel_kernel=5, thresh=(0.7, 1.3)):'''根据倾斜角度过滤'''gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)absgraddir = np.arctan(sobel_y / (sobel_x + 1e-7))binary_out = np.zeros_like(absgraddir)binary_out[(absgraddir >= thresh[0]) & (absgraddir <= thresh[1])] = 255return binary_out

4.HLS颜色空间

将原来的RGB图像,转到HLS颜色空间,并取出S通道,根据阈值,对S通道进行筛选。

原因:车道线是白色以及黄色,训练图像是深色的路面以及其他标识,因此将颜色转换到HLS色域,该颜色空间的三种表示方式分别是:Hue(色相)、Saturation(饱和度)、Lightness(亮度)。饱和度越低,颜色越偏向于灰色。亮度越高,颜色越偏向于白色。色相越高,颜色越鲜艳。因此,由于训练图整体是偏灰色的,把饱和度高的这一部分的提取出来,会更容易取出车道线。

def hls_thresh(img, thresh=(100, 255)):hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)_, _, s_channel = cv2.split(hls)binary_output = np.zeros_like(s_channel)binary_output[(s_channel >= thresh[0]) & (s_channel <= thresh[1])] = 255return binary_output

5.二值图结合

以上四种方式,根据调整的阈值不同,可能有不同的效果,我们根据四个输出,对其进行融合,得到最终的二值图。

def combined_threshold(img):abs_bin = abs_sobel_threshold(img, orient='x', thresh_min=50, thresh_max=255)mag_bin = mag_threshold(img, mag_threshold=(50, 255))dir_bin = dir_threshold(img, sobel_kernel=5, thresh=(0.7, 1.3))hls_bin = hls_thresh(img, thresh=(170, 255))combined = np.zeros_like(dir_bin)combined[(abs_bin == 255) & (mag_bin == 255) & (dir_bin == 255) | (hls_bin == 255)] = 255return combined, abs_bin, mag_bin, dir_bin, hls_bin

 以上四种以及二值图结合后效果如下:

二、车道线分割

1.仿射变换

SRC是实际图像的车道线所在的位置四点坐标,原图是1280*720,选出了一个梯形的车道线。DST是目标图像四点坐标,是一个矩形,要把车道线映射到这个矩形上。也就是说我们要把原图中,有畸变的车道线,变成一个俯视图的车道线。因此这里先获取到变换矩阵m以及逆变换矩阵m_inv。

import cv2
import numpy as np
import matplotlib.pyplot as pltsrc = np.float32([[200, 720], [1100, 720], [595, 450], [685, 450]])
dst = np.float32([[300, 720], [980, 720], [300, 0], [980, 0]])
m_inv = cv2.getPerspectiveTransform(dst, src)
m = cv2.getPerspectiveTransform(src, dst)

2.车道线直方图

(这里首先把原图的1280*720转为了720*1280,也就是行和列的表示方式,以方便矩阵运算。)

由于已经转为了二值图,车道线在图的下半部分,于是我们直接把下半部分,沿着高的方向求和,就可以得到直方图了。从1280/2=640也就是图的中心位置,左右两侧分别从100开始,找到峰值,峰值所在的地方,就是车道线的地方。

    histogram = np.sum(binary_warped[int(binary_warped.shape[0] / 2):, :], axis=0)midpoint = np.int(histogram.shape[0] / 2)leftx_base = np.argmax(histogram[100:midpoint]) + 100rightx_base = np.argmax(histogram[midpoint:binary_warped.shape[1] - 100]) + midpoint

3.滑动窗口寻找车道线

首先选出图像中的非零点,并返回非零点的x和y坐标,这张图里面有23819个非零点,那么x和y就全都是23819维的向量。注意:矩阵的行,是坐标中的y,矩阵的列,是坐标中的x。

滑动窗口的方式是由上到下,窗口的起始位置,就是峰值所在的像素点的位置。窗口宽度这里设定为200,因此需要中心点左边-100,右边+100。高度就是根据窗口数量,用总高度除以数量所得。

有了窗口之后,只需要判断非零点的xy坐标全都在窗口内即可,在的话,就记录他的索引,用这个索引可以在23819那么多的非零点里面,取得对应的点的坐标。对于左右车道线都进行同样的操作。当出现了车道线之后,根据索引,取得其像素坐标,根据均值作为新的窗口中心点,继续往下移动。

最后把两侧车道线的xy坐标都合并起来。

总结:这种滑动的方式寻找的车道线,只适合车道线比较清晰的路面,如果路面上很多车道线都磨没了,这种方式很可能就找不到车道线了,但是深度学习的方式可以自动脑补出来车道线。

# 滑动窗口查找车道线nwindows = 9window_height = np.int(binary_warped.shape[0] / nwindows)nonzero = binary_warped.nonzero()nonzeroy = np.array(nonzero[0])nonzerox = np.array(nonzero[1])leftx_current = leftx_baserightx_current = rightx_basemargin = 100minpix = 10left_lane_inds = []right_lane_inds = []for window in range(nwindows):win_y_low = binary_warped.shape[0] - (window + 1) * window_heightwin_y_high = binary_warped.shape[0] - window * window_heightwin_xleft_low = leftx_current - marginwin_xleft_high = leftx_current + marginwin_xright_low = rightx_current - marginwin_xright_high = rightx_current + margin# 非零y的索引在窗口内,非零x的索引也在窗口内,只记录行索引即可,后面通过索引寻找good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox > win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0]good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & (nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0]left_lane_inds.append(good_left_inds)right_lane_inds.append(good_right_inds)if len(good_left_inds) > minpix:leftx_current = np.int(np.mean(nonzerox[good_left_inds]))if len(good_right_inds) > minpix:rightx_current = np.int(np.mean(nonzerox[good_right_inds]))left_lane_inds = np.concatenate(left_lane_inds)right_lane_inds = np.concatenate(right_lane_inds)leftx = nonzerox[left_lane_inds]lefty = nonzeroy[left_lane_inds]rightx = nonzerox[right_lane_inds]righty = nonzeroy[right_lane_inds]

4.车道线拟合

通过np.polyfit,对车道线进行拟合,根据多项式函数的特性,如果变量的幂比较高,是可以出现曲线的,因此这里设为最高3次幂,基本上就足够用来拟合了。返回的是四个值,分别是三次项参数、二次项参数、一次项参数、以及偏置。由于我们是通过x来计算y,因此方法中传入的是y和x。

有了参数,就可以创建y坐标了,这里的undist就是原图,读出来后是720*1280,因此y坐标就是0到719。根据得到的多项式参数,就可以算出左侧车道线以及右侧车道线的x坐标了。

    para_l = np.polyfit(lefty, leftx, deg=3)para_r = np.polyfit(righty, rightx, deg=3)ploty = np.linspace(0, undist.shape[0] - 1, undist.shape[0])left_fitx = para_l[0] * ploty ** 3 + para_l[1] * ploty ** 2 + para_l[2] * ploty + para_l[3]right_fitx = para_r[0] * ploty ** 3 + para_r[1] * ploty ** 2 + para_r[2] * ploty + para_r[3]

5.车道线区域标注

这里用一张黑色的图,把车道线的区域标出来。

    # 生成一张黑图,做mask,将车道线区域标注出来color_warp = np.zeros((720, 1280, 3), dtype='uint8')pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))])pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))])pts = np.hstack((pts_left, pts_right))cv2.fillPoly(color_warp, np.int_([pts]), (0, 255, 0))

对于左侧车道线,y是从0到719,也就是从上到下,因此,左侧车道线是从右到左的x坐标,由大到小排列,坐标顺序就是下图中的从上到下。

对于右侧也是一样,但是对于cv2.fillPoly来说,需要提供的是轮廓点,如果想生成下面左图这样的填充好的图的话,顺序应当是左上,左下,右下,右上,这样才是一个完整的轮廓,因此需要把右侧的坐标上下反转,用来在画图时,和左侧进行连接。

填充完毕,就可以逆变换了,使用前面已经准备好的矩阵进行逆变换。最后通过addWeighted方法,把变换好的mask和原图融合在一起,得到结果。

newwarp = cv2.warpPerspective(color_warp, m_inv, imgOut_size, flags=cv2.INTER_LINEAR)
result = cv2.addWeighted(img, 0.5, newwarp, 0.5, 0)

 

 


http://chatgpt.dhexx.cn/article/VCJmZOO7.shtml

相关文章

学习笔记之车道线相关记录

一. 车道线相关的知识 &&1.标线的分类 以下分类来自于百科&#xff1a; 按照道路交通标线的功能划分为&#xff1a;指示标线、警告标线和禁止标线。 按标划方法可分为&#xff1a;白色虚线、白色实线、黄色虚线、黄色实线、双白虚线、双白实线、双黄虚线和双黄实线…

关于python 最简单封装实例

一、 #定义一个类 class Person: #init是定义类实例初始化函数 ,没有返回return def __init__(self,name,area): self.name name self.area area #类里面定义方法 def run(self): print(self.name) …

Python软件封装打包

作者&#xff1a;Naples 链接&#xff1a;https://www.zhihu.com/question/32703639/answer/165326590 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 Python Tkinter打包封装的方法有&#xff1a;PyInstaller, py…

Python封装、继承和多态

Python 语言在设计之初&#xff0c;就定位为一门面向对象的编程语言&#xff0c;“Python 中一切皆对象”。同时&#xff0c;Python 也支持面向对象的三大特征&#xff1a;封装、继承和多态。 一、封装 封装&#xff08;Encapsulation&#xff09;&#xff0c;即在设计类时&am…

python程序封装

python程序封装1 报错请执行pip install --upgrade setuptools 和 pip install --upgrade wheel 步骤如下&#xff1a; &#xff08;1&#xff09;安装pyinstaller&#xff0c;可以直接在cmd命令行中&#xff0c;输入命令“pip install pyinstaller”&#xff0c;安装pyinsta…

python封装程序

#终端/cmd命令下&#xff1a; 1.安装python Welcome to Python.org 2.安装pip pip PyPI 下载get-pip.py 在cmd窗口下执行&#xff0c;python */*/get-pip.py&#xff08;*为文件所在位置&#xff09; *如果安装好后出现 不是内部命令的情况 需要在环境中添加&#xff…

制作python包,封装成可用模块

制作python包&#xff0c;封装成可用模块 首先编写py程序: printtest.py #coding: utf-8 def test():print(print test)if __name__ __main__:test() 将以上.py文件做成python模块&#xff0c;需要在相同目录下创建setup.py文件&#xff0c;setup.py中输入配置信息: #cod…

Python 程序封装-打包成exe程序

Python 程序封装-打包成exe程序 前言一、 Python 打包工具—Pyinstaller二、打包具体过程1. 打包成仅包含一个独立的exe程序2. 打包成包含文件夹的程序&#xff0c;内有相关的依赖库&#xff08;推荐&#xff09;3. 其他的打包命令 三、注意事项 欢迎学习交流&#xff01; 邮箱…

怎么python程序封装?此文详解

python程序封装1 步骤如下: (1)安装pyinstaller,可以直接在cmd命令行中,输入命令“pip install pyinstaller”,安装pyinstaller (2)进入py代码的保存目录,这里py代码放在“E:\python学习\python_work” (3)cmd,输入命令:e:,进入e盘 (4)继续输入:E:\pytho…

Python封装

在用新电脑做python的封装的时候&#xff0c;出现了一系列的问题。在这里简单写一下Python的封装的一些流程以及可能出现的问题和解决方法吧。 封装我选择的是pyinstaller 首先是安装pyinstaller&#xff1a;Python 默认并不包含 PyInstaller 模块&#xff0c;因此需要自行安…

python之类的封装

博主简介&#xff1a;原互联网大厂tencent员工&#xff0c;网安巨头Venustech员工&#xff0c;阿里云开发社区专家博主&#xff0c;微信公众号java基础笔记优质创作者&#xff0c;csdn优质创作博主&#xff0c;创业者&#xff0c;知识共享者,欢迎关注&#xff0c;点赞&#xff…

Python入门——函数封装

当工程量比较大时&#xff0c;我们可以采取“函数封装”的方法实现函数的重复使用&#xff0c;避免“重复造轮子”。 步骤 手动创建一个包&#xff0c;只需进行以下 2 步操作&#xff1a; 新建一个文件夹&#xff0c;文件夹的名称就是新建包的包名&#xff1b;在该文件夹中&…

Python学习基础笔记五十八——封装

封装&#xff1a;广义上的面向对象封装。代码的保护。面向对象的思想本身就是一种封装&#xff0c;只让自己的对象能调用自己类中的方法。 狭义的封装概念&#xff1a;面向对象的三大特性之一&#xff1a;让属性和方法都藏起来&#xff0c;不让你看见。 例1&#xff1a; clas…

Python必备封装基本代码~Python函数

大家好&#xff0c;我是辣条 最近不少粉丝通过文末找到辣条让我分享一些代码封装这一块的内容&#xff0c;今天他来了~ 一遍看不懂就收起来慢慢看&#xff0c;我写的还是很详细的&#xff0c;一定是能轻松拿捏住Python函数的&#xff0c;不过还请记得多多支持辣条&#xff0c;…

PMP学习笔记顺口溜

区分几种组织结构 老板项目为系统&#xff1b; 只有职能为职能&#xff1b; 多个部门多部门&#xff0c; 多个项目为项目 职能项目为矩阵&#xff0c;项强为强&#xff0c;项弱为弱&#xff1b; 项目职能一般大 &#xff1a;平衡 网络分散为虚拟

PMP学习笔记 零 启动

PMP 学习笔记 零 启动 我为什么要学习PMP 我是一个程序猿&#xff0c;别人让我做什么&#xff0c;我就去做什么&#xff0c;提出什么需求我就完成&#xff0c;但是渐渐的我不满足成为一个工具人&#xff0c;就开始也开始设计功能&#xff0c;和抛开产品经理独自完成一些需求&…

我的PMP学习考试心得

01看书学习是基础&#xff0c;但更需要深化理解 备考期间&#xff0c;我通读了PMBOK&#xff0c;认真观看了视频教程和小红书&#xff0c;按照班主任的要求循序渐进&#xff0c;慢慢掌握了基本的知识点。因为缺少基础&#xff0c;所以只能通过反复记忆&#xff0c;特别是利用每…

PMP学习群沙龙+抽奖活动

3月考试临近&#xff0c;学员们都在积极备考。 老师们当然也不会闲着。 这不&#xff0c;就给大家带来福利了。 针对我们的PMP学习群的福利活动即将上线。 进群填写信息即可获得抽奖机会一个&#xff0c;邀请进群一人再赠送一个抽奖机会。 百分百中奖 奖品&#xff1a; 超…

PMP 学习记录

1.预算和估算 2.挣值分析 1&#xff09;三个指标&#xff1a; PV &#xff08;planned value&#xff09;&#xff0c;计划值&#xff1b; AC&#xff08;Actual Cost&#xff09;&#xff1a;实际成本&#xff0c; EV&#xff1a;Earned value&#xff0c;挣得的值&#xff1b…

pmp学习资料下载-pmp备考

PMP的全称是Project Management Professional&#xff0c;中文名称叫项目管理专业人士资格认证。 它是由美国项目管理协会(PMI)发起的&#xff0c;严格评估项目管理人员知识技能是否具有高品质的资格认证考试。其目的是为了给项目管理人员提供统一的行业标准。 目前PMP已经在…