比特派钱包苹果app官方下载|lss
海运lss是什么费用?LSS附加费定义、收费标准及费用承担主体详解-跨境电商行业报告-敦煌网
海运lss是什么费用?LSS附加费定义、收费标准及费用承担主体详解-跨境电商行业报告-敦煌网
欢迎您!登录
或
轻松注册
您好!
(
88
)
退出
我的DHgate
服务市场
帮助
敦煌客户端
论坛
Buyer Home
帮助中心
联系客服
举报&投诉
买全球,卖全球
简体中文
English
简体中文
全部频道
全部频道
政策规则
敦煌跨境课堂
帮助中心
公告
媒体报道
选品推荐
成功故事
首页
入驻通道
入驻通道
招商计划
全年营销日历
商家成长
商家成长计划
商家知识库
直播预告
商家成长地图
跨境课堂
选品推荐
大时尚
3C数码
大家居
汽配&工商业
海外市场调研
运营工具
驼驼礼包套餐
数据智囊
视觉精灵
流量快车
店铺模板
流量获取
联盟CPS营销
网红带货广场
汇投广告
站内广告系统
品效宝
政策规则
增值服务
渠道合作
国货优品
乘风星舰
工业出海
敦煌网集团
MyyShop
DHLink
我要开店
首页/选品推荐/正文
海运lss是什么费用?LSS附加费定义、收费标准及费用承担主体详解
2020-09-14
来源:
跨境工具人的king
标签:
海运 lss 国际物流 外贸知识
来自百度图片 现在船公司进出口商品时需要征收LSS附加费,LSS是什么意思?海运lss是什么费用? 海运LSS是什么费用: LSS的英文全称为Low Sulphur Fuel Surcharge或Low Sulphur Surcharge,意为“低硫附加费”,是众多的航运附加费中的其中一个费用。 海运lss由来: 为支持节能减排,航运业出台船舶排放标准。2015年1月1日起,应使用硫含量不大于0.10%m/m的燃油。此外,如果船舶使用与低硫燃油同等效果的废气清洗系统,可免除低硫燃油使用要求。2015年1月1日起的这个限制排放标准同样适用于另外三个IMO划定的排放控制区(ECA)。 按照这个排放限制,要降低燃油排放硫含量,船舶在进入这些控制区域时就必须使用价格和标准更高的燃油,或者加装废气清洗系统,这些都将导致成本上升。于是低硫燃油附加费LSS出现了。 LSS低硫附加费收费标准: 各船公司收取的LSS费不一样,基本港和转运港收费标准也不一样。对于基本港,通常是15-25usd/TEU,如欧洲基本港,部分船公司收费情况如下(费用可能会调整,仅供参考): 美国总统轮船APL:USD20/TEU 日本邮船NYK:USD25/TEU 东方海外OOCL:USD24/TEU 地中海航运MSC:USD15/TEU 马士基航运MSK:USD15/TEU 中远集装箱COSCO:USD20/TEU 达飞轮船CMA:USD25/TEU 川崎汽船KLINE:USD25/TEU LSS如何报价: 不同货代对于LSS的报价方式可能有所不同。 有的货代是ALL IN价,即LSS费用已经包含在海运费里面,不再作为一个单独费用列出来。有的货代把LSS费用单独列出来。 发货人在询价时最好问清楚,是否包含了LSS费用 lss费用由谁承担: 一般认为LSS费用不属于起运港本地费用(local charge),而是属于海运费的一个组成部分,所以一些货代把LSS包含在海运费里报一个ALL IN价是合理的。 如果做的是FOB指定货,FOB条款不含运费,作为运费组成部分的LSS理应由收货人承担。 如果是C&F或CIF条款,LSS费应该由发货人承担。但是对于船公司来说,他们基本都默认起运港shipper预付此费用。 以上是关于LSS低硫附加费的详细介绍。(来源:网络整理)本文转载,如有侵权请联系我们删除
上一篇:关税怎么算 从价关税的计算方法
下一篇:ce认证是什么,ce认证是欧洲一种安全认证标志
敦煌Dhgate跨境平台微信公众号
实时获取行业资讯、运营技巧;
1v1资深运营顾问专属服务;
免费领取跨境运营地图;
提供专业解决方案!
关于我们
关于DHgate.com
招贤纳士
服务条款
网站地图
微博@敦煌网DHgate
新手指南
上传产品
运费设置
店铺创建
促销活动
交易流程
选品推荐
敦煌服务
在线客服
DHLink
在线发货
手机版
物流服务
应用市场
授权代运营商
渠道合作
交易安全
货款提现
银行账户设置
请款
风险控制
纠纷处理
Copyright Notice © 2004 - 2024 DHgate.com All rights reserved.
增值电信业务经营许可证: 京B2-20180361 |
京ICP备18054285号-7
| 京公网安备11010802029844号 |
经营证照
我要开店
微信咨询
扫码添加我微信
入驻通道
电话咨询
LSS低硫附加费的含义、由来和收费标准全解读 - 知乎
LSS低硫附加费的含义、由来和收费标准全解读 - 知乎切换模式写文章登录/注册LSS低硫附加费的含义、由来和收费标准全解读巨鲨鱼Erin1. LSS低硫附加费的含义LSS的英文全称为Low Sulphur Fuel Surcharge或Low Sulphur Surcharge,意为“低硫附加费”,这是众多的航运附加费中的其中一个费用,是最近才有的比较新的一个费用。2. LSS的由来根据2015年1月1日生效的环境保护法规,新的船舶排放控制条例将在欧洲北部(包括波罗的海、北海和英吉利海峡)和北美(自美国和加拿大海岸200海里)区域正式生效。此条例严格控制船舶燃油排放物中硫化物的含量,从2015年之前的1%降低至0.1%。有关排放控制区的条例细则由国际海事组织(IMO)的成员国拟定并通过。排放控制区要求承运商在船舶航行至此区域时降低某些特定类型物质排放量,如硫化物(SOx)和氮化物(NOx)。航运中常规燃油为重质燃油,硫含量通常在1.0%至3.5%之间。为符合含硫量为0.1%的排放控制区的要求,航运公司必须使用不同类型纯度更高的燃料,因此价格也更为昂贵。3. LSS低硫附加费如何报价不同货代对于LSS的报价方式可能有所不同。如有的货代是ALL IN价,即LSS费用已经包含在海运费里面,不再作为一个单独费用列出来。有的货代把LSS费用单独列出来。发货人在询价时最好问清楚,是否包含了LSS费用。4. LSS由发货人承担还是收货人承担通常认为LSS费用不属于起运港本地费用(local charge),应该属于海运费的一个组成部分,所以一些货代把LSS包含在海运费里报一个ALL IN价是合理的。如果做的是FOB指定货,货代还向发货人收取LSS费用,这就不合理。因为FOB条款不含运费,作为运费组成部分的LSS理应由收货人承担。如果是C&F或CIF条款,LSS费应该由发货人承担。但是对于船公司来说,他们基本都默认起运港shipper预付此费用。5. LSS低硫附加费的收费标准对于船公司来说,低硫时代下,不得不更多使用低硫燃油,低硫燃油的成本比以前的常规燃油高很多,为了弥补这个上涨的成本,而加收低硫附加费,把增加的成本和货主共同分担。各船公司收取的LSS费不一样,基本港和转运港收费标准也不一样。对于基本港,通常是15-25usd/TEU,如欧洲基本港,部分船公司收费情况如下(费用可能会调整,仅供参考):美国总统轮船APL:USD20/TEU日本邮船NYK:USD25/TEU东方海外OOCL:USD24/TEU地中海航运MSC:USD15/TEU马士基航运MSK:USD15/TEU中远集装箱COSCO:USD20/TEU达飞轮船CMA:USD25/TEU韩进海运HANJIN:USD25/TEU川崎汽船KLINE:USD25/TEU更多的国际物流如何找客户以下答案可以查看。编辑于 2023-06-05 16:08・IP 属地福建收费货代赞同 5添加评论分享喜欢收藏申请
LSS(Lift,Splat,Shoot)-实现BEV感知的最佳利器 - 知乎
LSS(Lift,Splat,Shoot)-实现BEV感知的最佳利器 - 知乎首发于BEV感知切换模式写文章登录/注册LSS(Lift,Splat,Shoot)-实现BEV感知的最佳利器Uber.N同济大学 车辆工程硕士今天分享的这篇论文的重要性,不言自明,在工业界具有非常重要的地位。自从BEV下的感知在Tesla AI Day 被提出后,业内很多公司开始进行BEV工程化的探索。当前在BEV下进行感知方法大致分为两类,一类是以Transformer 为主体的隐式深度(Depth)信息进行转换的架构,另一类则是基于显示的深度估计投影到BEV下的方法,也就是本文的主人公——LSS(Lift,Splat,Shoot)。而这篇发表于ECCV2020的文章在BEV探索的道路上成为了一把利剑,有不少SOTA的论文是在此基础上改进得出,例如CaDDN,BEVDet等文章均是在此基础上改进得到。当前,这套由NVIDIA提出的LSS仍然是目前业内实现BEV感知的最佳利器之一。LSS 的大致流程概述题目:Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D论文链接:Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D代码链接:GitHub - nv-tlabs/lift-splat-shoot: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D (ECCV 2020)关于论文的解读,网上已经有太多优秀的论文思路和源码详解,我再重复也不过是画蛇添足。在此推荐一个由宏景智驾的一位工程师出的的手撕代码视频,如果想了解论文思路和源码,看这个视频就够了,供诸君参考:手撕BEV的开山之作:lift, splat, shoot(没完全shoot)_哔哩哔哩_bilibili论文亮点LSS最大的贡献在于:提供了一个端到端的训练方法,解决了多个传感器融合的问题。传统的多个传感器单独检测后再进行后处理的方法无法将此过程损失传进行反向传播而调整相机输入,而LSS则省去了这一阶段的后处理,直接输出融合结果。最关键的"Lift"在这篇文章中,最关键的就是这个Lift部分,可以简单回顾一下:整个Lift过程,其实分为三个部分。这个过程其实CaDDN论文中的画的图比较容易理解,我大致画了下:“Lift” 的完整过程特征提取&深度估计多视角相机输入后,进入Backbone,同时利用一个深度估计网络估计出Depth的feature。值得注意的是,这里的Depth feature与 Image feature 的size 是相等的,因为后续要进行外积(Outer product)操作。2. 外积(Outer product)这一步是LSS的最灵魂的操作。作者在原文中多次提到,Depth的信息是"ambiguous"(模棱两可)的,因为作者并没有使用每个pixel直接预测的Depth具体值,而是使用Depth distrbution的方式来表示每个pixel的Depth信息。因此,其实无法确定每个pixel的特征投影BEV视角下的具体位置,因此对于每个pixel特征,作者使用的是“all possible depths ”。使用外积操作,将Image feature (维度 H\times W \times C ) 和 Depth feature (维度 H\times W \times D) 构造成一个(H\times W \times D \times C)的 Frustum feature。3. Grid Sampling这一步的目的就是将上面构造出的Frustum Feature 利用相机外参和内参转换到BEV视角下。具体过程是,通过限定好BEV视角的范围,划定好一个个的grid,将能够投影到相应grid 的 Feature 汇总到一个grid 里,之后再进行 "Splat"操作。这一步虽然听起来平平无奇,但是在具体的代码实现方面却有很多trick值得学习,感兴趣的可以去看我上面分享的大佬代码带读的链接。总结和展望LSS从提出到现在已经经过了时间的验证,大量学者在其基础上进行了更进一步的研究,提出了各种花里胡哨的SOTA模型。总结来看,值得提及的有以下几点:优点:1.LSS的方法提供了一个很好的融合到BEV视角下的方法。基于此方法,无论是动态目标检测,还是静态的道路结构认知,甚至是红绿灯检测,前车转向灯检测等等信息,都可以使用此方法提取到BEV特征下进行输出,极大地提高了自动驾驶感知框架的集成度。2.虽然LSS提出的初衷是为了融合多视角相机的特征,为“纯视觉”模型而服务。但是在实际应用中,此套方法完全兼容其他传感器的特征融合。如果你想融合超声波雷达特征也不是不可以试试。缺点:1.极度依赖Depth信息的准确性,且必须显示地提供Depth 特征。当然,这是大部分纯视觉方法的硬伤。如果直接使用此方法通过梯度反传促进Depth网络的优化,如果Depth 网络设计的比较复杂,往往由于反传链过长使得Depth的优化方向比较模糊,难以取得较好效果。当然,一个好的解决方法是先预训练好一个较好的Depth权重,使得LSS过程中具有较为理想的Depth输出。2.外积操作过于耗时。虽然对于机器学习来说,这样的计算量不足为道,但是对于要部署到车上的模型,当图片的feature size 较大, 且想要预测的Depth距离和精细度高时,外积这一操作带来的计算量则会大大增加。这十分不利于模型的轻量化部署,而这一点上,Transformer的方法反而还稍好一些。以上均为个人见解,如果大佬们有其他高见,希望在评论区怒斥。我是Uber,关注我,了解更多关于自动驾驶、机器学习的知识~编辑于 2022-11-06 21:44・IP 属地上海自动驾驶机器学习深度学习(Deep Learning)赞同 14116 条评论分享喜欢收藏申请转载文章被以下专栏收录BE
LSS (Lift, Splat, Shoot) 论文+源码万字长文解析 - 知乎
LSS (Lift, Splat, Shoot) 论文+源码万字长文解析 - 知乎首发于自动驾驶学习笔记切换模式写文章登录/注册LSS (Lift, Splat, Shoot) 论文+源码万字长文解析Fangzh北京理工大学 信息与通信工程硕士在读论文研究背景LSS是一篇发表在ECCV 2020上有关自动驾驶感知方向的论文,具体子任务为object segmentation and map segmentation。论文和官方repo如下:论文:官方repo:目前在自动驾驶领域,比较火的一类研究方向是基于采集到的环视图像信息,去构建BEV视角下的特征完成自动驾驶感知的相关任务。所以如何准确的完成从相机视角向BEV视角下的转变就变得由为重要。目前感觉比较主流的方法可以大体分为两种:1)显式估计图像的深度信息,完成BEV视角的构建,在某些文章中也被称为自下而上的构建方式;2)利用transformer中的query查询机制,利用BEV Query构建BEV特征,这一过程也被称为自上而下的构建方式;具体分析也可以参考这两篇博客内容(侵删)。LSS这篇论文的核心则是通过显式估计图像的深度信息,对采集到的环视图像进行特征提取,并根据估计出来的离散深度信息,实现图像特征向BEV特征的转换,进而完成自动驾驶中的语义分割任务。论文中展示的插图:右侧为LSS算法根据当前帧的六张环视图像得到的BEV视角下的语义感知结果算法实现过程梳理一、相关参数设置对于感知算法而言,我认为比较重要的是要了解在BEV视角下,x轴和y轴方向的感知距离,以及BEV网格的单位大小。在LSS源码中,其感知范围,BEV单元格大小,BEV下的网格尺寸如下:感知范围x轴方向的感知范围 -50m ~ 50m;y轴方向的感知范围 -50m ~ 50m;z轴方向的感知范围 -10m ~ 10m;BEV单元格大小x轴方向的单位长度 0.5m;y轴方向的单位长度 0.5m;z轴方向的单位长度 20m;BEV的网格尺寸200 x 200 x 1;深度估计范围由于LSS需要显式估计像素的离散深度,论文给出的范围是 4m ~ 45m,间隔为1m,也就是算法会估计41个离散深度;二、模型相关参数模型用到的参数主要包括以下7个参数,分别是imgs,rots,trans,intrinsic,post_rots,post_trans,binimgs;imgs:输入的环视相机图片,imgs = (bs, N, 3, H, W),N代表环视相机个数;rots:由相机坐标系->车身坐标系的旋转矩阵,rots = (bs, N, 3, 3);trans:由相机坐标系->车身坐标系的平移矩阵,trans=(bs, N, 3);intrinsic:相机内参,intrinsic = (bs, N, 3, 3);post_rots:由图像增强引起的旋转矩阵,post_rots = (bs, N, 3, 3);post_trans:由图像增强引起的平移矩阵,post_trans = (bs, N, 3);binimgs:由于LSS做的是语义分割任务,所以会将真值目标投影到BEV坐标系,将预测结果与真值计算损失;具体而言,在binimgs中对应物体的bbox内的位置为1,其他位置为0;三、算法前向过程在进行详细描述LSS算法前向过程之前,先整体概括下LSS算法包括的五个步骤。1)生成视锥,并根据相机内外参将视锥中的点投影到ego坐标系;2)对环视图像完成特征的提取,并构建图像特征点云;3)利用变换后的ego坐标系的点与图像特征点云利用Voxel Pooling构建BEV特征;4)对生成的BEV特征利用BEV Encoder做进一步的特征融合;5)利用特征融合后的BEV特征完成语义分割任务;生成视锥,并完成视锥锥点由图像坐标系->ego坐标系的空间位置转换a)生成视锥需要注意的是,生成的锥点,其位置是基于图像坐标系的,同时锥点是图像特征上每个单元格映射回原始图像的位置。生成方式如下:def create_frustum():
# 原始图片大小 ogfH:128 ogfW:352
ogfH, ogfW = self.data_aug_conf['final_dim']
# 下采样16倍后图像大小 fH: 8 fW: 22
fH, fW = ogfH // self.downsample, ogfW // self.downsample
# self.grid_conf['dbound'] = [4, 45, 1]
# 在深度方向上划分网格 ds: DxfHxfW (41x8x22)
ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)
D, _, _ = ds.shape # D: 41 表示深度方向上网格的数量
"""
1. torch.linspace(0, ogfW - 1, fW, dtype=torch.float)
tensor([0.0000, 16.7143, 33.4286, 50.1429, 66.8571, 83.5714, 100.2857,
117.0000, 133.7143, 150.4286, 167.1429, 183.8571, 200.5714, 217.2857,
234.0000, 250.7143, 267.4286, 284.1429, 300.8571, 317.5714, 334.2857,
351.0000])
2. torch.linspace(0, ogfH - 1, fH, dtype=torch.float)
tensor([0.0000, 18.1429, 36.2857, 54.4286, 72.5714, 90.7143, 108.8571,
127.0000])
"""
# 在0到351上划分22个格子 xs: DxfHxfW(41x8x22)
xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)
# 在0到127上划分8个格子 ys: DxfHxfW(41x8x22)
ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)
# D x H x W x 3
# 堆积起来形成网格坐标, frustum[i,j,k,0]就是(i,j)位置,深度为k的像素的宽度方向上的栅格坐标 frustum: DxfHxfWx3
frustum = torch.stack((xs, ys, ds), -1)
return nn.Parameter(frustum, requires_grad=False)b)锥点由图像坐标系向ego坐标系进行坐标转化这一过程主要涉及到相机的内外参数,对应代码中的函数为get_geometry();def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
B, N, _ = trans.shape # B: batch size N:环视相机个数
# undo post-transformation
# B x N x D x H x W x 3
# 抵消数据增强及预处理对像素的变化
points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))
# 图像坐标系 -> 归一化相机坐标系 -> 相机坐标系 -> 车身坐标系
# 但是自认为由于转换过程是线性的,所以反归一化是在图像坐标系完成的,然后再利用
# 求完逆的内参投影回相机坐标系
points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
points[:, :, :, :, :, 2:3]
), 5) # 反归一化
combine = rots.matmul(torch.inverse(intrins))
points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
points += trans.view(B, N, 1, 1, 1, 3)
# (bs, N, depth, H, W, 3):其物理含义
# 每个batch中的每个环视相机图像特征点,其在不同深度下位置对应
# 在ego坐标系下的坐标
return points2. 对环视图像进行特征提取,并构建图像特征点云a)利用Efficientnet-B0主干网络对环视图像进行特征提取输入的环视图像 (bs, N, 3, H, W),在进行特征提取之前,会将前两个维度进行合并,一起提取特征,对应维度变换为 (bs, N, 3, H, W) -> (bs * N, 3, H, W);其输出的多尺度特征尺寸大小如下:level0 = (bs * N, 16, H / 2, W / 2)
level1 = (bs * N, 24, H / 4, W / 4)
level2 = (bs * N, 40, H / 8, W / 8)
level3 = (bs * N, 112, H / 16, W / 16)
level4 = (bs * N, 320, H / 32, W / 32)b)对其中的后两层特征进行融合,丰富特征的语义信息,融合后的特征尺寸大小为 (bs * N, 512, H / 16, W / 16)Step1: 对最后一层特征升采样到倒数第二层大小;
level4 -> Up -> level4' = (bs * N, 320, H / 16, W / 16)
Step2:对主干网络输出的后两层特征进行concat;
cat(level4', level3) -> output = (bs * N, 432, H / 16, W / 16)
Step3:对concat后的特征,利用ConvLayer卷积层做进一步特征拟合;
ConvLayer(output) -> output' = (bs * N, 512, H / 16, W / 16)
其中ConvLayer层构造如下:
"""Sequential(
(0): Conv2d(432, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(4): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU(inplace=True)
)"""c)估计深度方向的概率分布并输出特征图每个位置的语义特征 (用64维的特征表示),整个过程用1x1卷积层实现c)步骤整体pipeline
output' -> Conv1x1 -> x = (bs * N, 105, H / 16, W / 16)
b)步骤输出的特征:
output = Tensor[(bs * N, 512, H / 16, W / 16)]
c)步骤使用的1x1卷积层:
Conv1x1 = Conv2d(512, 105, kernel_size=(1, 1), stride=(1, 1))
c)步骤输出的特征以及对应的物理含义:
x = Tensor[(bs * N, 105, H / 16, W / 16)]
第二维的105个通道分成两部分;第一部分:前41个维度代表不同深度上41个离散深度;
第二部分:后64个维度代表特征图上的不同位置对应的语义特征;d)对c)步骤估计出来的离散深度利用softmax()函数计算深度方向的概率密度e)利用得到的深度方向的概率密度和语义特征通过外积运算构建图像特征点云代码实现:# d)步骤得到的深度方向的概率密度
depth = (bs * N, 41, H / 16, W / 16) -> unsqueeze -> (bs * N, 1, 41, H / 16, W / 16)
# c)步骤得到的特征,选择后64维是预测出来的语义特征
x[:, self.D:(self.D + self.C)] = (bs * N, 64, H / 16, W / 16) -> unsqueeze(2) -> (bs * N, 64, 1 , H / 16, W / 16)
# 概率密度和语义特征做外积,构建图像特征点云
new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2) # (bs * N, 64, 41, H / 16, W / 16)论文图例:论文中表示构建图像特征点云的实现过程插图3. 利用ego坐标系下的坐标点与图像特征点云,利用Voxel Pooling构建BEV特征a)Voxel Pooling前的准备工作def voxel_pooling(self, geom_feats, x):
# geom_feats;(B x N x D x H x W x 3):在ego坐标系下的坐标点;
# x;(B x N x D x fH x fW x C):图像点云特征
B, N, D, H, W, C = x.shape
Nprime = B*N*D*H*W
# 将特征点云展平,一共有 B*N*D*H*W 个点
x = x.reshape(Nprime, C)
# flatten indices
geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() # ego下的空间坐标转换到体素坐标(计算栅格坐标并取整)
geom_feats = geom_feats.view(Nprime, 3) # 将体素坐标同样展平,geom_feats: (B*N*D*H*W, 3)
batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
device=x.device, dtype=torch.long) for ix in range(B)]) # 每个点对应于哪个batch
geom_feats = torch.cat((geom_feats, batch_ix), 1) # geom_feats: (B*N*D*H*W, 4)
# filter out points that are outside box
# 过滤掉在边界线之外的点 x:0~199 y: 0~199 z: 0
kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
& (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
& (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
x = x[kept]
geom_feats = geom_feats[kept]
# get tensors from the same voxel next to each other
ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
+ geom_feats[:, 1] * (self.nx[2] * B)\
+ geom_feats[:, 2] * B\
+ geom_feats[:, 3] # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
sorts = ranks.argsort()
x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts] # 按照rank排序,这样rank相近的点就在一起了
# cumsum trick
if not self.use_quickcumsum:
x, geom_feats = cumsum_trick(x, geom_feats, ranks)
else:
x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks)
# griddify (B x C x Z x X x Y)
final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device) # final: bs x 64 x 1 x 200 x 200
final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # 将x按照栅格坐标放到final中
# collapse Z
final = torch.cat(final.unbind(dim=2), 1) # 消除掉z维
return final # final: bs x 64 x 200 x 200b)采用cumsum_trick完成Voxel Pooling运算,代码和图例如下:主要需要注意的,图中的区间索引代表下面代码中的ranks,点的特征代表的是x;代码:class QuickCumsum(torch.autograd.Function):
@staticmethod
def forward(ctx, x, geom_feats, ranks):
x = x.cumsum(0) # 求前缀和
kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool)
kept[:-1] = (ranks[1:] != ranks[:-1]) # 筛选出ranks中前后rank值不相等的位置
x, geom_feats = x[kept], geom_feats[kept] # rank值相等的点只留下最后一个,即一个batch中的一个格子里只留最后一个点
x = torch.cat((x[:1], x[1:] - x[:-1])) # x后一个减前一个,还原到cumsum之前的x,此时的一个点是之前与其rank相等的点的feature的和,相当于把同一个格子的点特征进行了sum
# save kept for backward
ctx.save_for_backward(kept)
# no gradient for geom_feats
ctx.mark_non_differentiable(geom_feats)
return x, geom_feats图例: 图例来源:https://zhuanlan.zhihu.com/p/567880155(侵删)4 + 5. 对生成的BEV特征利用BEV Encoder做进一步的特征融合 + 语义分割结果预测a)对BEV特征先利用ResNet-18进行多尺度特征提取,输出的多尺度特征尺寸如下level0:(bs, 64, 100, 100)
level1: (bs, 128, 50, 50)
level2: (bs, 256, 25, 25)b)对输出的多尺度特征进行特征融合 + 对融合后的特征实现BEV网格上的语义分割Step1: level2 -> Up (4x) -> level2' = (bs, 256, 100, 100)
Step2: concat(level2', level0) -> output = (bs, 320, 100, 100)
Step3: ConvLayer(output) -> output' = (bs, 256, 100, 100)
'''ConvLayer的配置如下
Sequential(
(0): Conv2d(320, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(2): ReLU(inplace=True)
(3): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(4): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(5): ReLU(inplace=True)
)'''
Step4: Up2(output') -> final = (bs, 1, 200, 200) # 第二个维度的1就代表BEV每个网格下的二分类结果
'''Up2的配置如下
Sequential(
(0): Upsample(scale_factor=2.0, mode=bilinear)
(1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(3): ReLU(inplace=True)
(4): Conv2d(128, 1, kernel_size=(1, 1), stride=(1, 1))
)'''最后就是将输出的语义分割结果与binimgs的真值标注做基于像素的交叉熵损失,从而指导模型的学习过程。以上就是LSS算法的整体实现流程,如有错误,欢迎大家在评论区评论指正!:-)编辑于 2022-12-04 23:17・IP 属地黑龙江自动驾驶自动驾驶技术赞同 14425 条评论分享喜欢收藏申请转载文章被以下专栏收录自动驾驶学习笔记记录自己在自动驾驶方向的学
BEV感知经典LSS(Lift-Splat-Shoot)论文注释代码,三十分钟全搞定_bev lss 论文-CSDN博客
>BEV感知经典LSS(Lift-Splat-Shoot)论文注释代码,三十分钟全搞定_bev lss 论文-CSDN博客
BEV感知经典LSS(Lift-Splat-Shoot)论文注释代码,三十分钟全搞定
MinyounZhang
已于 2023-11-26 22:38:45 修改
阅读量1.1k
收藏
28
点赞数
30
文章标签:
深度学习
自动驾驶
计算机视觉
神经网络
于 2023-11-24 11:27:25 首次发布
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/MinyounZhang/article/details/134582017
版权
家人们大家好,我是老张。今天分享一波自动驾驶BEV(Bird-eye view,鸟瞰图视角)感知范式的 开创性工作,NVIDIA提出的LSS(Lift-Splat-Shoot)。尽管时至今日,构建BEV特征的方式逐渐丰富;显式构建BEV特征也不再是必须,但阅读和思考本文仍然会有很多收获。
我的分享包括代码和中文注释,没有太多文字描述与公式,争取三十分钟人人都能看明白,现在发车。论文和官方Repo链接:
arxiv.org/pdf/2008.05711.pdf nv-tlabs/lift-splat-shoot: Lift, Splat, https://github.com/nv-tlabs/lift-splat-shoot
LSS提出了一种将多视角的相机图像融合在BEV空间下的编码方法,其中:
Lift是通过预测深度信息,将2D图像编码到3D空间;Splat是将3D特征的高度拍扁为BEV特征;Shoot是运动规划。
在本工作中,BEV特征空间是以自车为中心的栅格,X轴Y轴最远检测距离各50米,Z轴各10米。这个100 * 100 * 20的空间就是BEV特征需要表征的,栅格内每个位置是空间中该位置的特征。
xbound=[-50.0, 50.0, 0.5], # x方向网格
ybound=[-50.0, 50.0, 0.5], # y方向网格
zbound=[-10.0, 10.0, 20.0], # z方向网格
dbound=[4.0, 45.0, 1.0], # 深度方向网格
将2D图像编码到BEV视角下,需要模型有优质的深度信息,距离预测的准物体在BEV特征中的位置才会准。关于如何把环视摄像机的图片编码到BEV空间下,LSS采用最直接的方式:显式的预测深度信息;另一范式如BEVFormer等则使用Transformer结构,隐式的通过BEV特征空间的query查询2D图像中的特征。
来看模型流水线图,其实Extrinsics+Splat,完整流水线可以分为5部分:
1.生成视锥点云,代表特征图中元素的相机坐标 2.根据相机内外参数、数据增强参数,将视锥点云从相机坐标映射到世界坐标 3.CamEncode网络提取2D图片的深度特征矩阵 4.⭐深度特征绑定视锥点云,Voxel Pooling为BEV特征 5.BEV特征编码,输出
一、2D图片 - > 深度特征
我们先从最容易理解的【3】开始说起,不考虑batch和摄像头数量:一张2D图片的张量维度为【3, W, H】;经过特征提取网络,输出张量为【41+64, Wf, Hf】,Wf和Hf是下采样后特征图的高宽。
输出通道数量105是个特殊值,这里前41个通道表示深度取值范围从4米到45米,后64的通道存放当前点的特征。通道拆分做内积得到64 * 41的矩阵,表示该特征点分别在4 ~ 45米每个深度下的特征值。所以模型最终输出张量维度为【 64, 41, Wf, Hf】,对应代码20 ~21行
下方是论文给的图,把矩阵放到图左边或许会更直观些;本段代码实现特征提取+输出深度特征矩阵,不想看也没关系
class CamEncode(nn.Module): # 提取图像特征,进行图像深度编码
def __init__(self, D, C, downsample):
super(CamEncode, self).__init__()
self.D = D # 41 深度区间【4-45】
self.C = C # 64 点的特征向量维度
self.trunk = EfficientNet.from_pretrained("efficientnet-b0") # efficientnet 提取特征
self.up1 = Up(320+112, 512) # 上采样模块,输入320+112(多尺度融合),输出通道512
self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0) # 1x1卷积调整通道数
def get_depth_dist(self, x, eps=1e-20): # 深度维计算softmax,得到每个像素不同深度的概率
return x.softmax(dim=1)
def get_depth_feat(self, x):
x = self.get_eff_depth(x) # 使用efficientnet提取特征 x: BN x 512 x 8 x 22
x = self.depthnet(x) # 1x1卷积变换维度 x: BN x 105(C+D) x 8 x 22
depth = self.get_depth_dist(x[:, :self.D]) # 第二个维度的前D个作为深度维,进行softmax depth: BN x 41 x 8 x 22
new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2) # 将深度概率分布和特征通道利用广播机制相乘
return depth, new_x # new_x: BN x 64 x 41 x 8 x 22
def get_eff_depth(self, x): # 使用efficientnet提取特征
# adapted from https://github.com/lukemelas/EfficientNet-PyTorch/blob/master/efficientnet_pytorch/model.py#L231
endpoints = dict()
# Stem
x = self.trunk._swish(self.trunk._bn0(self.trunk._conv_stem(x))) # x: BN x 32 x 64 x 176
prev_x = x
# Blocks
for idx, block in enumerate(self.trunk._blocks):
drop_connect_rate = self.trunk._global_params.drop_connect_rate
if drop_connect_rate:
drop_connect_rate *= float(idx) / len(self.trunk._blocks) # scale drop connect_rate
x = block(x, drop_connect_rate=drop_connect_rate)
if prev_x.size(2) > x.size(2):
endpoints['reduction_{}'.format(len(endpoints)+1)] = prev_x
prev_x = x
# Head
endpoints['reduction_{}'.format(len(endpoints)+1)] = x # x: BN x 320 x 4 x 11
x = self.up1(endpoints['reduction_5'], endpoints['reduction_4']) # 对endpoints[4]上采样,然后和endpoints[5] concat 在一起
return x # x: 24 x 512 x 8 x 22
def forward(self, x):
depth, x = self.get_depth_feat(x) # depth: B*N x D x fH x fW(24 x 41 x 8 x 22) x: B*N x C x D x fH x fW(24 x 64 x 41 x 8 x 22)
return x
二、相机坐标->世界坐标的映射关系
然后要回到【1】生成视锥点云。我们得到了单张2D图片的深度特征矩阵。这些特征如何映射到世界坐标系下,和BEV视角的栅格关联上呢?第一步,先建立特征图在世界坐标系下坐标位置的视锥点云,也就是深度特征每个点的在相机坐标系的位置。 CamEncode网络输出维度是【 64, 41, fW, fH】,表示高宽深分别为【fH,fW,41】的立方体中,每个位置特征64维;视锥点云维度【41 x fH x fW x 3】中,3代表XYZ三条坐标轴,因此视锥点云存储立方体每个位置在相机坐标系的XYZ坐标。有些类似位置编码对吧。
def create_frustum(self):
# make grid in image plane
ogfH, ogfW = self.data_aug_conf['final_dim']
fH, fW = ogfH // self.downsample, ogfW // self.downsample # fw和fh是特征图长宽
ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)
D, _, _ = ds.shape # D是深度区间41,【4-45】
#【0, fW, 2fW, 3fW ..., ogfW-2fW, ogfW-fW, ogfW】
xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)
ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)
# D x fH x fW x 3
frustum = torch.stack((xs, ys, ds), -1)
return nn.Parameter(frustum, requires_grad=False) # 不计算梯度
步骤【2】坐标系转换。这些特征点的在相机坐标系的位置,需要通过相机内外参数映射到世界坐标系下。代码中的映射涉及仿射变化和相机参数,如果没接触过,跳过这部分也不会影响理解论文。注意有6张不同方向的环视摄像头图片,所以内外参是6部相机的内外参数组成。
def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
"""
rots:相机外参旋转, trans:相机外参平移, intrins:相机内参, post_rots:数据增强旋转, post_trans:数据增强平移
Determine the (x,y,z) locations (in the ego frame) of the points in the point cloud.
Returns B x N x D x H/downsample x W/downsample x 3
"""
B, N, _ = trans.shape
# undo post-transformation
# B x N x D x H x W x 3,先还原数据增强中旋转和平移对坐标的影响
points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))
# cam_to_ego,利用相机内外参
points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
points[:, :, :, :, :, 2:3]
), 5)
combine = rots.matmul(torch.inverse(intrins))
points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
points += trans.view(B, N, 1, 1, 1, 3)
return points #维度不变,坐标值从相机坐标系->世界坐标系
三、BEV池化
至此,我们将6个环视摄像头拍摄的图片,通过显式的计算41个深度下的特征,将这【6,41 ,fW, fH】个特征点一一放置在了早先准备好的BEV特征空间下。终于可以到达【4】,构建BEV特征。
在这张像棋盘一样的BEV特征空间中,有位置放了多个特征点,有的位置没放特征点,因此需要【4】在每个栅格进行BEV池化,得到尺寸统一的BEV特征。
官方代码的实现是:
先根据特征点的XYZ坐标和batch,计算每个点的索引值;索引值相同的点位于同一个栅格中;【代码26-30】根据索引值排序,则索引值变为有序数组,形如【1.2,1.4,2,4,4,5 … 】;只需“遍历”索引值,将相同索引值的位置求和,完成池化,形如【1.2+1.4,2,4+4,5 … 】
下方voxel_pooling函数准备索引,BEV池化在QuickCumsum类中进行;
def voxel_pooling(self, geom_feats, x):
# geom_feats: B x N x D x H x W x 3 (4 x 6 x 41 x 8 x 22 x 3)
# x: B x N x D x fH x fW x C(4 x 6 x 41 x 8 x 22 x 64)
B, N, D, H, W, C = x.shape # B: 4 N: 6 D: 41 H: 8 W: 22 C: 64
Nprime = B*N*D*H*W # Nprime: 173184
# flatten x
x = x.reshape(Nprime, C) # 将图像展平,一共有 B*N*D*H*W 个点
# flatten indices
geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() # 将[-50,50] [-10 10]的范围平移到[0,100] [0,20],计算栅格坐标并取整
geom_feats = geom_feats.view(Nprime, 3) # 将像素映射关系同样展平 geom_feats: B*N*D*H*W x 3 (173184 x 3)
batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
device=x.device, dtype=torch.long) for ix in range(B)]) # 每个点对应于哪个batch
geom_feats = torch.cat((geom_feats, batch_ix), 1) # geom_feats: B*N*D*H*W x 4(173184 x 4), geom_feats[:,3]表示batch_id
# filter out points that are outside box
# 过滤掉在边界线之外的点 x:0~199 y: 0~199 z: 0
kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
& (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
& (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
x = x[kept] # x: 168648 x 64
geom_feats = geom_feats[kept]
# get tensors from the same voxel next to each other
ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
+ geom_feats[:, 1] * (self.nx[2] * B)\
+ geom_feats[:, 2] * B\
+ geom_feats[:, 3] # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
sorts = ranks.argsort()
x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts] # 按照rank排序,这样rank相近的点就在一起了
# x: 168648 x 64 geom_feats: 168648 x 4 ranks: 168648
# cumsum trick
if not self.use_quickcumsum:
x, geom_feats = cumsum_trick(x, geom_feats, ranks)
else:
x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks) # 一个batch的一个格子里只留一个点 x: 29072 x 64 geom_feats: 29072 x 4
# griddify (B x C x Z x X x Y)
final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device) # final: 4 x 64 x 1 x 200 x 200
final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # 将x按照栅格坐标放到final中
# collapse Z
final = torch.cat(final.unbind(dim=2), 1) # 消除掉z维
return final # final: 4 x 64 x 200 x 200
torch.autograd.Function是自定义的算子,路径已经变了,能在python层面自定义算子的前向和后向过程。代码中前向过程使用前缀和区间求和;向量错位比较前后元素是否一致,这些计算步骤都使计算过程向量化;前向过程存储了保留下特征的位置,反向过程再使用前缀和技巧,将特征保留位置的梯度分给求和的位置
class QuickCumsum(torch.autograd.Function):
@staticmethod
def forward(ctx, x, geom_feats, ranks):
# x: 168648 x 64 geom_feats: 168648 x 4 ranks: 168648 x
x = x.cumsum(0) # 求前缀和 x: 168648 x 64
kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool) # kept: 168648 x
kept[:-1] = (ranks[1:] != ranks[:-1]) # rank错位比较,rank[0]!=rank[1],则留下rank[1]
x, geom_feats = x[kept], geom_feats[kept] # rank值相等的点只留下最后一个,即一个batch中的一个格子里只留最后一个点 x: 29072 geom_feats: 29072 x 4
x = torch.cat((x[:1], x[1:] - x[:-1])) # x错位相减,还原前缀和之前的x,此时点的feature是rank相同点之和,相当于把同一个格子的点特征进行了sum
# save kept for backward
ctx.save_for_backward(kept)
# no gradient for geom_feats
ctx.mark_non_differentiable(geom_feats)
return x, geom_feats
@staticmethod
def backward(ctx, gradx, gradgeom):
kept, = ctx.saved_tensors
back = torch.cumsum(kept, 0)
back[kept] -= 1
val = gradx[back]
return val, None, None
以上是本文主要的创新点。当然鉴于完整性,也奉上BEV特征编码部分代码,总而言之用卷积堆了些计算量,并做了多尺度融合。在这个位置堆计算量是很必要的,因为在此之前所有特征的计算是相机之间独立的,现在所有特征都统一到BEV视角,当然要利用好相机之间特征的语义信息,把BEV特征做大做强。
四、BEV空间下特征再编码
class BevEncode(nn.Module):
def __init__(self, inC, outC): # inC: 64 outC: 1
super(BevEncode, self).__init__()
# 使用resnet的前3个stage作为backbone
trunk = resnet18(pretrained=False, zero_init_residual=True)
self.conv1 = nn.Conv2d(inC, 64, kernel_size=7, stride=2, padding=3,
bias=False)
self.bn1 = trunk.bn1
self.relu = trunk.relu
self.layer1 = trunk.layer1
self.layer2 = trunk.layer2
self.layer3 = trunk.layer3
self.up1 = Up(64+256, 256, scale_factor=4)
self.up2 = nn.Sequential( # 2倍上采样->3x3卷积->1x1卷积
nn.Upsample(scale_factor=2, mode='bilinear',
align_corners=True),
nn.Conv2d(256, 128, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, outC, kernel_size=1, padding=0),
)
def forward(self, x): # x: 4 x 64 x 200 x 200
x = self.conv1(x) # x: 4 x 64 x 100 x 100
x = self.bn1(x)
x = self.relu(x)
x1 = self.layer1(x) # x1: 4 x 64 x 100 x 100
x = self.layer2(x1) # x: 4 x 128 x 50 x 50
x = self.layer3(x) # x: 4 x 256 x 25 x 25
x = self.up1(x, x1) # 给x进行4倍上采样然后和x1 concat 在一起 x: 4 x 256 x 100 x 100
x = self.up2(x) # 2倍上采样->3x3卷积->1x1卷积 x: 4 x 1 x 200 x 200
return x
**训练时,模型输入6路环视摄像头图片和相机内外参数;输出在BEV视角下的特征图,标签同样是BEV视角下网格的类别真值,按照网格逐个计算交叉熵;**至于数据增强等技巧,就感兴趣自行查阅代码吧
不得不说,阅读这样从方法到代码都很干净的工作真好。好了家人们,今天就到这吧,我是老张,我们有缘下次见!
优惠劵
MinyounZhang
关注
关注
30
点赞
踩
28
收藏
觉得还不错?
一键收藏
知道了
0
评论
BEV感知经典LSS(Lift-Splat-Shoot)论文注释代码,三十分钟全搞定
自动驾驶BEV感知工作,LSS(Lift-Splat-Shoot)论文+注释代码30分钟全面讲解
复制链接
扫一扫
参与评论
您还未登录,请先
登录
后发表或查看评论
Lift-Splat-Shoot算法理解及代码中文注释
qq_37497304的博客
09-09
5035
对算法Lift-Splat-Shoot的理解以及源代码中文注释
3D 边界框注释工具(3D-BAT) 点云和图像标注_JavaScript_代码_下载
06-14
3D 边界框注释工具 (3D BAT)
3D 边界框注释工具
3D 边界框标注说明
观看原始视频(10 秒)以熟悉序列并了解插值的意义所在
观看教程视频以熟悉(平移/缩放/旋转对象、插值以及如何使用辅助视图)
启动 WhatPulse。使用koyunujiju@braun4email.com和密码登录:labeluser
在鸟瞰图 (BEV) 中绘制边界框
使用 3D 箭头(拖放)或滑块在 BEV 中移动/缩放它
选择 5 类(汽车、行人、骑自行车者、摩托车、卡车)中的一种
必要时进行插值
通过单击边界框选择要插入的对象
在菜单中激活“插值模式”(复选框)-> 开始位置将被保存
通过跳过 x 帧移动到所需的帧
将对象平移到新位置
单击菜单中的“插值”按钮
对序列中的所有对象重复步骤 4-7
将标签下载到您的计算机(JSON 文件)
标记完成后停止时间。
制作键盘鼠标热图截图,记录点击次数和击键次数
深度学习bev感知算法概述
09-21
bev感知
AVP-SLAM-SIM:模拟中AVP-SLAM(IROS 2020)的基本实现(不是官方代码)。 https
05-25
AVP-SLAM-SIM
基本实现仿真 。
尊重AVP-SLAM项目->童琴,陈同庆,陈以伦和苏青
AVP-SLAM-SIM
基本实现仿真! ··
目录
关于该项目
代码结构
怎么跑
怎么跑
路线图
贡献
执照
接触
致谢
关于该项目
这个项目只是我对Paper的实现,而不是正式发布,我们仅发布我们的仿真代码。 Other Code will be released soon
代码结构
我们发布了基本的代码结构,对于整个项目,您至少需要calib , segmentation , avp-bev , sync part等。avp-bev是该项目的核心部分之一,该结构如图所示:
如果您对此项目感兴趣,则可以遵循***.h文件来关联您的实现。
怎么跑
这个项目提供了一个凉亭世界。 因此,如果您想测试代码,则需要准备仿真世界。
该项目需要一个凉亭环境,通常加载凉亭模型需要很长时
【BEV感知 LSS方案】Lift-Splat-Shoot 论文精读与代码实现
黎国溥
12-01
264
LSS全称是Lift-Splat-Shoot,它先从车辆周围的多个摄像头拍摄到的图像进行特征提取,在特征图中估计出每个点的深度然后把这些点“提升”到3D空间中。接着,这些3D信息被放置到一个网格上最后将这些信息“拍扁”到一个平面视图上,形成BEV特征图。Lift,是提升的意思,2D → 3D特征转换模块,将二维图像特征生成3D特征,涉及到深度估计。Splat,是展开的意思,3D → BEV特征编码模块,把3D特征“拍扁”得到BEV特征图。Shooting,是指在BEV特征图上进行相关任务操作。
【BEV感知 EA-LSS 方案】Edge-aware Lift-splat-shot
黎国溥
12-11
244
本文分享LSS方案的改进方案——EA-LSS,它解决了“深度跳变”问题,提出了一个新框架Edge-aware Lift-splat-shot。适用于“多视图转BEV”,可以代替原来的LSS模块,并有效地提高了检测精度,而推理时间的增加很少。在nuScenes测试集上验证,纯相机模型或多模态模型,EA-LSS都是有效的,并达到了Top1排名(截至本文时间2023-12)。
fe20tp2_bev_2-main-源码.rar
10-10
fe20tp2_bev_2-main-源码.rar
鱼眼相机bev-carla测试图
02-28
鱼眼相机bev-carla测试图
ISO_TS_7637-4:2020_Part 4:Electrical transient conduction
09-05
标准内容高清可复制,非扫描版。
Part 4: Electrical transient conduction along shielded high voltage supply lines only
道路车辆—传导和耦合引起的电气干扰—第 4 部分:仅沿屏蔽高压电源线的电气瞬态传导
本文件规定了测试方法和程序,以确保安装在装有电压高于 60 V dc 的电气系统的乘用车和商用车辆上的设备的屏蔽高压电源线上的传导电气瞬变的兼容性。 并且低于 1 500 V d.c. 以及与车身隔离的电源。 它描述了瞬态注入和测量的台架测试。 它适用于所有类型的电动独立驱动的道路车辆(例如纯电动汽车(BEV)或混合动力汽车(HEV)、插电式混合动力汽车(PHEV))。
st-bev定价演示
02-12
st-bev定价演示
使用V2G技术的电动汽车充电拓扑,其影响和智能电网运营的评论-研究论文
05-19
电动汽车正在Swift发展成为内燃机汽车的完美替代品,从而减少了运输部门对石油和天然气等自然资源的高度依赖。 BEV和PHEV可以为防止环境恶化,降低排放率和降低驾驶成本提供最佳解决方案。 电动汽车采用率的上升趋势是对电动汽车动力驱动,电池和充电器技术发展的新技术经济进步的满足。 随着智能电网情景的增长趋势,电动汽车可以通过引入“车辆到电网”技术来平衡电网参数,这也促进了可再生能源的电网整合。 这项研究对以下三个主要的电动汽车研究领域进行了综述:电动汽车充电拓扑,电动汽车的各种影响以及使用“车辆到电网”(V2G)技术的智能电网运行。 这项研究还着眼于当前问题,电动汽车的大规模部署面临的挑战以及该领域的未来研究方向。
lss
02-14
lss
avp-bev-open:一个pkg在视图图像(4-6个相机)周围四处划动以生成鸟瞰图
05-26
AEV-电动汽车 一个pkg在视图图像(4-6个相机)周围四处划动,以产生鸟瞰图! ·· 目录 关于该项目 红色->相机看不到的盲区 蓝色->车身区域 环视系统是驾驶员监视驾驶环境的重要信息介质。 典型的环视系统由围绕...
浅谈人工智能之深度学习
IT深耕十余载,大道之简
03-01
999
在有监督学习中,训练数据有特征和标记,通过学习找到特征和标记之间的映射关系,并不断调整网络参数以减小预测误差。在无监督学习中,训练数据只有特征,没有标记,网络需要自行学习数据的内在规律和结构。在半监督学习中,训练数据部分有标记,部分无标记,网络需要同时利用有标记和无标记的数据进行学习。深度学习的基本特点是从大量的未标记或半标记的数据中,通过分层的特征变换,学习数据的表示和特征,从而实现对复杂函数的逼近和分类等任务。在学习过程中,需要定期总结所学内容,反思自己的不足和需要改进的地方,及时调整学习计划和策略。
PyTorch:深度学习的革命性框架
最新发布
Songxianshengbei的博客
03-06
525
在PyTorch中,可以使用循环和条件语句来控制训练、验证和测试的过程,并使用适当的损失函数和优化器来更新模型参数。PyTorch不仅简化了深度学习的复杂流程,还提供了丰富的功能和强大的扩展性,使得深度学习的应用更加广泛和深入。同时,它还支持Python编程语言的语法和特性,使得代码的编写更加简洁和高效。然后,在模型的训练过程中,将模型的输出和真实标签作为输入传递给损失函数,计算出损失值。更强大的生态系统:随着PyTorch生态系统的不断壮大和完善,我们可以期待更多优秀的开源项目、库和工具的涌现。
深度学习中常见的backbone、neck、head的理解
qq_60498436的博客
02-29
315
Neck(颈部):颈部位于骨干网络和头部之间,负责对骨干网络提取的特征进行进一步的处理和整合。颈部的作用类似于连接骨干网络和头部的桥梁,可以帮助将特征更好地传递给头部进行最终的预测或分类任务。Backbone(骨干网络):骨干网络通常是指整个深度神经网络的主要部分,负责提取输入数据的特征。头部通常由全连接层或卷积层组成,用于将颈部提取的特征映射到最终的输出空间,生成网络的最终预测结果。在不同的任务中,头部的结构会有所不同,以适应不同的任务需求。
深度学习需要掌握哪些数学基础?
人邮异步社区
03-03
374
这些深度神经网络可能有数千万甚至上亿的参数需要学习,而且即便是精通算法的研 究员,也需要在有充足训练样本的情况下,通过精细化的调参才能实现有效优化,达到对数据的良好表征。另外,本书的示例是以Python 代码而不是严格理论证 明的形式展开的,这使得它们特别适合深度学习的从业者(特别是初学者)使用,尤其是那些 希望通过学习底层数学知识来更好地了解深度学习原理,从而改进训练算法和模型的朋友。本书内容安排有序,首先介绍基础理论,然后引出更高级的数学内容,最后用实际的深度 学习算法让你将之前掌握的内容融会贯通。
BEV(Bird’s-eye-view)三部曲
07-27
BEV(Bird's-eye-view)三部曲是一种视角技术,用于获取和显示场景的鸟瞰图。它包括以下三个步骤:
1. 数据采集:使用多个摄像头或传感器来获取场景的全景图像或数据。这些摄像头或传感器通常位于车辆的顶部或周围,以获得鸟瞰视角。
2. 数据处理:通过将来自不同摄像头或传感器的数据进行配准和融合,生成完整的鸟瞰图像或数据。这些数据可以包括车辆周围的道路、交通标志、行人和其他车辆等信息。
3. 数据显示:将处理后的鸟瞰图像或数据以可视化的方式呈现给驾驶员或其他系统。这可以通过车辆内的显示屏或其他交互界面来实现,帮助驾驶员更好地了解周围环境并做出决策。
BEV三部曲在自动驾驶和智能交通系统中具有重要的应用,可以提供全局视角和更全面的环境感知能力。它可以帮助驾驶员避免盲区,增强安全性,并提供更好的导航和路径规划功能。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
MinyounZhang
CSDN认证博客专家
CSDN认证企业博客
码龄7年
暂无认证
4
原创
20万+
周排名
11万+
总排名
8114
访问
等级
116
积分
32
粉丝
41
获赞
0
评论
55
收藏
私信
关注
热门文章
训练集与测试集分布差距
2985
BEV感知经典LSS(Lift-Splat-Shoot)论文注释代码,三十分钟全搞定
1105
MapTR自动驾驶感知端到端建图方案,三十分钟全搞定
946
特征集的贝叶斯误差及贝叶斯最优化分类器
753
分类专栏
人工智能算法面试
ML基础
2篇
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
MapTR自动驾驶感知端到端建图方案,三十分钟全搞定
训练集与测试集分布差距
特征集的贝叶斯误差及贝叶斯最优化分类器
2023年2篇
2021年2篇
目录
目录
分类专栏
人工智能算法面试
ML基础
2篇
目录
评论
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值
【LSS: Lift, Splat, Shoot】代码的复现与详细解读_lss代码复现-CSDN博客
>【LSS: Lift, Splat, Shoot】代码的复现与详细解读_lss代码复现-CSDN博客
【LSS: Lift, Splat, Shoot】代码的复现与详细解读
最新推荐文章于 2024-03-07 00:01:08 发布
zyw2002
最新推荐文章于 2024-03-07 00:01:08 发布
阅读量6.4k
收藏
107
点赞数
38
分类专栏:
# 3d目标检测
文章标签:
目标检测
LSS
BEV
自动驾驶
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zyw2002/article/details/128319169
版权
3d目标检测
专栏收录该内容
22 篇文章
25 订阅
订阅专栏
文章目录
一、代码复现1.1 环境搭建1.2 数据集下载1.3 Evaluate a model1.4 Visualize Predictions1.5 Visualize Input/Output Data1.6 Train a model
二、代码理解main.pyexplore.pydata.pymodels.pytools.pytrain.py
原论文:https://arxiv.org/pdf/2008.05711v1.pdf 论文解读:论文精读《LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting》 代码: https://github.com/nv-tlabs/lift-splat-shoot
一、代码复现
1.1 环境搭建
使用ubuntu从零配置环境参考:此文
使用anaconda创建虚拟环境
conda create -n lssEnv python=3.8
conda activate lssEnv
安装torch 先从官网上下载轮子,然后直接安装
pip install torch-1.9.0+cu102-cp38-cp38-linux_x86_64.whl
pip install torchvision-0.10.0+cu102-cp38-cp38-linux_x86_64.whl
安装工具
pip install nuscenes-devkit tensorboardX efficientnet_pytorch==0.7.0
安装tensorflow (方便在训练过程中使用TensorBoard)
pip install tensorflow-gpu==2.2.0
1.2 数据集下载
NuSences 数据集解析以及 nuScenes devkit 的使用
在官网上下载mini版本的数据集(Nuscenes的官网下载链接 )
解压后有四个文件夹: maps、samples、sweeps、v1.0-mini, 并将根目录下的v1.0-mini改成mini 下载最新的Map expansion 解压到maps文件下
1.3 Evaluate a model
下载项目文件
git clone https://github.com/nv-tlabs/lift-splat-shoot.git
下载权重文件
wget https://github.com/lukemelas/EfficientNet-PyTorch/releases/download/1.0/efficientnet-b0-355c32eb.pth
运行 main.py 文件中的eval_model_iou 对模型进行评估。 其中,因为我们采用的是mini 版本的 nuScenes,所以 采用mini参数。反之,如果我们采用的是Trianval 版本的 nuScenes,则采用Trianval参数。 modelf 选择刚才下载的权重文件放置的路径 dataroot 选择我们下载mini数据集的路径 gpuid 如果是默认一块则为0
python main.py eval_model_iou mini --modelf=./efficientnet-b0-355c32eb.pth --dataroot=../dataset/nuScenes --gpuid=0
这时会报错 : 解决方案 把explore.py文件下的第239行中,选择不加载模型状态
model.load_state_dict(torch.load(modelf), False)
然后,运行成功
1.4 Visualize Predictions
运行 main.py 文件中的viz_model_preds 对预测结果进行可视化。
python main.py viz_model_preds mini --modelf=./efficientnet-b0-355c32eb.pth --dataroot=../dataset/nuScenes --map_folder=../dataset/nuScenes/mini --gpuid=0
可视化结果
1.5 Visualize Input/Output Data
运行lidar_check, 检查以确保正确地解析了extrinsics/intrinsics
python main.py lidar_check mini --dataroot=../dataset/nuScenes --viz_train=False
可视化结果
1.6 Train a model
在项目文件夹下新建一个runs的目录,用来存放训练时的日志信息。 执行下面的命令开始训练
python main.py train mini --dataroot=../dataset/nuScenes --logdir=./runs --gpuid=0
tensorboard --logdir=./runs --bind_all
在服务器上打开tensorboard。 其中log_dir 是网络训练时自己指定的日志目录, 比如: ./runs
tensorboard --logdir=./runs --host=127.0.0.1
利用MobaXterm配置隧道 然后新建一个隧道,并进行配置。 1) 选择【本地端口转发】 2)【我的电脑】选择6006端口 3)【ssh服务器】和我们通过SSH连接远程服务器的设置是一样的,分别填写相应的IP地址,用户名,端口号(通常为22)即可 4)【远程服务器】远程服务器 填localhost , 远程端口填6006 然后启动隧道 在本地浏览器上输入127.0.0.1:6006, 可以看到tensorboard面板
二、代码理解
我们按照代码的执行逻辑来拆开理解。
main.py
main.py文件是函数的执行入口。 Fire (python Fire 的使用指南)通过使用字典格式,选择函数暴露给命令行。 当命令行参数传入eval_model_iou ,程序就开始执行src/explore.py文件下的eval_model_iou 函数。
if __name__ == '__main__':
Fire({
'lidar_check': src.explore.lidar_check,
'cumsum_check': src.explore.cumsum_check,
'train': src.train.train,
'eval_model_iou': src.explore.eval_model_iou,
'viz_model_preds': src.explore.viz_model_preds,
})
explore.py
我们来看看explore.py中的eval_model_iou函数。
函数参数: 先来看看这个函数需要传入哪些参数~
version, # 数据集版本: mini/trival
modelf, # 模型文件路径
dataroot='/data/nuscenes',# 数据集路径
gpuid=1,# gpu的序号
H=900, W=1600, # 图片的宽和高
resize_lim=(0.193, 0.225), # resize 的范围
final_dim=(128, 352), # 数据预处理后最终的图片大小
bot_pct_lim=(0.0, 0.22), # 裁剪图片时,图像底部裁掉部分所占的比例范围
rot_lim=(-5.4, 5.4), # 训练时旋转图片的角度范围
rand_flip=True, # 是否随机翻转
然后定义了两个字典grid_conf 和 data_aug_con
grid_conf = { # 网格配置
'xbound': xbound,
'ybound': ybound,
'zbound': zbound,
'dbound': dbound,
}
data_aug_conf = { # 数据增强配置
'resize_lim': resize_lim,
'final_dim': final_dim,
'rot_lim': rot_lim,
'H': H, 'W': W,
'rand_flip': rand_flip,
'bot_pct_lim': bot_pct_lim,
'cams': ['CAM_FRONT_LEFT', 'CAM_FRONT', 'CAM_FRONT_RIGHT',
'CAM_BACK_LEFT', 'CAM_BACK', 'CAM_BACK_RIGHT'],
'Ncams': 5, # 读取数据时读取的摄像机的数目-1
}
数据的加载、训练和评估:
调用data.py文件中的compile_data 生成训练集和验证集的数据加载器trainloader和valloader。
trainloader, valloader = compile_data(version, dataroot, data_aug_conf=data_aug_conf,
grid_conf=grid_conf, bsz=bsz, nworkers=nworkers,
parser_name='segmentationdata') # 测试集和验证集集的数据加载器
调用model.py文件中的compile_model 构造LSS模型
model = compile_model(grid_conf, data_aug_conf, outC=1) # 获取模型
把模型迁移到GPU上
device = torch.device('cpu') if gpuid < 0 else torch.device(f'cuda:{gpuid}') # 如果不能使用gpu(cuda),则使用cpu
model.to(device) # 把模型迁移到device设备上
使用在tool.py文件中定义SimpleLoss的计算损失, 然后开启评估模型,最后调用get_val_info 对模型进行评估
loss_fn = SimpleLoss(1.0).cuda(gpuid) # 计算损失
model.eval() # 开启评估模式
val_info = get_val_info(model, valloader, loss_fn, device) # 推理并打印输出loss和iou
完整的注释如下:
def eval_model_iou(version, # 数据集版本: mini/trival
modelf, # 模型文件路径
dataroot='/data/nuscenes',# 数据集路径
gpuid=1,# gpu的序号
H=900, W=1600, # 图片的宽和高
resize_lim=(0.193, 0.225), # resize 的范围
final_dim=(128, 352), # 数据预处理后最终的图片大小
bot_pct_lim=(0.0, 0.22), # 裁剪图片时,图像底部裁掉部分所占的比例范围
rot_lim=(-5.4, 5.4), # 训练时旋转图片的角度范围
rand_flip=True, # 是否随机翻转
# 分别显示x,y,z,d方向的范围并划分网格 [下边界,上边界,网格间距]
xbound=[-50.0, 50.0, 0.5],
ybound=[-50.0, 50.0, 0.5],
zbound=[-10.0, 10.0, 20.0],
dbound=[4.0, 45.0, 1.0],
bsz=4,# bachsize的大小
nworkers=10, # 线程数
):
grid_conf = { # 网格配置
'xbound': xbound,
'ybound': ybound,
'zbound': zbound,
'dbound': dbound,
}
data_aug_conf = { # 数据增强配置
'resize_lim': resize_lim,
'final_dim': final_dim,
'rot_lim': rot_lim,
'H': H, 'W': W,
'rand_flip': rand_flip,
'bot_pct_lim': bot_pct_lim,
'cams': ['CAM_FRONT_LEFT', 'CAM_FRONT', 'CAM_FRONT_RIGHT',
'CAM_BACK_LEFT', 'CAM_BACK', 'CAM_BACK_RIGHT'],
'Ncams': 5, # 读取数据时读取的摄像机的数目-1
}
trainloader, valloader = compile_data(version, dataroot, data_aug_conf=data_aug_conf,
grid_conf=grid_conf, bsz=bsz, nworkers=nworkers,
parser_name='segmentationdata') # 测试集和验证集集的数据加载器
device = torch.device('cpu') if gpuid < 0 else torch.device(f'cuda:{gpuid}') # 如果不能使用gpu(cuda),则使用cpu
model = compile_model(grid_conf, data_aug_conf, outC=1) # 获取模型
print('loading', modelf)
model.load_state_dict(torch.load(modelf),False) # 加载状态字典
model.to(device) # 把模型迁移到device设备上
loss_fn = SimpleLoss(1.0).cuda(gpuid) # 计算损失
model.eval() # 开启评估模式
val_info = get_val_info(model, valloader, loss_fn, device) # 推理并打印输出loss和iou
print(val_info)
data.py
explore.py 中调用了compile_data函数。
compile_data 函数
首先是调用nuscenes.nuscenes.NuScenes 库构建了一个nusc的数据集然后把nusc作为参数传入parser() 中构建数据解析器traindata和valdata 。其中parser 根据输入的参数parser_name有两种选择,一个是VizData,一个是SegmentationData (这两个都是继承自定义的NuscData的Dataset类,我们下面会详细介绍) 然后traindata和valdata 再把这两个参数传入torch.utils.data.DataLoader 构建了训练集和测试集的数据加载器,并返回。
def compile_data(version, dataroot, data_aug_conf, grid_conf, bsz,
nworkers, parser_name):
nusc = NuScenes(version='v1.0-{}'.format(version),
dataroot=os.path.join(dataroot, version),
verbose=False) # 加载ncscenes 数据
parser = {
'vizdata': VizData,
'segmentationdata': SegmentationData,
}[parser_name] # 根据传入的参数选择数据解析器
traindata = parser(nusc, is_train=True, data_aug_conf=data_aug_conf,
grid_conf=grid_conf) # 训练数据集
valdata = parser(nusc, is_train=False, data_aug_conf=data_aug_conf,
grid_conf=grid_conf) # 验证数据集
# 训练数据加载器
trainloader = torch.utils.data.DataLoader(traindata, batch_size=bsz,
shuffle=True,
num_workers=nworkers,
drop_last=True,
worker_init_fn=worker_rnd_init) # 给每个线程设置随机的种子
# 验证数据加载器
valloader = torch.utils.data.DataLoader(valdata, batch_size=bsz,
shuffle=False,
num_workers=nworkers)
return trainloader, valloader
worker_rnd_init 获取随机种子(被compile_data 中的Dataloader函数调用)
def worker_rnd_init(x):
np.random.seed(13 + x) # x是线程id,获取随机种子
NuscData 类
初始化
def __init__(self, nusc, is_train, data_aug_conf, grid_conf):
self.nusc = nusc
self.is_train = is_train # 是否为训练集
self.data_aug_conf = data_aug_conf # 数据增强配置
self.grid_conf = grid_conf # 网格配置
self.scenes = self.get_scenes() # 得到scene名字的列表list: [scene-0061, scene-0103,...]
self.ixes = self.prepro() # 得到属于self.scenes的所有sample
'''
xbound=[-50.0, 50.0, 0.5],
ybound=[-50.0, 50.0, 0.5],
zbound=[-10.0, 10.0, 20.0],
dbound=[4.0, 45.0, 1.0],
'''
dx, bx, nx = gen_dx_bx(grid_conf['xbound'], grid_conf['ybound'], grid_conf['zbound']) # toos.py文件下定义的函数,用来划分网格
self.dx, self.bx, self.nx = dx.numpy(), bx.numpy(), nx.numpy() # 转换成numpy
self.fix_nuscenes_formatting()
print(self)
fix_nuscenes_formatting() 调整ncscenes数据格式 (被类初始化函数调用)
def fix_nuscenes_formatting(self): # 调整ncscenes数据格式
"""If nuscenes is stored with trainval/1 trainval/2 ... structure, adjust the file paths
stored in the nuScenes object.
"""
# check if default file paths work
rec = self.ixes[0]
sampimg = self.nusc.get('sample_data', rec['data']['CAM_FRONT'])
imgname = os.path.join(self.nusc.dataroot, sampimg['filename'])
def find_name(f):
d, fi = os.path.split(f)
d, di = os.path.split(d)
d, d0 = os.path.split(d)
d, d1 = os.path.split(d)
d, d2 = os.path.split(d)
return di, fi, f'{d2}/{d1}/{d0}/{di}/{fi}'
# adjust the image paths if needed
if not os.path.isfile(imgname):
print('adjusting nuscenes file paths')
fs = glob(os.path.join(self.nusc.dataroot, 'samples/*/samples/CAM*/*.jpg'))
fs += glob(os.path.join(self.nusc.dataroot, 'samples/*/samples/LIDAR_TOP/*.pcd.bin'))
info = {}
for f in fs:
di, fi, fname = find_name(f)
info[f'samples/{di}/{fi}'] = fname
fs = glob(os.path.join(self.nusc.dataroot, 'sweeps/*/sweeps/LIDAR_TOP/*.pcd.bin'))
for f in fs:
di, fi, fname = find_name(f)
info[f'sweeps/{di}/{fi}'] = fname
for rec in self.nusc.sample_data:
if rec['channel'] == 'LIDAR_TOP' or (rec['is_key_frame'] and rec['channel'] in self.data_aug_conf['cams']):
rec['filename'] = info[rec['filename']]
get_scenes() 根据 self.nusc.version 场景分为训练集和验证集(被类初始化函数调用)
def get_scenes(self):
# filter by scene split
split = {
'v1.0-trainval': {True: 'train', False: 'val'},
'v1.0-mini': {True: 'mini_train', False: 'mini_val'},
}[self.nusc.version][self.is_train]
scenes = create_splits_scenes()[split] # 根据 self.nusc.version 场景分为训练集和验证集,得到的是场景名字的list: [scene-0061,scene-0103,...]
return scenes
prepro() 将self.scenes中的所有sample取出并依照 scene_token和timestamp排序 (被类初始化函数调用)
def prepro(self): # 将self.scenes中的所有sample取出并依照 scene_token和timestamp排序
samples = [samp for samp in self.nusc.sample]
# remove samples that aren't in this split
samples = [samp for samp in samples if
self.nusc.get('scene', samp['scene_token'])['name'] in self.scenes]
# sort by scene, timestamp (only to make chronological viz easier)
samples.sort(key=lambda x: (x['scene_token'], x['timestamp']))
return samples
get_image_data 得到图像数据以及各种参数信息(被 SegmentationData 类中的__getitem__函数调用)
def get_image_data(self, rec, cams): # rec: 取出的sample cams:选择的相机通道
imgs = [] # 图像数据
rots = [] # 相机坐标系到自车坐标系的旋转矩阵
trans = [] # 相机坐标系到自车坐标系的平移向量
intrins = [] # 相机内参
post_rots = [] # 数据增强的像素坐标旋转映射关系
post_trans = [] # 数据增强的像素坐标平移映射关系
for cam in cams:
samp = self.nusc.get('sample_data', rec['data'][cam]) # 根据相机通道选择对应的sample_data
imgname = os.path.join(self.nusc.dataroot, samp['filename']) # 图片路径
img = Image.open(imgname) # 读取图像 1600 x 900
post_rot = torch.eye(2)
post_tran = torch.zeros(2)
sens = self.nusc.get('calibrated_sensor', samp['calibrated_sensor_token']) # 相机record
intrin = torch.Tensor(sens['camera_intrinsic']) # 相机内参
rot = torch.Tensor(Quaternion(sens['rotation']).rotation_matrix) # 相机坐标系相对于ego坐标系的旋转矩阵
tran = torch.Tensor(sens['translation']) # 相机坐标系相对于ego坐标系的平移矩阵
# augmentation (resize, crop, horizontal flip, rotate)
resize, resize_dims, crop, flip, rotate = self.sample_augmentation() # 获取数据增强的参数
img, post_rot2, post_tran2 = img_transform(img, post_rot, post_tran,
resize=resize,
resize_dims=resize_dims,
crop=crop,
flip=flip,
rotate=rotate,
) # 进行数据增强,并得到增强前后的像素点坐标的对应关系
# 为了方便,写成3维矩阵的格式
post_tran = torch.zeros(3)
post_rot = torch.eye(3)
post_tran[:2] = post_tran2
post_rot[:2, :2] = post_rot2
imgs.append(normalize_img(img)) # 标准化: ToTensor, Normalize 3,128,352
intrins.append(intrin)
rots.append(rot)
trans.append(tran)
post_rots.append(post_rot)
post_trans.append(post_tran)
return (torch.stack(imgs), torch.stack(rots), torch.stack(trans),
torch.stack(intrins), torch.stack(post_rots), torch.stack(post_trans))
get_lidar_data 获取雷达数据
def get_lidar_data(self, rec, nsweeps):
pts = get_lidar_data(self.nusc, rec,
nsweeps=nsweeps, min_distance=2.2)
return torch.Tensor(pts)[:3] # x,y,z
sample_augmentation() 对图片进行数据增强(被get_image_data()函数调用)
def sample_augmentation(self): # 数据增强
H, W = self.data_aug_conf['H'], self.data_aug_conf['W'] # 原始图片大小
fH, fW = self.data_aug_conf['final_dim'] # 数据增强后图片大小
if self.is_train: # 训练数据集增强
# 随机缩放图片大小
resize = np.random.uniform(*self.data_aug_conf['resize_lim'])
resize_dims = (int(W*resize), int(H*resize))
newW, newH = resize_dims
# 随机裁剪图片
crop_h = int((1 - np.random.uniform(*self.data_aug_conf['bot_pct_lim']))*newH) - fH
crop_w = int(np.random.uniform(0, max(0, newW - fW)))
crop = (crop_w, crop_h, crop_w + fW, crop_h + fH)
# 随机翻转图片
flip = False
if self.data_aug_conf['rand_flip'] and np.random.choice([0, 1]):
flip = True
# 随机旋转图片
rotate = np.random.uniform(*self.data_aug_conf['rot_lim'])
else: # 测试数据增强
# 缩小图片
resize = max(fH/H, fW/W)
resize_dims = (int(W*resize), int(H*resize))
newW, newH = resize_dims
# 裁剪图片
crop_h = int((1 - np.mean(self.data_aug_conf['bot_pct_lim']))*newH) - fH
crop_w = int(max(0, newW - fW) / 2)
crop = (crop_w, crop_h, crop_w + fW, crop_h + fH)
flip = False # 不翻转
rotate = 0 # 不旋转
return resize, resize_dims, crop, flip, rotate
get_binimg 得到自车坐标系相对于地图全局坐标系的位置 (被SegmentationData 中的__getitem__调用)
def get_binimg(self, rec): # 得到自车坐标系相对于地图全局坐标系的位置
egopose = self.nusc.get('ego_pose',
self.nusc.get('sample_data', rec['data']['LIDAR_TOP'])['ego_pose_token']) # 自车的位置
trans = -np.array(egopose['translation']) # 平移
rot = Quaternion(egopose['rotation']).inverse # 旋转
img = np.zeros((self.nx[0], self.nx[1]))
for tok in rec['anns']: # 遍历该sample的每个annotation token
inst = self.nusc.get('sample_annotation', tok) # 找到该annotation
# add category for lyft
if not inst['category_name'].split('.')[0] == 'vehicle': # 只关注车辆类别
continue
box = Box(inst['translation'], inst['size'], Quaternion(inst['rotation']))
box.translate(trans) # 将box的center坐标从全局坐标系转换到自车坐标系下
box.rotate(rot) # 将box的center坐标从全局坐标系转换到自车坐标系下
pts = box.bottom_corners()[:2].T # 三维边界框取底面的四个角的(x,y)值后转置, 4x2
pts = np.round(
(pts - self.bx[:2] + self.dx[:2]/2.) / self.dx[:2]
).astype(np.int32) # # 将box的实际坐标对应到网格坐标,同时将坐标范围[-50,50]平移到[0,100]
pts[:, [1, 0]] = pts[:, [0, 1]] # 把(x,y)的形式换成(y,x)的形式
cv2.fillPoly(img, [pts], 1.0) # 在网格中画出box
return torch.Tensor(img).unsqueeze(0) # 转化为Tensor 1x200x200
choose_cams 选择相机通道 (被SegmentationData 中的__getitem__调用)
def choose_cams(self): # 选择相机通道
if self.is_train and self.data_aug_conf['Ncams'] < len(self.data_aug_conf['cams']):
cams = np.random.choice(self.data_aug_conf['cams'], self.data_aug_conf['Ncams'],
replace=False) # 随机选择
else:
cams = self.data_aug_conf['cams'] # 选择全部的相机通道
return cams
SegmentationData类
SegmentationData 类的定义
class SegmentationData(NuscData): # SegmentationData类继承NuscData
def __init__(self, *args, **kwargs):
super(SegmentationData, self).__init__(*args, **kwargs)
def __getitem__(self, index):
rec = self.ixes[index] # 按照索引取出sample
cams = self.choose_cams() # 对于训练集且data_aug_conf中Ncams<6的,随机选择摄像机通道,否则选择全部相机通道
imgs, rots, trans, intrins, post_rots, post_trans = self.get_image_data(rec, cams) # 读取图像数据、相机参数和数据增强的像素坐标映射关系
binimg = self.get_binimg(rec)
return imgs, rots, trans, intrins, post_rots, post_trans, binimg
VizData类
class VizData(NuscData):
def __init__(self, *args, **kwargs):
super(VizData, self).__init__(*args, **kwargs)
def __getitem__(self, index):
rec = self.ixes[index]
cams = self.choose_cams()
imgs, rots, trans, intrins, post_rots, post_trans = self.get_image_data(rec, cams)
lidar_data = self.get_lidar_data(rec, nsweeps=3)
binimg = self.get_binimg(rec)
return imgs, rots, trans, intrins, post_rots, post_trans, lidar_data, binimg
models.py
compile_model函数 explore.py 中调用了compile_model函数。 该函数构造了LiftSplatShoot 模型
def compile_model(grid_conf, data_aug_conf, outC):
return LiftSplatShoot(grid_conf, data_aug_conf, outC)
Up类 上采样(被CamEncode类和BEVEncode类中的初始化函数调用)
class Up(nn.Module): # 上采样
def __init__(self, in_channels, out_channels, scale_factor=2):
super().__init__()
self.up = nn.Upsample(scale_factor=scale_factor, mode='bilinear',
align_corners=True) # 上采样 BxCxHxW->BxCx2Hx2W
self.conv = nn.Sequential( # 两个3x3卷积
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True)
)
def forward(self, x1, x2):
x1 = self.up(x1) # 对x1进行上采样
x1 = torch.cat([x2, x1], dim=1) # 将x1和x2 concat 在一起
return self.conv(x1)
CamEncode类 CamEncode类继承自nn.Module提取图像特征并编码(被LiftSplatShoot类中的初始化函数调用)
初始化
def __init__(self, D, C, downsample): # D: 41 C:64 downsample:16
super(CamEncode, self).__init__()
self.D = D # 深度上的网格数:41
self.C = C # 图像特征维度:64
# 使用 efficientnet 提取特征
self.trunk = EfficientNet.from_pretrained("efficientnet-b0")
# 上采样模块,输入输出通道分别为320+112和512
self.up1 = Up(320+112, 512)
# 1x1卷积,变换维度
self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0)
forward 返回带有深度信息的特征(调用get_depth_feat函数)
def forward(self, x):
'''
depth: B*N x D x fH x fW(24 x 41 x 8 x 22)
x: B*N x C x D x fH x fW(24 x 64 x 41 x 8 x 22)
'''
depth, x = self.get_depth_feat(x)
return x
get_depth_feat 提取带有深度的特征 (调用get_eff_depth提取特征,调用get_depth_dist把深度信息离散化)
def get_depth_feat(self, x): # 提取带有深度的特征
# 使用efficientnet提取特征 x: 24x512x8x22
x = self.get_eff_depth(x)
# Depth
# 1x1卷积变换维度 x: 24x105x8x22 =24x(C+D)xfHxfW
x = self.depthnet(x)
'''
第二个维度的前D个作为深度维(把连续的深度值离散化)
进行softmax depth: 24 x 41 x 8 x 22
'''
depth = self.get_depth_dist(x[:, :self.D])
'''
将特征通道维和通道维利用广播机制相乘
depth.unsqueeze(1) -> torch.Size([24, 1, 41, 8, 22])
x[:, self.D:(self.D + self.C)] -> torch.Size([24, 64, 8, 22])
x.unsqueeze(2)-> torch.Size([24, 64, 1, 8, 22])
depth*x-> new_x: torch.Size([24, 64, 41, 8, 22])
'''
new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2)
return depth, new_x
get_depth_dist 对深度维进行softmax,得到每个像素不同深度的概率
def get_depth_dist(self, x, eps=1e-20): # 对深度维进行softmax,得到每个像素不同深度的概率
return x.softmax(dim=1)
get_eff_depth 使用efficientnet提取特征
def get_eff_depth(self, x): # 使用efficientnet提取特征
# adapted from https://github.com/lukemelas/EfficientNet-PyTorch/blob/master/efficientnet_pytorch/model.py#L231
endpoints = dict()
# Stem
x = self.trunk._swish(self.trunk._bn0(self.trunk._conv_stem(x))) # x: 24 x 32 x 64 x 176
prev_x = x
# Blocks
for idx, block in enumerate(self.trunk._blocks):
drop_connect_rate = self.trunk._global_params.drop_connect_rate
if drop_connect_rate:
drop_connect_rate *= float(idx) / len(self.trunk._blocks) # scale drop connect_rate
x = block(x, drop_connect_rate=drop_connect_rate)
if prev_x.size(2) > x.size(2):
endpoints['reduction_{}'.format(len(endpoints)+1)] = prev_x
prev_x = x
# Head
# x: 24 x 320 x 4 x 11
endpoints['reduction_{}'.format(len(endpoints)+1)] = x
# 先对endpoints[4]进行上采样,然后将 endpoints[5]和endpoints[4] concat 在一起
x = self.up1(endpoints['reduction_5'], endpoints['reduction_4'])
return x
BevEncode 类 CamEncode类继承自nn.Module 对BEV视图的特征进行编码(被LiftSplatShoot类中的初始化函数调用)
def __init__(self, inC, outC):
super(BevEncode, self).__init__()
# 使用resnet的前3个stage作为backbone
trunk = resnet18(pretrained=False, zero_init_residual=True)
self.conv1 = nn.Conv2d(inC, 64, kernel_size=7, stride=2, padding=3,
bias=False)
self.bn1 = trunk.bn1
self.relu = trunk.relu
self.layer1 = trunk.layer1
self.layer2 = trunk.layer2
self.layer3 = trunk.layer3
self.up1 = Up(64+256, 256, scale_factor=4)
self.up2 = nn.Sequential( # 2倍上采样->3x3卷积->1x1卷积
nn.Upsample(scale_factor=2, mode='bilinear',
align_corners=True),
nn.Conv2d(256, 128, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, outC, kernel_size=1, padding=0),
)
return x
forword
def forward(self, x): # x: 4 x 64 x 200 x 200
x = self.conv1(x) # x: 4 x 64 x 100 x 100
x = self.bn1(x)
x = self.relu(x)
x1 = self.layer1(x) # x1: 4 x 64 x 100 x 100
x = self.layer2(x1) # x: 4 x 128 x 50 x 50
x = self.layer3(x) # x: 4 x 256 x 25 x 25
x = self.up1(x, x1) # 给x进行4倍上采样然后和x1 concat 在一起 x: 4 x 256 x 100 x 100
x = self.up2(x) # 2倍上采样->3x3卷积->1x1卷积 x: 4 x 1 x 200 x 200
return x
LiftSplatShoot类
LiftSplatShoot类继承自nn.Module
初始化
def __init__(self, grid_conf, data_aug_conf, outC): # outC=1
super(LiftSplatShoot, self).__init__()
self.grid_conf = grid_conf # 网格配置参数
self.data_aug_conf = data_aug_conf # 数据增强配置参数
dx, bx, nx = gen_dx_bx(self.grid_conf['xbound'],
self.grid_conf['ybound'],
self.grid_conf['zbound'],
) # 网格划分
self.dx = nn.Parameter(dx, requires_grad=False) # dx: x,y,z方向上的网格间距 [0.5,0.5,20]
self.bx = nn.Parameter(bx, requires_grad=False) # bx: 第一个网格的中心坐标 [-49.5,-49.5,0]
self.nx = nn.Parameter(nx, requires_grad=False) # nx: 分别为x, y, z三个方向上格子的数量 [200,200,1]
self.downsample = 16 # 下采样倍数
self.camC = 64 # 图像特征维度
self.frustum = self.create_frustum() # frustum: DxfHxfWx3(41x8x22x3)
self.D, _, _, _ = self.frustum.shape # D: 41
self.camencode = CamEncode(self.D, self.camC, self.downsample) # D: 41 C:64 downsample:16
self.bevencode = BevEncode(inC=self.camC, outC=outC)
# toggle using QuickCumsum vs. autograd
self.use_quickcumsum = True
forword 调用get_voxels把图像转换到BEV下,然后调用bevencode (初始化函数中定义,是BevEncode类的实例化)提取特征
def forward(self, x, rots, trans, intrins, post_rots, post_trans):
# x:[4,6,3,128,352]
# rots: [4,6,3,3]
# trans: [4,6,3]
# intrins: [4,6,3,3]
# post_rots: [4,6,3,3]
# post_trans: [4,6,3]
# 将图像转换到BEV下,x: B x C x 200 x 200 (4 x 64 x 200 x 200)
x = self.get_voxels(x, rots, trans, intrins, post_rots, post_trans)
# 用resnet18提取特征 x: 4 x 1 x 200 x 200
x = self.bevencode(x)
return x
get_voxels 先调用get_geometry把在相机坐标系(ego frame)下的坐标 (x,y,z) 转换成自车坐标系下的点云坐标;然后调用get_cam_feats提取单张图像特征,最后调用voxel_pooling 对体素特征进行汇聚。
def get_voxels(self, x, rots, trans, intrins, post_rots, post_trans):
# 像素坐标到自车中坐标的映射关系 geom: B x N x D x fH x fW x 3 (4 x 6 x 41 x 8 x 22 x 3)
geom = self.get_geometry(rots, trans, intrins, post_rots, post_trans)
# 提取图像特征并预测深度编码 x: B x N x D x fH x fW x C(4 x 6 x 41 x 8 x 22 x 64)
x = self.get_cam_feats(x)
# x: 4 x 64 x 200 x 200
x = self.voxel_pooling(geom, x)
return x
get_geometry 把在相机坐标系(ego frame)下的坐标 (x,y,z) 转换成自车坐标系下的点云坐标 (被get_voxels调用)
def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
""" 把在相机坐标系(ego frame)下的坐标 (x,y,z) 转换成自车坐标系下的点云坐标
返回 B x N x D x H/downsample x W/downsample x 3
"""
# B:4(batchsize) N: 6(相机数目)
B, N, _ = trans.shape
# undo post-transformation
# B x N x D x H x W x 3
# 抵消数据增强及预处理对像素的变化
points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))
# 相机坐标系转换成自车坐标系
points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
points[:, :, :, :, :, 2:3]
), 5) # 将像素坐标(u,v,d)变成齐次坐标(du,dv,d)
# d[u,v,1]^T=intrins*rots^(-1)*([x,y,z]^T-trans)
combine = rots.matmul(torch.inverse(intrins))
points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
points += trans.view(B, N, 1, 1, 1, 3) # 将像素坐标d[u,v,1]^T转换到车体坐标系下的[x,y,z]^T
return points # B x N x D x H x W x 3 (4 x 6 x 41 x 8 x 22 x 3)
get_cam_feats 调用camecode提取单张图像的特征 (被get_voxels调用)
def get_cam_feats(self, x):
"""
提取单张图像的特征
返回: B x N x D x H/downsample x W/downsample x C
"""
# B: 4 N: 6 C: 3 imH: 128 imW: 352
B, N, C, imH, imW = x.shape
# B和N两个维度合起来 x: 24 x 3 x 128 x 352
x = x.view(B*N, C, imH, imW)
# 进行图像编码 x: B*N x C x D x fH x fW (24 x 64 x 41 x 8 x 22)
x = self.camencode(x)
# 将前两维拆开 x: B x N x C x D x fH x fW(4 x 6 x 64 x 41 x 8 x 22)
x = x.view(B, N, self.camC, self.D, imH//self.downsample, imW//self.downsample)
# x: B x N x D x fH x fW x C(4 x 6 x 41 x 8 x 22 x 64)
x = x.permute(0, 1, 3, 4, 5, 2)
return x
voxel_pooling 对voxel进行池化操作,调用了tools.py文件中定义的quicksum (被get_voxels调用)
def voxel_pooling(self, geom_feats, x): # 对voxel进行池化操作
# geom_feats: B x N x D x fH x fW x 3 (4 x 6 x 41 x 8 x 22 x 3)
# x: B x N x D x fH x fW x C(4 x 6 x 41 x 8 x 22 x 64)
B, N, D, H, W, C = x.shape # B: 4 N: 6 D: 41 H: 8 W: 22 C: 64
Nprime = B*N*D*H*W # Nprime: 173184
# flatten x
x = x.reshape(Nprime, C) # 将图像展平,一共有 B*N*D*H*W 个点
# flatten indices
geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() # 将[-50,50] [-10 10]的范围平移到[0,100] [0,20],计算栅格坐标并取整
geom_feats = geom_feats.view(Nprime, 3) # 将像素映射关系同样展平 geom_feats: B*N*D*H*W x 3 (173184 x 3)
batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
device=x.device, dtype=torch.long) for ix in range(B)]) # 每个点对应于哪个batch
geom_feats = torch.cat((geom_feats, batch_ix), 1) # geom_feats: B*N*D*H*W x 4(173184 x 4), geom_feats[:,3]表示batch_id
# filter out points that are outside box
# 过滤掉在边界线之外的点 x:0~199 y: 0~199 z: 0
kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
& (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
& (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
x = x[kept] # x: 168648 x 64
geom_feats = geom_feats[kept]
# get tensors from the same voxel next to each other
ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
+ geom_feats[:, 1] * (self.nx[2] * B)\
+ geom_feats[:, 2] * B\
+ geom_feats[:, 3] # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
sorts = ranks.argsort() # 按照rank排序,这样rank相近的点就在一起了
x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts]
# cumsum trick
if not self.use_quickcumsum:
x, geom_feats = cumsum_trick(x, geom_feats, ranks)
else:
x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks) # 一个batch的一个格子里只留一个点 x: 29072 x 64 geom_feats: 29072 x 4
# griddify (B x C x Z x X x Y)
final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device) # final: 4 x 64 x 1 x 200 x 200
final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # 将x按照栅格坐标放到final中
# collapse Z
final = torch.cat(final.unbind(dim=2), 1) # 消除掉z维
return final # final: 4 x 64 x 200 x 200
create_frustum 为每一张图片生成一个棱台状(frustum)的点云 (被初始化函数调用)
def create_frustum(self): # 为每一张图片生成一个棱台状(frustum)的点云
# make grid in image plane
# 数据增强后图片大小 ogfH:128 ogfW:352
ogfH, ogfW = self.data_aug_conf['final_dim']
# 下采样16倍后图像大小 fH: 128/16=8 fW: 352/16=22
fH, fW = ogfH // self.downsample, ogfW // self.downsample
'''
ds: 在深度方向上划分网格
dbound: [4.0, 45.0, 1.0]
arange后-> [4.0,5.0,6.0,...,44.0]
view后(相当于reshape操作)-> (41x1x1)
expand后(扩展张量中某维数据的尺寸)-> ds: DxfHxfW(41x8x22)
'''
ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)
D, _, _ = ds.shape # D: 41 表示深度方向上网格的数量
'''
xs: 在宽度方向上划分网格
linspace 后(在[0,ogfW)区间内,均匀划分fW份)-> [0,16,32..336] 大小=fW(22)
view后-> 1x1xfW(1x1x22)
expand后-> xs: DxfHxfW(41x8x22)
'''
xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)
'''
ys: 在高度方向上划分网格
linspace 后(在[0,ogfH)区间内,均匀划分fH份)-> [0,16,32..112] 大小=fH(8)
view 后-> 1xfHx1 (1x8x1)
expand 后-> ys: DxfHxfW (41x8x22)
'''
ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)
'''
frustum: 把xs,ys,ds堆叠到一起
stack后-> frustum: DxfHxfWx3
堆积起来形成网格坐标, frustum[d,h,w,0]就是(h,w)位置,深度为d的像素的宽度方向上的栅格坐标
'''
frustum = torch.stack((xs, ys, ds), -1)
return nn.Parameter(frustum, requires_grad=False)
tools.py
img_transform 对输入图像进行数据增强(被data.py中的get_image_data调用)
def img_transform(img, post_rot, post_tran,
resize, resize_dims, crop,
flip, rotate): # 数据增强
# adjust image
img = img.resize(resize_dims) # 图像缩放
img = img.crop(crop) # 图像裁剪
if flip:
img = img.transpose(method=Image.FLIP_LEFT_RIGHT) # 左右翻转
img = img.rotate(rotate) # 旋转
# post-homography transformation
# 数据增强后的图像上的某一点的坐标需要对应回增强前的坐标
post_rot *= resize # [[0.22,0],[0,0.22]]
post_tran -= torch.Tensor(crop[:2]) # [0,-48]
if flip:
A = torch.Tensor([[-1, 0], [0, 1]])
b = torch.Tensor([crop[2] - crop[0], 0])
post_rot = A.matmul(post_rot)
post_tran = A.matmul(post_tran) + b
A = get_rot(rotate/180*np.pi) # 得到数据增强时旋转操作的旋转矩阵
b = torch.Tensor([crop[2] - crop[0], crop[3] - crop[1]]) / 2 # 裁剪保留部分图像的中心坐标 (176, 64)
b = A.matmul(-b) + b # 0
post_rot = A.matmul(post_rot)
post_tran = A.matmul(post_tran) + b
return img, post_rot, post_tran
gen_dx_bx 划分网格 (被model.py中的LiftSplatShoot 类中的初始化函数调用)
# 划分网络
'''
xbound=[-50.0, 50.0, 0.5],
ybound=[-50.0, 50.0, 0.5],
zbound=[-10.0, 10.0, 20.0]
'''
def gen_dx_bx(xbound, ybound, zbound):
dx = torch.Tensor([row[2] for row in [xbound, ybound, zbound]]) # dx=[0.5,0.5,20] 分别为x, y, z三个方向上的网格间距
bx = torch.Tensor([row[0] + row[2]/2.0 for row in [xbound, ybound, zbound]]) # bx=[-49.75,-49.75,0] 分别为x, y, z三个方向上第一个格子中心点的坐标
nx = torch.LongTensor([(row[1] - row[0]) / row[2] for row in [xbound, ybound, zbound]]) # nx=[200,200,1] 分别为x, y, z三个方向上格子的数量
return dx, bx, nx
QuickCumsum类 论文中提到的QuickCumsum 技巧(被modle.py文件中的voxel_pooling函数调用)
class QuickCumsum(torch.autograd.Function):
@staticmethod
def forward(ctx, x, geom_feats, ranks):
# x: 168648 x 64 geom_feats: 168648 x 4 ranks: 168648 x
x = x.cumsum(0) # 求前缀和 x: 168648 x 64
kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool) # kept: 168648 x
kept[:-1] = (ranks[1:] != ranks[:-1]) # 筛选出ranks中前后rank值不相等的位置
# rank值相等的点只留下最后一个,即一个batch中的一个格子里只留最后一个点 x: 29072 geom_feats: 29072 x 4
x, geom_feats = x[kept], geom_feats[kept]
# x后一个减前一个,还原到cumsum之前的x,此时的一个点是之前与其rank相等的点的feature的和,相当于把同一个格子的点特征进行了sum
x = torch.cat((x[:1], x[1:] - x[:-1]))
# save kept for backward
ctx.save_for_backward(kept)
# no gradient for geom_feats
ctx.mark_non_differentiable(geom_feats)
return x, geom_feats
@staticmethod
def backward(ctx, gradx, gradgeom):
kept, = ctx.saved_tensors
back = torch.cumsum(kept, 0)
back[kept] -= 1
val = gradx[back]
return val, None, None
cumsum_trick (被modle.py文件中的voxel_pooling函数调用)
def cumsum_trick(x, geom_feats, ranks):
x = x.cumsum(0)
kept = torch.ones(x.shape[0], device=x.device, dtype=torch.bool)
kept[:-1] = (ranks[1:] != ranks[:-1])
x, geom_feats = x[kept], geom_feats[kept]
x = torch.cat((x[:1], x[1:] - x[:-1]))
return x, geom_feats
SimpleLoss 计算损失(被explore.py中的eval_model_iou调用)
class SimpleLoss(torch.nn.Module):
def __init__(self, pos_weight):
super(SimpleLoss, self).__init__()
# sigmoid+二值交叉熵损失, pos_weight是给正样本乘的权重系数,防止正样本过少,用于平衡precision和recall。
self.loss_fn = torch.nn.BCEWithLogitsLoss(pos_weight=torch.Tensor([pos_weight]))
def forward(self, ypred, ytgt):
loss = self.loss_fn(ypred, ytgt)
return loss
train.py
train 对模型进行训练
def train(version, # 数据集的版本
dataroot='/data/nuscenes', # 数据集路径
nepochs=10000, # 训练最大的epoch数
gpuid=1, # gpu的序号
H=900, W=1600, # 图片大小
resize_lim=(0.193, 0.225), # resize的范围
final_dim=(128, 352), # 数据预处理之后最终的图片大小
bot_pct_lim=(0.0, 0.22), # 裁剪图片时,图像底部裁剪掉部分所占比例范围
rot_lim=(-5.4, 5.4), # 训练时旋转图片的角度范围
rand_flip=True, # # 是否随机翻转
ncams=5, # 训练时选择的相机通道数
max_grad_norm=5.0,
pos_weight=2.13, # 损失函数中给正样本项损失乘的权重系数
logdir='./runs', # 日志的输出文件
xbound=[-50.0, 50.0, 0.5], # 限制x方向的范围并划分网格
ybound=[-50.0, 50.0, 0.5], # 限制y方向的范围并划分网格
zbound=[-10.0, 10.0, 20.0], # 限制z方向的范围并划分网格
dbound=[4.0, 45.0, 1.0], # 限制深度方向的范围并划分网格
bsz=4, # batchsize
nworkers=10, # 线程数
lr=1e-3, # 学习率
weight_decay=1e-7, # 权重衰减系数
):
grid_conf = { # 网格配置
'xbound': xbound,
'ybound': ybound,
'zbound': zbound,
'dbound': dbound,
}
data_aug_conf = { # 数据增强配置
'resize_lim': resize_lim,
'final_dim': final_dim,
'rot_lim': rot_lim,
'H': H, 'W': W,
'rand_flip': rand_flip,
'bot_pct_lim': bot_pct_lim,
'cams': ['CAM_FRONT_LEFT', 'CAM_FRONT', 'CAM_FRONT_RIGHT',
'CAM_BACK_LEFT', 'CAM_BACK', 'CAM_BACK_RIGHT'],
'Ncams': ncams,
}
trainloader, valloader = compile_data(version, dataroot, data_aug_conf=data_aug_conf,
grid_conf=grid_conf, bsz=bsz, nworkers=nworkers,
parser_name='segmentationdata') # 获取训练数据和测试数据
device = torch.device('cpu') if gpuid < 0 else torch.device(f'cuda:{gpuid}')
model = compile_model(grid_conf, data_aug_conf, outC=1) # 获取模型
model.to(device)
opt = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay) # 使用Adam优化器
loss_fn = SimpleLoss(pos_weight).cuda(gpuid) # 损失函数
writer = SummaryWriter(logdir=logdir) # 用于记录训练过程
val_step = 1000 if version == 'mini' else 10000 # 每隔多少个iter验证一次
model.train()
counter = 0
for epoch in range(nepochs):
np.random.seed()
for batchi, (imgs, rots, trans, intrins, post_rots, post_trans, binimgs) in enumerate(trainloader):
# imgs: 4 x 5 x 3 x 128 x 352
# rots: 4 x 5 x 3 x 3]
# trans: 4 x 5 x 3
# intrins: 4 x 5 x 3 x 3
# post_rots: 4 x 5 x 3 x 3
# post_trans: 4 x 5 x 3
# binimgs: 4 x 1 x 200 x 200
t0 = time()
opt.zero_grad()
preds = model(imgs.to(device),
rots.to(device),
trans.to(device),
intrins.to(device),
post_rots.to(device),
post_trans.to(device),
) # 推理 preds: 4 x 1 x 200 x 200
binimgs = binimgs.to(device)
loss = loss_fn(preds, binimgs) # 计算二值交叉熵损失
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_grad_norm) # 梯度裁剪
opt.step()
counter += 1
t1 = time()
if counter % 10 == 0: # 每10个iter打印并记录一次loss
print(counter, loss.item())
writer.add_scalar('train/loss', loss, counter)
if counter % 50 == 0: # 每50个iter打印并记录一次iou和一次优化的时间
_, _, iou = get_batch_iou(preds, binimgs)
writer.add_scalar('train/iou', iou, counter)
writer.add_scalar('train/epoch', epoch, counter)
writer.add_scalar('train/step_time', t1 - t0, counter)
if counter % val_step == 0: # 验证一次,记录loss和iou
val_info = get_val_info(model, valloader, loss_fn, device)
print('VAL', val_info)
writer.add_scalar('val/loss', val_info['loss'], counter)
writer.add_scalar('val/iou', val_info['iou'], counter)
if counter % val_step == 0: # 记录checkpoint
model.eval()
mname = os.path.join(logdir, "model{}.pt".format(counter))
print('saving', mname)
torch.save(model.state_dict(), mname)
model.train()
优惠劵
zyw2002
关注
关注
38
点赞
踩
107
收藏
觉得还不错?
一键收藏
打赏
知道了
16
评论
【LSS: Lift, Splat, Shoot】代码的复现与详细解读
基于ubuntu复现Lift,Splat,Shoot代码,并对代码进行详细的解读和注释
复制链接
扫一扫
专栏目录
16 条评论
您还未登录,请先
登录
后发表或查看评论
Lift, Splat, Shoot源码及权重
01-10
Lift, Splat, Shoot: Encoding Images From Arbitrary Camera Rigs by Implicitly Unprojecting to 3D源码及权重,给不会上网的人下载
LSS-lift splat shoot论文与代码解读
weixin_41803339的博客
10-02
4908
LSS BEV
视觉BEV经典算法:LSS详解与代码
10点43的博客
03-06
3262
本文介绍一篇视觉BEV经典算法:LSS,论文收录于,本文通过显示的进行完成目标语义分割,重点是。
BEV(1)---lift splat shoot
weixin_42454048的博客
04-16
954
如图,已知世界坐标系上的某点P(X, Y, Z)经过相机的内参矩阵可以获得唯一的图像坐标p(x, y),但是反过来已知图像上某点p(x, y),无法获得唯一的世界坐标(只能知道P在Op这一射线上),只有当深度坐标Z已知时,我们才可求得唯一的世界坐标P,因此2D坐标往3D坐标的转换多围绕Z的获取展开。
CHESS-LSS:带有引擎的学校象棋项目
05-13
棋
带有引擎的学校象棋项目
经典回顾!LSS (Lift, Splat, Shoot) 论文&源码万字长文解析
CV_Autobot的博客
12-06
4049
作者|Fangzh 编辑|汽车人原文链接:https://zhuanlan.zhihu.com/p/589146284点击下方卡片,关注“自动驾驶之心”公众号ADAS巨卷干货,即可获取点击进入→自动驾驶之心【多传感器融合】技术交流群后台回复【多传感器融合综述】获取图像/激光雷达/毫米波雷达融合综述等干货资料!论文研究背景LSS是一篇发表在ECCV 2020上有关自动驾驶感知方向的论文,具体...
Lift-Splat-Shoot源码学习
WhynotLike2021的博客
07-21
238
得到最后的BEV体素输出,然后将其作为bevencode的输入(感觉这里的bevencode其实就是解码器,因为它的。
代码复现是什么?它的目标是什么?
Ty134688的博客
09-08
2093
这通常涉及到从已发表的论文、研究报告或开源项目中获取关于算法、模型或实验的详细描述,并尝试使用相同的方法和代码来再现原始作者的研究结果。代码复现通常需要仔细的阅读和理解原始研究的文档,包括论文、报告或项目文档,以及研究代码本身。通过重新实现研究中的代码,其他研究者可以验证原始研究的结果是否可以在不同的环境和条件下重复。研究者可以在原始研究的基础上进行进一步的扩展、改进或定制,以满足自己的研究需求。在尝试复现代码的过程中,研究者可能会发现原始代码中的错误、不一致性或不完善之处,这有助于改进研究的质量。
自动驾驶BEV感知系列算法整理总结
slamer111的博客
04-18
4169
BEV自动驾驶感知系列整理
代码复现问题以及解决
Randle的博客
02-28
620
随便写写
图像锐化的matlab代码-edges-master:LSS项目
05-27
图像锐化的matlab代码
lift cookbook
09-10
基于lift 框架的web开发,从环境构建,前端设计、到后端服务的开发。
matlab.rar_lift9tv
09-21
本电子书籍的功能是应用MATLAB环境进行通信系统的仿真与应用
PyTorch中torch.tensor与torch.Tensor的区别详解
09-16
主要介绍了PyTorch中torch.tensor与torch.Tensor的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
LSS:用于创建LSS目录和随机数的代码
03-17
用于创建LSS目录和随机数的代码 正在创建针对不同特定设置开发的版本。 这些位于py / LSS目录中的单独文件夹中。 通用工具位于LSS目录中。 旨在充当可执行文件的脚本位于bin目录中。 当前,应该进入bin目录以运行...
LSS:LSS脚本
03-19
最小二乘 LSS脚本
《探索自动驾驶技术的前景与挑战》
最新发布
Anastasia_y的博客
03-07
457
综上所述,自动驾驶技术的未来发展将呈现出技术不断进步、多模态融合、自动驾驶与人工智能的融合、商业化和应用推广、政策和法律的规范化以及社会接受度的提升等趋势。此外,自动驾驶汽车的推广和普及也受到政策法规、社会接受度和基础设施建设等因素的影响,需要长期的投入和努力。:自动驾驶技术的开发需要经过大量的测试和验证,以确保其安全性和可靠性。:未来的自动驾驶技术将更加依赖人工智能算法,通过深度学习和强化学习等方法,使车辆能够从大量的数据中学习驾驶规则、行为模式和环境特征,不断优化和改进驾驶决策的准确性和适应性。
智能驾驶规划控制理论学习01-自动驾驶系统介绍、规划控制模块介绍
m0_68682181的博客
02-28
1282
目前被国内外广为接受的自动驾驶级别划分标准是 SAE(国际汽车工程学会)分级,从 Level-0~Level-5 总计6 个级别,Level-0 为最低级别,Level-5 为最高级别。限于法规等政策因素,目前绝大多数车企都处在L2或是L2+水平,随着L3自动驾驶相关法规的出台,在今年年底有望出现L3水平的自动驾驶车辆。这里不再过多介绍自动驾驶的各个等级,主要介绍autoware官网给出的自动驾驶软件系统框架。
lss view transformer
07-27
LSS View Transformer是一种网络结构中的组件,用于处理图像域数据增广。它是基于图像特征预测深度,并使用预测的深度和图像特征来渲染点云,并在竖直方向上进行池化以生成BEV(鸟瞰图)特征。LSS View Transformer通常与其他组件(如Image-view Encoder backbone和BEV Encoder)一起使用,以完成特定的任务。\[3\]
#### 引用[.reference_title]
- *1* *2* *3* [BEVDet: High-Performance Multi-Camera 3D Object Detection in Bird-Eye-View](https://blog.csdn.net/kebijuelun/article/details/124613261)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item]
[ .reference_list ]
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
zyw2002
CSDN认证博客专家
CSDN认证企业博客
码龄4年
高校学生
205
原创
520
周排名
5万+
总排名
67万+
访问
等级
4207
积分
3万+
粉丝
1715
获赞
452
评论
8456
收藏
私信
关注
热门文章
YOLOv8详解 【网络结构+代码+实操】
131104
数据库课程设计 论坛系统—— 系统详细设计说明书
66514
Android Studio 连接手机进行调试
21032
NuSences 数据集解析以及 nuScenes devkit 的使用
13194
Android开发Java版 —— 基础知识
12998
分类专栏
python基础
16篇
深度学习基础
13篇
深度学习框架
14篇
计算机视觉基础
3篇
2D目标检测
10篇
3d目标检测
22篇
语义分割
OpenCV
13篇
图像增强
7篇
ROS机器人
7篇
前端开发
网站搭建
2篇
微信小程序
5篇
安卓开发
8篇
Django
6篇
Vue
1篇
工具
3篇
CS本科课程
操作系统
7篇
数据结构
15篇
计算机网络
6篇
数据库
16篇
计算机体系结构
7篇
大数据
5篇
编译原理
1篇
汇编与接口技术
7篇
程序设计模式
1篇
高性能计算
4篇
数学基础
3篇
数学建模
1篇
英语
3篇
算法
4篇
最新评论
论文精读:《DETR3D: 3D Object Detection from Multi-view Images via 3D-to-2D Queries》
啊 昃:
这篇文章中部分公式与原文不匹配,后续读者具体还是参考原文公式来看。(比如最后的损失函数,σ---->σ* 。 c_j=空集---->c_j≠空集)
【LSS: Lift, Splat, Shoot】代码的复现与详细解读
Junsun Chen:
可视化的时候,我也出现错误 至少在两个设备上运行
YOLOv8改进——引入可变形卷积DCNv3
烤荔枝的大猫:
我没按照博客的来,我是用dcnv3实现我自己的功能
YOLOv8改进——引入可变形卷积DCNv3
Abo_luo:
看看报错信息
YOLOv8改进——引入可变形卷积DCNv3
千宇.:
我解决了,不过dcnv3我加进去就报错,c2f_dcnv3可以加,请问你可以吗
最新文章
论文详解——《Deep Color Consistent Network for Low-Light Image Enhancement》
论文详解——《Learning Semantic-Aware Knowledge Guidance for Low-Light Image Enhancement》
论文及代码详解——Restormer
2023年28篇
2022年127篇
2021年50篇
目录
目录
分类专栏
python基础
16篇
深度学习基础
13篇
深度学习框架
14篇
计算机视觉基础
3篇
2D目标检测
10篇
3d目标检测
22篇
语义分割
OpenCV
13篇
图像增强
7篇
ROS机器人
7篇
前端开发
网站搭建
2篇
微信小程序
5篇
安卓开发
8篇
Django
6篇
Vue
1篇
工具
3篇
CS本科课程
操作系统
7篇
数据结构
15篇
计算机网络
6篇
数据库
16篇
计算机体系结构
7篇
大数据
5篇
编译原理
1篇
汇编与接口技术
7篇
程序设计模式
1篇
高性能计算
4篇
数学基础
3篇
数学建模
1篇
英语
3篇
算法
4篇
目录
评论 16
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
打赏作者
zyw2002
你的鼓励将是我创作的最大动力
¥1
¥2
¥4
¥6
¥10
¥20
扫码支付:¥1
获取中
扫码支付
您的余额不足,请更换扫码支付或充值
打赏作者
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值
自动驾驶:BEV开山之作LSS(lift,splat,shoot)原理代码串讲-阿里云开发者社区
自动驾驶:BEV开山之作LSS(lift,splat,shoot)原理代码串讲-阿里云开发者社区
产品解决方案文档与社区权益中心定价云市场合作伙伴支持与服务了解阿里云售前咨询 95187-1 在线服务售后咨询 4008013260 在线服务其他服务 我要建议 我要投诉更多联系方式备案控制台开发者社区首页探索云世界探索云世界云上快速入门,热门云上应用快速查找了解更多问产品动手实践考认证TIANCHI大赛活动广场活动广场丰富的线上&线下活动,深入探索云世界任务中心做任务,得社区积分和周边高校计划让每位学生受益于普惠算力训练营资深技术专家手把手带教话题畅聊无限,分享你的技术见解开发者评测最真实的开发者用云体验乘风者计划让创作激发创新阿里云MVP遇见技术追梦人直播技术交流,直击现场下载下载海量开发者使用工具、手册,免费下载镜像站极速、全面、稳定、安全的开源镜像技术资料开发手册、白皮书、案例集等实战精华插件为开发者定制的Chrome浏览器插件探索云世界新手上云云上应用构建云上数据管理云上探索人工智能云计算弹性计算无影存储网络倚天云原生容器serverless中间件微服务可观测消息队列数据库关系型数据库NoSQL数据库数据仓库数据管理工具PolarDB开源向量数据库热门Modelscope模型即服务弹性计算云原生数据库物联网云效DevOps龙蜥操作系统平头哥钉钉开放平台大数据大数据计算实时数仓Hologres实时计算FlinkE-MapReduceDataWorksElasticsearch机器学习平台PAI智能搜索推荐人工智能机器学习平台PAI视觉智能开放平台智能语音交互自然语言处理多模态模型pythonsdk通用模型开发与运维云效DevOps钉钉宜搭支持服务镜像站码上公益
开发者社区
人工智能
文章
正文
自动驾驶:BEV开山之作LSS(lift,splat,shoot)原理代码串讲
2023-03-03
2086
版权
版权声明:
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《
阿里云开发者社区用户服务协议》和
《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写
侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
简介:
自动驾驶:BEV开山之作LSS(lift,splat,shoot)原理代码串讲
@TOC
前言
目前在自动驾驶领域,比较火的一类研究方向是基于采集到的环视图像信息,去构建BEV视角下的特征完成自动驾驶感知的相关任务。所以如何准确的完成从相机视角向BEV视角下的转变就变得由为重要。目前感觉比较主流的方法可以大体分为两种:
显式估计图像的深度信息,完成BEV视角的构建,在某些文章中也被称为自下而上的构建方式;
利用transformer中的query查询机制,利用BEV Query构建BEV特征,这一过程也被称为自上而下的构建方式;
LSS最大的贡献在于:提供了一个端到端的训练方法,解决了多个传感器融合的问题。传统的多个传感器单独检测后再进行后处理的方法无法将此过程损失传进行反向传播而调整相机输入,而LSS则省去了这一阶段的后处理,直接输出融合结果。
Lift
参数
我们先介绍一下一些参数:感知范围x轴方向的感知范围 -50m ~ 50m;y轴方向的感知范围 -50m ~ 50m;z轴方向的感知范围 -10m ~ 10m;BEV单元格大小x轴方向的单位长度 0.5m;y轴方向的单位长度 0.5m;z轴方向的单位长度 20m;BEV的网格尺寸200 x 200 x 1;深度估计范围由于LSS需要显式估计像素的离散深度,论文给出的范围是 4m ~ 45m,间隔为1m,也就是算法会估计41个离散深度,也就是下面的dbound。why dbound:因为二维像素可以理解为现实世界中的某一个点到相机中心的一条射线,我们如果知道相机的内外参数,就是知道了对应关系,但是我们不知道是射线上面的那一个点(也就是不知道depth),所以作者在距离相机5m到45m的视锥内,每隔1m有一个模型可选的深度值(这样每个像素有41个可选的离散深度值)。
代码如下:
ogfH=128
ogfW=352
xbound=[-50.0, 50.0, 0.5]
ybound=[-50.0, 50.0, 0.5]
zbound=[-10.0, 10.0, 20.0]
dbound=[4.0, 45.0, 1.0]
fH, fW = ogfH // 16, ogfW // 16
创建视锥
什么是视锥?
代码:
def create_frustum(self):
# make grid in image plane
ogfH, ogfW = self.data_aug_conf['final_dim']
fH, fW = ogfH // self.downsample, ogfW // self.downsample
ds = torch.arange(*self.grid_conf['dbound'], dtype=torch.float).view(-1, 1, 1).expand(-1, fH, fW)
D, _, _ = ds.shape
xs = torch.linspace(0, ogfW - 1, fW, dtype=torch.float).view(1, 1, fW).expand(D, fH, fW)
ys = torch.linspace(0, ogfH - 1, fH, dtype=torch.float).view(1, fH, 1).expand(D, fH, fW)
# D x H x W x 3
frustum = torch.stack((xs, ys, ds), -1)
return nn.Parameter(frustum, requires_grad=False)
根据代码可知,它的尺寸是根据一个2dimage构建的,它的尺寸为D H W * 3,维度3表示:【 x,y,depth】。我们可以把这个视锥理解为一个长方体,长x,宽y 高depth,视锥中的每个点都是长方体的坐标。
CamEncode
这部分主要是通过Efficient Net来提取图像的features,首先看代码:
class CamEncode(nn.Module):
def __init__(self, D, C, downsample):
super(CamEncode, self).__init__()
self.D = D
self.C = C
self.trunk = EfficientNet.from_pretrained("efficientnet-b0")
self.up1 = Up(320+112, 512)
# 输出通道数为D+C,D为可选深度值个数,C为特征通道数
self.depthnet = nn.Conv2d(512, self.D + self.C, kernel_size=1, padding=0)
def get_depth_dist(self, x, eps=1e-20):
return x.softmax(dim=1)
def get_depth_feat(self, x):
# 主干网络提取特征
x = self.get_eff_depth(x)
# 输出通道数为D+C
x = self.depthnet(x)
# softmax编码,相理解为每个可选深度的权重
depth = self.get_depth_dist(x[:, :self.D])
# 深度值 * 特征 = 2D特征转变为3D空间(俯视图)内的特征
new_x = depth.unsqueeze(1) * x[:, self.D:(self.D + self.C)].unsqueeze(2)
return depth, new_x
def forward(self, x):
depth, x = self.get_depth_feat(x)
return x
起初与以往的相同,到了init函数的最后一句,把feature的channel下采样到了 D +C,D与上面的视锥的D一致,用来储存深度特征,C为图像的语义特征,然后对channel为D的那部分在执行softmax 用来预测depth的概率分布,然后把D这部分与C这部分单独拿出来让二者做外积,就得到了shape为BNDCHW的feature。demo代码如下:
a = torch.ones(36*4).resize(4,6,6)+1
demo1 = a.unsqueeze(1)
print(demo1.shape)
b = torch.ones(36*4).resize(4,6,6)+3
demo2 = b.unsqueeze(0)
print(demo2.shape)
c = demo1*demo2
print(c.shape)
torch.Size([4, 1, 6, 6])
torch.Size([1, 4, 6, 6])
torch.Size([4, 4, 6, 6])
我们观察右面的网格图,首先解释一下网格图的坐标,其中a代表某一个深度softmax概率(大小为H * W),c代表语义特征的某一个channel的feature,那么ac就表示这两个矩阵的对应元素相乘,于是就为feature的每一个点赋予了一个depth 概率,然后广播所有的ac,就得到了不同的channel的语义特征在不同深度(channel)的feature map,经过训练,重要的特征颜色会越来越深(由于softmax概率高),反之就会越来越暗淡,趋近于0.
Splat
得到了带有深度信息的feature map,那么我们想知道这些特征对应3D空间的哪个点,我们怎么做呢?
由于我们的视锥对原图做了16倍的下采样,而在上面得到feature map的感受野也是16,那么我们可以在接下来的操作把feature map映射到视锥坐标下。
转换视锥坐标系
首先我们之前得到了一个2D的视锥,现在通过相机的内外参数把它映射到车身(以车中心为原点)坐标系。
代码如下:
def get_geometry(self, rots, trans, intrins, post_rots, post_trans):
B, N, _ = trans.shape # B: batch size N:环视相机个数
# undo post-transformation
# B x N x D x H x W x 3
# 抵消数据增强及预处理对像素的变化
points = self.frustum - post_trans.view(B, N, 1, 1, 1, 3)
points = torch.inverse(post_rots).view(B, N, 1, 1, 1, 3, 3).matmul(points.unsqueeze(-1))
# 图像坐标系 -> 归一化相机坐标系 -> 相机坐标系 -> 车身坐标系
# 但是自认为由于转换过程是线性的,所以反归一化是在图像坐标系完成的,然后再利用
# 求完逆的内参投影回相机坐标系
points = torch.cat((points[:, :, :, :, :, :2] * points[:, :, :, :, :, 2:3],
points[:, :, :, :, :, 2:3]
), 5) # 反归一化
combine = rots.matmul(torch.inverse(intrins))
points = combine.view(B, N, 1, 1, 1, 3, 3).matmul(points).squeeze(-1)
points += trans.view(B, N, 1, 1, 1, 3)
# (bs, N, depth, H, W, 3):其物理含义
# 每个batch中的每个环视相机图像特征点,其在不同深度下位置对应
# 在ego坐标系下的坐标
return points
Voxel Pooling
代码:
def voxel_pooling(self, geom_feats, x):
# geom_feats;(B x N x D x H x W x 3):在ego坐标系下的坐标点;
# x;(B x N x D x fH x fW x C):图像点云特征
B, N, D, H, W, C = x.shape
Nprime = B*N*D*H*W
# 将特征点云展平,一共有 B*N*D*H*W 个点
x = x.reshape(Nprime, C)
# flatten indices
geom_feats = ((geom_feats - (self.bx - self.dx/2.)) / self.dx).long() # ego下的空间坐标转换到体素坐标(计算栅格坐标并取整)
geom_feats = geom_feats.view(Nprime, 3) # 将体素坐标同样展平,geom_feats: (B*N*D*H*W, 3)
batch_ix = torch.cat([torch.full([Nprime//B, 1], ix,
device=x.device, dtype=torch.long) for ix in range(B)]) # 每个点对应于哪个batch
geom_feats = torch.cat((geom_feats, batch_ix), 1) # geom_feats: (B*N*D*H*W, 4)
# filter out points that are outside box
# 过滤掉在边界线之外的点 x:0~199 y: 0~199 z: 0
kept = (geom_feats[:, 0] >= 0) & (geom_feats[:, 0] < self.nx[0])\
& (geom_feats[:, 1] >= 0) & (geom_feats[:, 1] < self.nx[1])\
& (geom_feats[:, 2] >= 0) & (geom_feats[:, 2] < self.nx[2])
x = x[kept]
geom_feats = geom_feats[kept]
# get tensors from the same voxel next to each other
ranks = geom_feats[:, 0] * (self.nx[1] * self.nx[2] * B)\
+ geom_feats[:, 1] * (self.nx[2] * B)\
+ geom_feats[:, 2] * B\
+ geom_feats[:, 3] # 给每一个点一个rank值,rank相等的点在同一个batch,并且在在同一个格子里面
sorts = ranks.argsort()
x, geom_feats, ranks = x[sorts], geom_feats[sorts], ranks[sorts] # 按照rank排序,这样rank相近的点就在一起了
# cumsum trick
if not self.use_quickcumsum:
x, geom_feats = cumsum_trick(x, geom_feats, ranks)
else:
x, geom_feats = QuickCumsum.apply(x, geom_feats, ranks)
# griddify (B x C x Z x X x Y)
final = torch.zeros((B, C, self.nx[2], self.nx[0], self.nx[1]), device=x.device) # final: bs x 64 x 1 x 200 x 200
final[geom_feats[:, 3], :, geom_feats[:, 2], geom_feats[:, 0], geom_feats[:, 1]] = x # 将x按照栅格坐标放到final中
# collapse Z
final = torch.cat(final.unbind(dim=2), 1) # 消除掉z维
return final # final: bs x 64 x 200 x 200
总结
优点:
1.LSS的方法提供了一个很好的融合到BEV视角下的方法。基于此方法,无论是动态目标检测,还是静态的道路结构认知,甚至是红绿灯检测,前车转向灯检测等等信息,都可以使用此方法提取到BEV特征下进行输出,极大地提高了自动驾驶感知框架的集成度。
2.虽然LSS提出的初衷是为了融合多视角相机的特征,为“纯视觉”模型而服务。但是在实际应用中,此套方法完全兼容其他传感器的特征融合。如果你想融合超声波雷达特征也不是不可以试试。
缺点:
1.极度依赖Depth信息的准确性,且必须显示地提供Depth 特征。当然,这是大部分纯视觉方法的硬伤。如果直接使用此方法通过梯度反传促进Depth网络的优化,如果Depth 网络设计的比较复杂,往往由于反传链过长使得Depth的优化方向比较模糊,难以取得较好效果。当然,一个好的解决方法是先预训练好一个较好的Depth权重,使得LSS过程中具有较为理想的Depth输出。
2.外积操作过于耗时。虽然对于机器学习来说,这样的计算量不足为道,但是对于要部署到车上的模型,当图片的feature size 较大, 且想要预测的Depth距离和精细度高时,外积这一操作带来的计算量则会大大增加。这十分不利于模型的轻量化部署,而这一点上,Transformer的方法反而还稍好一些。
HanZee
目录
热门文章
最新文章
为什么选择阿里云什么是云计算全球基础设施技术领先稳定可靠安全合规分析师报告产品和定价全部产品免费试用产品动态产品定价价格计算器云上成本管理解决方案技术解决方案文档与社区文档开发者社区天池大赛培训与认证权益中心免费试用高校计划企业扶持计划推荐返现计划支持与服务基础服务企业增值服务迁云服务官网公告健康看板信任中心关注阿里云关注阿里云公众号或下载阿里云APP,关注云资讯,随时随地运维管控云服务售前咨询:95187-1售后服务:400-80-13260法律声明及隐私权政策Cookies政策廉正举报安全举报联系我们加入我们阿里巴巴集团淘宝网天猫全球速卖通阿里巴巴国际交易市场1688阿里妈妈飞猪阿里云计算AliOS万网高德UC友盟优酷钉钉支付宝达摩院淘宝海外阿里云盘饿了么© 2009-2024 Aliyun.com 版权所有 增值电信业务经营许可证: 浙B2-20080101 域名注册服务机构许可: 浙D3-20210002 京D3-20220015浙公网安备 33010602009975号浙B2-20080101-4
LSS是什么?全面解析低硫附加费-出海哥
LSS是什么?全面解析低硫附加费-出海哥
首页
国外广告联盟
跨境电商平台
亚马逊
TikTok
Shopee
Temu
Lazada
eBay
速卖通
资讯
出海哥首页资讯
LSS是什么?全面解析低硫附加费
2023-10-26 09:44
阅读 103
LSS是Low Sulphur Fuel Surcharge的缩写,中文意思是低硫附加费。LSS是众多航运附加费中的一种,是指为弥补在新的硫氧化物排放控制区域航行船舶使用低硫燃油所增加的成本而收取的附加费。
LSS的由来
为支持全球节能减排,国际航运业、国际海事组织等先后出台船舶排放标准,要求在排放控制区严格控制船舶燃油排放物中硫化物的含量。
LSS的收取对象
LSS一般由船公司向其承运的货物所有人或收货人收取。
LSS的收取标准
LSS的收取标准由船公司自行制定,一般根据船舶吨位、航线、燃油价格等因素确定。
LSS的影响
LSS的收取会增加进出口货物的运输成本,影响进出口贸易的成本结构。
LSS的趋势
随着全球节能减排的不断推进,LSS的收取范围和收取标准将会进一步扩大。
LSS的注意事项
进出口货物收货人应注意询问船公司是否收取LSS,并将LSS费用考虑在进出口成本中。
LSS是什么时候开始收取的?
LSS最早于2015年1月1日起在北欧海域开始收取。目前,LSS已在全球多个地区的排放控制区内开始收取。
LSS的收取范围是怎样的?
LSS一般在排放控制区内收取。排放控制区包括北欧海域、渤海、长江口等地区。
LSS的收取标准是怎样的?
LSS的收取标准由船公司自行制定,一般根据船舶吨位、航线、燃油价格等因素确定。
LSS如何影响进出口贸易?
LSS的收取会增加进出口货物的运输成本,影响进出口贸易的成本结构。
希望这篇文章对您有所帮助。
赞 (0)
大家还在看
英文邮件结尾“bestregards”的使用方法
在英文邮件中,结尾的问候语是必不可少的一部分。它可以用来表达对收件人的尊重和感谢,也可以用来结束邮件。 “best regards”是英文邮件中使用最广泛的…
资讯
2023-11-15
推特注册教程2023:国内如何注册推特?
推特(Twitter)是全球知名的社交媒体平台,用户可以通过推特发表文字、图片、视频等内容,并与其他用户进行交流。在国内,由于推特被屏蔽,因此注册推特需要一定的技巧。 方法一:使用…
资讯
2023-09-16
saas平台有哪些?常见的saas平台推荐
SaaS,即软件即服务,是一种通过互联网向用户提供软件的服务模式。用户不需要购买或维护软件的硬件和软件基础设施,只需通过互联网即可访问和使用软件。 SaaS平台是提供SaaS服务的…
资讯
2023-10-10
bouxavenue:高品质女装品牌,让你尽情展现自信魅力
bouxavenue是一家来自美国的时尚女装品牌,专注于为女性提供高品质、时尚、舒适的服装。该品牌成立于2012年,总部位于美国纽约市。bouxavenue的服装设计风格简约大方,…
资讯
2023-11-25
B2B和B2C是什么意思?有什么区别?
B2B和B2C是电子商务中的两个主要商业模式,分别代表了企业对企业和企业对消费者。两者在交易主体、交易对象、交易规模、交易流程、交易特点等方面存在着明显的差异。 B2B B2B是B…
资讯
2023-11-27
4PX转运四方速递查询方法介绍
4PX是一家全球性物流公司,提供转运、国际快递、货运等服务。4PX转运四方速递查询方法如下: 登录4PX官网或APP,进入“订单管理”页面。 在订单列表中找…
资讯
2023-11-07
宝付支付是哪个平台?一文了解其基本情况
宝付支付是一家第三方支付平台,由上海漫道金服有限公司旗下的宝付网络科技(上海)有限公司运营。宝付支付成立于2011年,同年获得中国人民银行颁发的《支付业务许可证》,许可范围为互联网…
资讯
2023-10-03
供应链平台有哪些?
供应链平台是通过互联网技术,连接供应链上下游企业,为企业提供交易、交付、金融等供应链服务的平台。随着供应链管理的不断发展,供应链平台也逐渐成为企业提升供应链效率、降低成本的重要手段…
资讯
2023-10-12
SKU是什么意思?电商运营必备知识
SKU是库存保有单位(Stock Keeping Unit)的缩写,是指在一定条件下,具有可辨识性,可以单独出售或者出库的基本单位。在电商领域,SKU通常指商品的唯一编号,由数字、…
资讯
2023-09-29
单独海损与共同海损的区别
在海上运输过程中,船舶和货物可能会遭受损失。根据损失的原因和性质,可以将其分为单独海损和共同海损两类。 单独海损是指在海上运输过程中,由于保单承保风险直接导致的船舶和货物的损失。单…
资讯
2023-10-29
热门暂无内容大家还在看
商家最怕什么投诉才会退款?
看小黄车的软件推荐,让你轻松发现好物
点击率计算公式是什么?如何提高点击率?
PayPal注册教程:个人账户和商家账户怎么注册?
雅琪诺化妆品怎么样?口碑评价分析
FOB计算公式,外贸人必备
【pexels】免费高清图片素材网站,助力你的创作
小额纳税人和一般纳税人的区别有哪些?
Copyright © 2023 出海哥 版权所有 蜀ICP备2021021928号-8
论文精读《LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting》-CSDN博客
>论文精读《LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting》-CSDN博客
论文精读《LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting》
最新推荐文章于 2024-03-03 20:13:33 发布
zyw2002
最新推荐文章于 2024-03-03 20:13:33 发布
阅读量4.2k
收藏
30
点赞数
9
分类专栏:
# 3d目标检测
文章标签:
人工智能
计算机视觉
深度学习
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zyw2002/article/details/127906938
版权
3d目标检测
专栏收录该内容
22 篇文章
25 订阅
订阅专栏
LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D
文章目录
LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D论文精读摘要(Abstract)1. 介绍(Introduction)2. 相关工作(Related Work)2.1 单目目标检测(Monocular Object Detection)2.2 基于鸟瞰图框架的推测(Inference in the Bird's-Eye-View Frame)
3. 方法(Method)3.1 Lift:潜在的深度分布(Lift: Latent Depth Distribution)3.2 Splat:柱体池化 (Splat: Pillar Pooling)3.3 Shoot:运动规划 (Shoot: Motion Planning)
参考
论文精读
本文提出一个模型,输出多视角的摄像头数据,可以直接推断出在BEV坐标系下的语义信息。
车辆(蓝色), 可以驾驶的区域(橙色),车道线(绿色)
摘要(Abstract)
自动驾驶汽车感知的目标是从多个传感器提取语义表示,并将这些表示融合到一个单一的“鸟瞰”坐标框架中,以供运动规划使用。
我们提出了一种新的端到端架构,可以直接从任意数量的摄像机中提取给定图像数据的场景鸟瞰图表示。
我们方法背后的核心思想是将每个图像单独“Lift”成一个特征截锥。对于每个相机,然后将所有的截锥“Splat”到栅格化的鸟瞰网格中。
通过在整个相机设备上进行训练,我们提供的证据表明,我们的模型不仅能够学习如何表示图像,而且能够学习如何将来自所有相机的预测融合到场景的单一内聚表示中,同时对校准误差具有鲁棒性。在标准的鸟瞰任务,如目标分割和地图分割,我们模型优于所有基线和先前的工作。
为了学习运动规划的密集表示,我们表明,通过我们的模型推断的表示,通过“Shoot”模板轨迹到我们的网络输出的鸟瞰成本图,实现了可解释的端到端运动规划。
我们对我们的方法进行基准测试, 对比使用激光雷达的深度预测模型。
1. 介绍(Introduction)
多传感器感知
计算机视觉算法通常以一幅图像作为输入,输出一幅图像与坐标框架无关的预测,例如分类。或者在与输入图像相同的坐标系中进行预测,例如目标检测、语义分割或泛视分割。
这种范式与开箱即用自动驾驶的感知设置不匹配。在自动驾驶中,给出多个传感器作为输入,每个传感器都有不同的坐标框架,感知模型最终的任务是在自车框架中产生预测,供下游计划者消费,如图所示。
一个简单的方法
有许多简单、实用的策略可以将单幅图像范式扩展到多视图设置。例如,对于来自n个摄像机的3D物体检测问题,可以对所有输入的图像分别应用一个单幅图像检测器,然后根据被检测到物体的摄像机的内参和外参,转变成自车坐标系下。
这种单视图范式到多视图设置的扩展有三个有价值的对称性:
(1)平移不变性:如果图像中的像素坐标全部移位,则输出将移位相同的量。全卷积单幅图像对象检测器具有这个属性,多视图扩展从它们继承了这个属性。
(2)排列不变性:最终结果与来自n个摄像机视图的排列顺序无关。 (3)自我坐标系的平移/旋转不变性:在给定的图像中,无论捕获图像的相机相对于自我汽车位于哪里,同样的物体将被检测到。相当于说,自车坐标系旋转/平移,输出也将随之旋转/平移。
上述简单方法的缺点是,使用来自单幅图像检测器的后处理检测会阻止人们从自车框架中做出的预测中进行区分,一直到传感器输入。
因此,该模型不能以数据驱动的方式学习什么是跨相机融合信息的最佳方式。这也意味着反向传播不能用于利用来自下游计划器的反馈自动改进感知系统。
本文工作:提出Lift-Splat网络
本文提出Lift-Splat网络,保留了上述3个对称性的特点,且是端到端可微的方法。首先通过生成棱台形状的上下文特征点云,将图像“提升(lift)”到3D,然后将这些棱台“splat(可理解为投影)”到参考平面,以便于进行运动规划的下游任务 。此外,还提出“shoot(也可理解为投影)”提案轨迹到参考平面的方法来进行可解释的端到端运动规划。实验表明本文方法能够学习到从可能的输入分布中融合信息的有效机制。
2. 相关工作(Related Work)
我们从多个相机的图像数据中学习统一表示的方法建立在传感器融合和单目目标检测的最新工作基础上。来自Nutonomy、Lyft、Waymo和Argo的大规模多模态数据集,最近已经使完全依赖摄像头输入的自车的整个360度场景的完整表示学习成为可能。我们用Lift-Splat架构来探索这种可能性。
2.1 单目目标检测(Monocular Object Detection)
单目目标检测器是由它们如何从图像平面到给定三维参考坐标系的转换建模来定义的。一种标准的技术是在图像平面上应用成熟的2D目标检测器,然后训练第二个网络将2D检测框回归到3D检测框。
在nuScenes基准测试中,目前最先进的3D对象检测器使用了一种体系结构,该体系结构训练标准2d检测器,并通过损失来预测深度,该损失试图将错误的深度和错误的边界框造成的错误分离出来。这些方法在3D目标检测基准上取得了很好的性能,因为在图像平面上的检测排除了单目深度预测的模糊性。
一种最近取得成功的方法是分别训练一个网络进行单目深度预测,另一个网络分别进行鸟瞰图检测。这些方法被称为“伪雷达”。伪雷达经验成功的直观原因是,伪雷达能够训练一个鸟瞰网络,该网络运行在检测最终评估的坐标坐标系中,相对于图像平面,欧氏距离更有意义。
第三类单目物体探测器使用三维物体原语,根据它们在所有可用相机上的投影获取特征。Mono3D通过在地平面上生成三维建议,通过投影到可用图像上进行评分,从而在KITTI上实现了最先进的单目目标检测。正交特征变换建立在Mono3D的基础上,它将一个混合立方体的体素投影到图像上以收集特征,然后训练第二个“BEV”CNN以体素中的特征为条件在3D中进行检测。我们的模型解决了这些模型的一个潜在性能瓶颈,即像素为每个体素贡献相同的特征,而不依赖于该像素处对象的深度。
2.2 基于鸟瞰图框架的推测(Inference in the Bird’s-Eye-View Frame)
为了在鸟瞰图框架中直接执行推理,使用外在和内在的模型最近受到了广泛的关注。MonoLayout从单个图像执行鸟瞰推断,并使用一种对抗性的损失,以鼓励模型绘制看似合理的隐藏对象。在并发工作中,金字塔占用网络提出了一种转换器架构,将图像表示转换为鸟瞰表示。同时,FISHING Net并行工作提出了一种多视图体系结构,既能分割当前时间步中的对象,又能执行未来预测。我们在第5节中展示了我们的模型优于以往的经验工作。这些体系结构,以及我们的体系结构,使用的数据结构类似于来自机器学习图形社区的“多平面”图像。
3. 方法(Method)
3.1 Lift:潜在的深度分布(Lift: Latent Depth Distribution)
Lift: 对每个图像进行单独处理,获得了每个2D像素点在3D空间中的特征
第一个阶段的任务是对来自多个摄像机的图像单独进行处理。这个阶段的操作目的是将每个图片从二维升到统一的三维坐标系下。
单目传感器融合的挑战在于,我们需要将深度转换为参考坐标系坐标,但与每个像素相关的“深度”本质上是模糊的。我们提出的解决方案是为每个像素生成所有可能深度的表示。
具体来说,是将深度空间离散化成D段,则可以生成
D
×
H
×
W
D \times H\times W
D×H×W 的点云,对应的是一个棱台状的空间。
对于每个像素
p
p
p 对应的坐标是
(
h
,
w
)
(h,w)
(h,w), 预测一个上下文向量
c
∈
R
C
c\in R^C
c∈RC (即常见的卷积特征)和深度分布
α
\alpha
α则点云
(
d
,
h
,
w
)
(d,h,w)
(d,h,w)处的上下文特征是
c
d
=
a
d
c
c_d=a_dc
cd=adc
Outer product - Wikipedia
3.2 Splat:柱体池化 (Splat: Pillar Pooling)
Splat: 通过像素的2D坐标值和深度值,以及相机的内参和外参,计算出像素在车身坐标系中的3D坐标。
借鉴了pointPillars中的做法,把Lift步骤得到的点云转换成Pillars, 其中Pillars是无限高的体素。具体做法是,把每个点分配到离他最近的Pillars中,然后执行求和池化得到一个
C
×
H
×
W
C \times H \times W
C×H×W 的张量,再对该张量进行CNN操作得到鸟瞰图的预测结果。
将多个相机中的像素点投影在同一张俯视图中,先过滤掉感兴趣域(以车身为中心200*200范围)外的点。然后需要注意的是,在俯视图中同一个坐标可能存在多个特征,这里有两个原因:1是单张2D图像不同的像素点可能投影在俯视图中的同一个位置,2是不同相机图像中的不同像素点投影在俯视图中的同一个位置,例如不同相机画面中的同一个目标。对于同一个位置的多个特征,作者使用了sum-pooling的方法计算新的特征,最后得到了200x200xC的feature,源码中C取64。
棱台池化累积和技巧
就像OFT使用积分图像来加快他们的池化步骤一样,我们应用类似的技术来加快总和池化。考虑到生成的点云的大小,效率对于训练我们的模型至关重要。我们不是填充每个支柱然后执行求和池化,而是通过使用打包和利用“累积技巧”来避免填充求和池化。这个操作有一个解析梯度,可以有效地计算,以加快自动梯度
该技巧是基于本文方法用图像产生的点云形状是固定的,因此每个点可以预先分配一个区间(即BEV网格)索引,用于指示其属于哪一个区间。按照索引排序后,按下列方法操作:
3.3 Shoot:运动规划 (Shoot: Motion Planning)
测试阶段若使用推断的代价图进行规划,可以通过将不同轨迹投影到BEV平面,评估代价后选择代价最小的轨迹。
本文将 “规划”视为预测给定传感器观测
o
o
o 下自车
K
K
K个模板轨迹
T
=
{
τ
k
}
k
=
1
K
=
{
{
x
k
,
t
,
y
k
,
t
,
z
k
,
t
}
t
=
1
T
}
k
=
1
K
T=\left\{\tau_k\right\}{k=1}^K=\left\{\left\{x{k, t}, y_{k, t}, z_{k, t}\right\}{t=1}^T\right\}{k=1}^K
T={τk}k=1K={{xk,t,yk,t,zk,t}t=1T}k=1K 的分布,即
p
(
τ
∣
o
)
p(\tau \mid o)
p(τ∣o) ,定义为
p
(
τ
i
∣
o
)
=
exp
(
−
∑
x
i
,
y
i
∈
τ
i
c
o
(
x
i
,
y
i
)
)
∑
τ
∈
T
exp
(
−
∑
x
i
,
y
i
∈
τ
c
o
(
x
i
,
y
i
)
)
p\left(\tau_i \mid o\right)=\frac{\exp \left(-\sum_{x_i, y_i \in \tau_i} c_o\left(x_i, y_i\right)\right)}{\sum_{\tau \in \mathcal{T}} \exp \left(-\sum_{x_i, y_i \in \tau} c_o\left(x_i, y_i\right)\right)}
p(τi∣o)=∑τ∈Texp(−∑xi,yi∈τco(xi,yi))exp(−∑xi,yi∈τico(xi,yi))
对于给定真实轨迹,寻找 T 中最近邻模板轨迹,然后使用交叉熵损失训练。 实际应用中,模板轨迹集 T 是通过数据集中专家轨迹的K均值聚类得到的。
参考
https://github.com/nv-tlabs/lift-splat-shoot
Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D
手撕BEV的开山之作:lift, splat, shoot(没完全shoot)_哔哩哔哩_bilibili
Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D(LSS)_byzy的博客-CSDN博客
独热编码(One-Hot)最简洁理解_sereasuesue的博客-CSDN博客_one-hot
优惠劵
zyw2002
关注
关注
9
点赞
踩
30
收藏
觉得还不错?
一键收藏
打赏
知道了
1
评论
论文精读《LSS: Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting》
自动驾驶汽车感知的目标是从多个传感器提取语义表示,并将这些表示融合到一个单一的“鸟瞰”坐标框架中,以供运动规划使用。我们提出了一种新的端到端架构,可以直接从任意数量的摄像机中提取给定图像数据的场景鸟瞰图表示。我们方法背后的核心思想是将每个图像单独“Lift”成一个特征截锥。对于每个相机,然后将所有的截锥“Splat”到栅格化的鸟瞰网格中。
复制链接
扫一扫
专栏目录
Lift, Splat, Shoot源码及权重
01-10
Lift, Splat, Shoot: Encoding Images From Arbitrary Camera Rigs by Implicitly Unprojecting to 3D源码及权重,给不会上网的人下载
BEV的开山之作——LSS检测算法【Lift, Splat, Shoot】
weixin_45826522的博客
12-02
1124
【对D方向,也就是深度的每一个像素格进行加权】每一个采样点,都有对应的加权特征,有对应的几何深度,通过内外参将其投影到BEV视角下,得到BEV空间特征。求出camera坐标系的Zc后,依据坐标转换的矩阵公式,反解三维世界下的具体坐标,【Lift: Latent Depth Distribution】这样所有点就会汇聚为一个密集的三维集合,最后拍平则成为BEV feature【Splat:Pillar Pooling】:这种方法考虑了像素周围的16个像素,通过它们的加权平均值来计算下采样后的像素值。
1 条评论
您还未登录,请先
登录
后发表或查看评论
BEV感知:BEV开山之作LSS(lift,splat,shoot)原理代码串讲
Hanze的博客
02-16
3965
目前在自动驾驶领域,比较火的一类研究方向是基于采集到的环视图像信息,去构建BEV视角下的特征完成自动驾驶感知的相关任务。所以如何准确的完成从相机视角向BEV视角下的转变就变得由为重要。显式估计图像的深度信息,完成BEV视角的构建,在某些文章中也被称为自下而上的构建方式;利用transformer中的query查询机制,利用BEV Query构建BEV特征,这一过程也被称为自上而下的构建方式;LSS最大的贡献在于:提供了一个端到端的训练方法,解决了多个传感器融合的问题。
Lift Splat Shoot Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D 论文阅读笔记
Tianchao龙虾
12-15
682
Lift Splat Shoot Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D 论文阅读笔记
LSS:LSS脚本
03-19
最小二乘
LSS脚本
Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D(LSS)
weixin_45657478的博客
08-03
2590
Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D 论文笔记
Lift-Splat-Shoot算法理解及代码中文注释
qq_37497304的博客
09-09
5035
对算法Lift-Splat-Shoot的理解以及源代码中文注释
Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D
bestrivern的博客
05-12
2883
一.introduction
目前的计算机视觉算法任务,基于输出结果是否与输入图像在同一个参考系下,可以分为两类:
• 预测结果与输入不在同一个参考系:分类
• 预测结果与输入在同一个参考系:目标检测、语义分割、全景分割
从这个角度来看,基于BEV的自动驾驶感知算法,属于前一个类别,即预测结果与输入不在同一个参考系,这个最终得到的BEV结果属于本体车辆参考系。一般的做法是,通过算法模型,把属于多个参考系下的多模态数据,进行融合预测,形成在BEV参考系下的预测结果,如下图所示。
那么,多参考
LSS-lift splat shoot论文与代码解读
weixin_41803339的博客
10-02
4908
LSS BEV
CHESS-LSS:带有引擎的学校象棋项目
05-13
棋
带有引擎的学校象棋项目
7.1 LSS:Euro NCAP TEST PROTOCOL – Lane Support systems V2_0_1.pdf
05-19
7.1 LSS:Euro NCAP TEST PROTOCOL – Lane Support systems V2_0_1
LSS:用于创建LSS目录和随机数的代码
03-17
最小二乘
用于创建LSS目录和随机数的代码
正在创建针对不同特定设置开发的版本。 这些位于py / LSS目录中的单独文件夹中。 通用工具位于LSS目录中。
旨在充当可执行文件的脚本位于bin目录中。 当前,应该进入bin目录以运行所有内容(然后路径起作用)。 默认模式将在NERSC的CSCRATCH目录中产生输出。 此代码仅适用于NERSC。
一些例子:
python collectSV_zinfo_alltiles.py --type ELG --release blanc#这将收集blanc发行版中SV1 ELG目标的所有redshift信息(每天受发行版支持,LRG,QSO,BGS_ANY是其他受支持的类型,就像SV1中应该是其他类型一样) DESIMASK)
python mkCat_singletile.py --type ELG --tile 80623 --nigh
AI技术在室内定位的应用
jgtx8888的博客
03-01
1187
通过在室内布置信标节点,用户携带的移动设备可以接收到信标信号并计算出与信标的距离,进而确定自身位置。同时,一些新型的机器学习算法还可以处理复杂环境下的干扰问题,提高定位系统的鲁棒性。其中,AI技术在室内定位领域的应用越来越广泛,为我们的生活和工作带来了诸多便利。(2)提高鲁棒性:随着物联网技术的不断发展,室内定位系统将面临越来越多的干扰和挑战。随着AI技术的不断发展和普及,其在室内定位领域的应用将越来越广泛。(2)复杂环境下的定位精度:在复杂环境下,AI技术可能难以保证高精度的定位效果。
pytorch基础2-数据集与归一化
qq_33345365的博客
03-02
1448
Dataset逐个样本检索数据集的特征和标签。在训练模型时,通常希望以“minibatch”的形式传递样本,在每个周期重混洗(reshuffle)数据以减少模型过拟合,并使用Python的多进程加速数据检索。在机器学习中,需要指定数据集中的特征和标签。**特征(Feature)**是输入,**标签(label)**是输出。我们训练特征,然后训练模型以预测标签。特征是图像像素中的模式。标签是10个类别类型:T恤,凉鞋,连衣裙等。DataLoader是一个可迭代对象,用简单的API抽象了这种复杂性。
文献速递:帕金森的疾病分享--多模态机器学习预测帕金森病
weixin_38594676的博客
03-01
1642
在调整后,此模型的性能得到改善,如下所述及表3中,未调整模型在PPMI的平均AUC指标为80.75,标准差为8.84(范围=69.44–88.51),而在PPMI调整后的平均AUC为82.17,标准差为8.96(范围=70.93–90.17)。请注意,x轴的限制可能会有所不同,因为一些模型基于对输入数据的适应度和使用的算法,天生就会产生比其他模型更不极端的概率分布,更详细的图像包含在补充图5中。其次,机器学习(ML)流程自动化和人工智能的进展,以最大化利用这些大量的、容易获得的数据的价值。
如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?
2301_78164062的博客
03-01
1279
如何用ChatGPT+GEE+ENVI+Python进行高光谱,多光谱成像遥感数据处理?
AI:147-法律文档中的图像隐写术分析与敲诈勒索检测
一见已难忘的博客
03-02
4221
AI:147-法律文档中的图像隐写术分析与敲诈勒索检测
随着数字化时代的来临,法律文档在电子形式中广泛传播,然而,这也为一些不法分子提供了机会,利用图像隐写术在文档中植入恶意信息,进行敲诈勒索。本文将探讨人工智能在法律文档中图像隐写术分析和敲诈勒索检测方面的应用,同时提供相应的代码实例。
Transformer——词向量
最新发布
0 error(s)
03-03
1229
章讲解了独热编码的概念和局限性,然后详细阐述了词向量的概念及其优点,例如降低维度和捕捉语义关系。文章还深入讲解了几种主流的词向量训练方法,包括Word2Vec、GloVe和FastText,并列举了词向量在自然语言处理任务中的应用。此外,文章还讨论了如何通过权重矩阵获取词向量,以及词向量存在的局限性。这篇文章是理解自然语言处理中词向量的极好参考资料。
父级路由path:"/lss",children 的path:"/lss/kj"会出现什么问题
05-13
如果父级路由的path为"/lss",而其下的子路由的path为"/lss/kj",那么当用户访问"/lss/kj"时,父级路由会匹配到"/lss"这个路由,而子路由的"/kj"部分会被忽略,因此无法正确匹配到子路由,导致页面无法正常渲染。为了避免这种情况发生,可以将父级路由的path改为"/lss/"(在末尾添加一个斜杠),这样子路由的path为"/lss/kj"时,就会正确匹配到子路由。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
zyw2002
CSDN认证博客专家
CSDN认证企业博客
码龄4年
高校学生
205
原创
520
周排名
5万+
总排名
67万+
访问
等级
4207
积分
3万+
粉丝
1715
获赞
452
评论
8456
收藏
私信
关注
热门文章
YOLOv8详解 【网络结构+代码+实操】
131104
数据库课程设计 论坛系统—— 系统详细设计说明书
66514
Android Studio 连接手机进行调试
21032
NuSences 数据集解析以及 nuScenes devkit 的使用
13194
Android开发Java版 —— 基础知识
12998
分类专栏
python基础
16篇
深度学习基础
13篇
深度学习框架
14篇
计算机视觉基础
3篇
2D目标检测
10篇
3d目标检测
22篇
语义分割
OpenCV
13篇
图像增强
7篇
ROS机器人
7篇
前端开发
网站搭建
2篇
微信小程序
5篇
安卓开发
8篇
Django
6篇
Vue
1篇
工具
3篇
CS本科课程
操作系统
7篇
数据结构
15篇
计算机网络
6篇
数据库
16篇
计算机体系结构
7篇
大数据
5篇
编译原理
1篇
汇编与接口技术
7篇
程序设计模式
1篇
高性能计算
4篇
数学基础
3篇
数学建模
1篇
英语
3篇
算法
4篇
最新评论
论文精读:《DETR3D: 3D Object Detection from Multi-view Images via 3D-to-2D Queries》
啊 昃:
这篇文章中部分公式与原文不匹配,后续读者具体还是参考原文公式来看。(比如最后的损失函数,σ---->σ* 。 c_j=空集---->c_j≠空集)
【LSS: Lift, Splat, Shoot】代码的复现与详细解读
Junsun Chen:
可视化的时候,我也出现错误 至少在两个设备上运行
YOLOv8改进——引入可变形卷积DCNv3
烤荔枝的大猫:
我没按照博客的来,我是用dcnv3实现我自己的功能
YOLOv8改进——引入可变形卷积DCNv3
Abo_luo:
看看报错信息
YOLOv8改进——引入可变形卷积DCNv3
千宇.:
我解决了,不过dcnv3我加进去就报错,c2f_dcnv3可以加,请问你可以吗
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
论文详解——《Deep Color Consistent Network for Low-Light Image Enhancement》
论文详解——《Learning Semantic-Aware Knowledge Guidance for Low-Light Image Enhancement》
论文及代码详解——Restormer
2023年28篇
2022年127篇
2021年50篇
目录
目录
分类专栏
python基础
16篇
深度学习基础
13篇
深度学习框架
14篇
计算机视觉基础
3篇
2D目标检测
10篇
3d目标检测
22篇
语义分割
OpenCV
13篇
图像增强
7篇
ROS机器人
7篇
前端开发
网站搭建
2篇
微信小程序
5篇
安卓开发
8篇
Django
6篇
Vue
1篇
工具
3篇
CS本科课程
操作系统
7篇
数据结构
15篇
计算机网络
6篇
数据库
16篇
计算机体系结构
7篇
大数据
5篇
编译原理
1篇
汇编与接口技术
7篇
程序设计模式
1篇
高性能计算
4篇
数学基础
3篇
数学建模
1篇
英语
3篇
算法
4篇
目录
评论 1
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
个
红包个数最小为10个
红包总金额
元
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
打赏作者
zyw2002
你的鼓励将是我创作的最大动力
¥1
¥2
¥4
¥6
¥10
¥20
扫码支付:¥1
获取中
扫码支付
您的余额不足,请更换扫码支付或充值
打赏作者
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
0
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值
泰州市人力资源社会保障局
泰州市人力资源社会保障局
江苏人社网上办事二维码合集
@国务院 我为政府工作报告提建议
泰州实施十项重点活动助推“春风行动”走深走实
@国务院 我为政府工作报告提建议
江苏人社网上办事二维码合集
200辆大巴,专送省外务工人员返乡
泰州市在建工程项目集体承诺不欠薪!
新闻动态
各地动态
公示公告
政声传递
江苏人社网上办事二维码合集
@国务院 我为政府工作报告提建议
2023-12-14
泰州实施十项重点活动助推“春风行动”走深走实
2024-02-29
我市2024年度职称评审计划发布
2024-02-19
市人社部门慰问春节期间坚守岗位的一线员工
2024-02-19
200辆大巴,专送省外务工人员返乡
2024-02-08
我局四项“硬核举措”持续优化营商环境
2024-01-27
海陵区:“春风送岗 就在海陵”大型线下招聘会圆满举办
2月25日,“春风送岗 就在海陵”2024新春大型线下招聘会在泰州万达广场正式启动。活动现场供需两旺,热闹非凡。本次招聘会共吸引100家企业参加,提供5000余...
姜堰区:“就”在身边好便捷 助“零”为“灵”赋新能
2024-02-29
泰州海陵:千岗迎春到!
2024年“春风行动”首场大型招聘会供需两旺
2024-02-19
医药高新区(高港区):暖阳护归途 好岗春再来
2024-02-08
靖江新增2家省级博士后创新实践基地
2024-01-27
兴化高新区:获评“江苏省优秀劳动关系和谐工业园区”
2024-01-13
江苏泰兴:创新联动工作法有效化解新就业形态劳动争议
2023-12-29
2023年政府网站工作年度报表
2023年政府网站工作年度报表.pdf
泰州市人力资源和社会保障局2023年度法治政府建设情况报告
2024-03-07
无锡恒诚水利工程建设有限公司无拖欠农民工工资书面承诺
2024-03-06
江苏焱旺建设工程有限公司无拖欠农民工工资书面承诺
2024-03-06
江苏泰通建设有限公司无拖欠农民工工资书面承诺
2024-03-06
江苏泰滨水务工程有限公司无拖欠农民工工资书面承诺
2024-03-06
江苏鹏腾建筑有限公司无拖欠农民工工资书面承诺
2024-03-06
最全!50个动态场景看2024《政府工作报告》全文
十四届全国人大二次会议开幕会 习近平等党和国家领导人出席大会
2024-03-05
“@国务院 我为政府工作报告提建议”网民建言征集活动开始
2023-12-14
新华社消息|新华社围绕政府工作报告 播发新华时评三连评
2024-03-07
今年政府工作报告释放哪些重要信号?
2024-03-07
中国经济增长潜力几何?
2024-03-07
习近平在看望参加政协会议的民革科技界环境资源界委员时强调:积极建言资政广泛凝聚共识 助力中国式现代化建设
2024-03-06
习近平在参加江苏代表团审议时强调 因地制宜发展新质生产力
2024-03-06
1
2
3
4
5
泰州市人社查询服务线下窗口地址
“江苏智慧人社”APP待遇资格认证
互联网+督察横幅
农民工欠薪维权渠道
特困行业缓缴政策解读
政府信息公开
部门文件
部门政策解读
泰州市人力资源和社会保障局关于调整全市最低工资标准的通知
2024-01-29
泰州市人力资源和社会保障局泰州市财政局
关于公布2023年泰州市创业示范基地和优秀创业项目名单的通知
2023-12-29
省人力资源社会保障厅 省财政厅 省税务局 关于印发江苏省工伤保险费率管理办法(修订版)的通知
2023-12-08
泰州市人力资源和社会保障局关于
组织开展2023年泰州市技能大师工作室和企业首席技师推荐申报工作的通知
2023-11-01
泰州市人力资源和社会保障局关于做好2023年度市区企业专业技术人才奖励申报工作有关事项的通知
2023-10-18
省人力资源社会保障厅 省工业和信息化厅 省总工会 省工商业联合会
关于印发《江苏省职业技能等级直接认定试点工作方案》的通知
2023-09-28
关于开展2023年度市级人力资源服务骨干企业认定工作的通知
2023-09-15
市人力资源社会保障局 市公安局 转发省人力资源社会保障厅办公室 省公安厅办公室关于联合开展打击培训骗补买卖证书专项行动的通知
2023-09-12
《泰州市人力资源和社会保障局关于调整全市最低工资标准的通知》政策解读
2024-01-29
省人社厅关于《江苏省工伤保险费率管理办法》主要政策解读
2023-12-08
《江苏省职业技能等级直接认定试点工作方案》政策解读
2023-09-28
《江苏省劳务派遣单位信用评价管理暂行办法》政策解读
2023-08-10
《泰州市推进新时代人力资源服务业高质量发展的若干措施》政策解读
2023-04-10
省人社厅《关于执行工伤保险有关政策的意见》主要政策解读
2023-01-16
《泰州市青年就业见习工作实施办法》政策解读
2022-08-18
政府信息公开专栏
公开指南 公开制度
依申请公开
公开年报
公开目录
机构职能
政务服务
便民服务
人社网上办事大厅
江苏社保卡服务网
江苏省政务服务网
文件下载
2023年事业单位工作人员年度考核相关表单
2023-12-27
关于开展第四届江苏技能大奖评选表彰活动的通知
2023-11-06
事业单位管理岗位空缺岗位使用确认表
2023-09-04
2023年市突贡表格
2023-07-28
关于开展2023年泰州市有突出贡献的中青年专家推荐选拔工作的通知
2023-07-28
互动交流
调查征集
您对我局行风工作有何建议?
2024-02-08
您对我市社会保障卡使用及应用方面有何建议?(截止时间2023年12月31日)
2023-12-14
您对我局门户网站有何建议?(截止时间2023年10月31日)
2023-10-07
您对我局12333服务热线有何建议?(截止时间2023年8月18日)
2023-08-02
您认为我局政务公开工作还有哪些方面需要加强?(截止时间2023年6月20日)
2023-06-06
领导信箱
我要写信信件查询
信箱反馈
友情链接:
中国政府网 江苏省人民政府
泰州市人民政府