人工智能自动驾驶三维车道线检测—PersFormer模型代码详解

news2024/12/23 16:10:18

文章目录

  • 1. 背景介绍
  • 2. 数据加载和预处理
  • 3. 模型结构
  • 4. Loss计算
  • 5. 总结和讨论

1. 背景介绍

梳理了PersFormer 3D Lane这篇论文对应的开源代码。

2. 数据加载和预处理

数据组织方式参考:自动驾驶三维车道线检测系列—OpenLane数据集介绍。

坐标系参考:Waymo(OpenLane)的相机坐标系定义为:X轴向前,Y轴向左,Z轴向上。

所以,目前数据集中extrinsic如下所示:

    "extrinsic": [
        [
            0.9999790376291948,
            0.0045643569339703614,
            -0.004592488211072773,
            1.5393679168564702
        ],
        [
            -0.004561049760530192,
            0.9999893316554864,
            0.0007303425877435168,
            -0.023691101834458727
        ],
        [
            0.004595772761080904,
            -0.0007093807107760264,
            0.9999891877674156,
            2.116014704116658
        ],
        [
            0.0,
            0.0,
            0.0,
            1.0
        ]
    ]

表示相机与车体坐标系之间的外参。证明当前相机坐标系与车体坐标系基本一样,只不过坐标系原点不同。

真值中三维车道线坐标如下:

            "xyz": [
                [
                    17.382800978819017,
                    17.50040078037587,
                    ...
                    20.274071148194103,
                    20.38195255123586,
                    20.48338473286711,
                    ...
                    21.931553979558124,
                    22.035070660270055,
                    22.225836627658275,
                    ...
                    26.61997710857102,
                    26.79194824101369,
                    26.900410829029237,
                    27.120094997627227,
                    27.373557832034134,
                    27.544735503944892,
                    27.76517221715751,
                   ...
                    34.04364108074635,
                    34.34183952795403,
                    34.44990326517201,
                    34.64902798842567,
                    34.80330435386684,
                    34.97738513466869,
                    35.10772807701519,
                    35.21127993338897,
                    ...
                    43.26603227123105,
                    43.394400283321744,
                    43.54402499844422
                ],
                [
                    2.106162267265404,
                    2.117356716888622,
                    2.1267196912979487,
                    2.134409778081357,
                   ...
                    2.0836507579014345,
                    2.083507611187606,
                    2.0835073709153624,
                    2.0840386385354006,
                    2.084208834336426,
                    2.0839706956475896,
                    ...
                    2.041385347080901,
                    2.0409709291451565,
                    2.040427413456658,
                    2.039615358123066,
                    ...
                    2.0134828598759036,
                    2.0115237025717696,
                    2.008972154232112
                ],
                [
                    -2.3179790375385325,
                    -2.3071957428128735,
                    ...
                    -2.6387563474505975,
                    -2.6083380476462663,
                    -2.615133499516616,
                    -2.6078418903876996,
                    ...
                    -2.8227286818859705,
                    -2.7188114534133323,
                    -2.7218183933456475,
                    -2.7215278064851747,
                    ...
                    -2.8628242116626312,
                    -2.8972871689102906,
                    -2.9289600110699885
                ]
            ],

可以看出三维车道线真值的坐标系是在Camera坐标系之下的表示,也就是Z值在相机坐标系的地面处。

  • 初始化
    • 超参数设定
    • H_crop计算
    • H_ipm2g计算
    • H_g2ipm计算
    • Anchor grid计算
    • Anchor dim计算
    • 初始化OpenLane数据集
      • 输入:数据集文件夹
      • 输出:X, Y和Z的offset以及image anchors
      • 算法内容:
        • 获取所有的.json文件,即真值文件
        • 调整外参,从Waymo车体坐标系变换到ground坐标系。此处重点解释下,Waymo相机坐标系的定义与正常相机坐标系定义不一样,Waymo是X轴朝前,Y轴朝左,Z轴向上。此时,车体坐标系也是如此定义的。所以,数据集中默认的外参旋转近似单位阵。而坐标系的原点不一样而已。而ground坐标系的定义是Y轴朝前,X轴朝右,Z轴向上。此外,三维车道线结果是在Waymo相机坐标系下的。
        • 将三维车道线坐标从相机坐标系变换到ground坐标系
        • 将三维车道线坐标从ground坐标系变换到flat ground坐标系下
        • 计算offset作为预测回归的结果
  • 模型构建
  • 模型训练
  • 数据加载
    • 看起来模型训练数据加载时又做了很多上述数据初始化很多一样的步骤
    • 加载图像数据,根据resize参数调整图像尺寸
    • 对图像进行归一化处理
    • 数据增强,图像旋转以及Anchor旋转
    • 计算segmentation所需要的label,直接利用车道线三维真值投影回图像得到segmentation的label
    • 在IPM上绘制三维车道线真值数据,得到Seg Bev数据,用于后续模型训练

最终,数据处理这层为下游提供的数据包含如下:

  • 图像归一化后的数据
  • Segmentation label
  • Ground-truth lane (anchor)
  • Ground-truth lane 2D
  • Camera height
  • Camera pitch
  • 内参
  • 外参(ground to camera)
  • 数据增强矩阵
  • Segmentation Bev map

3. 模型结构

模型整体框图如下图所示:
在这里插入图片描述

  • Encoder
    • Backbone选用ResNet,输出特征list,out_featList
    • Neck选用一系列的二维卷积、BN和ReLU,输出特征list,neck_out
    • ShareEncoder,FrontViewPathway,输出frontview_features
    • 取frontview_features的最后一层特征,frontview_final_feat,执行Lane 2D的Attention,输出laneatt_proposals_list
  • PerspectiveTransformer,输入包括原图、frontview_features和变换矩阵M
    • 获取BEV中的2D网格
    • 将BEV网格利用M矩阵,投影到IPM
    • Transformer encoder: Self-attn -> norm -> cross-attn -> norm -> ffn -> norm
      • Query是query_embed,BEV视角
      • Value是frontview_features
      • Bev 2D grid
      • IPM 2D grid
      • 首先BEV得self-attention
      • 之后是BEV与IPM和frontview_features的cross attention
      • 最后norm等操作输出feature
    • 输出特征,projs
  • Bev Head
    • 输入:特征projs
    • 输出:特征bev_feature
    • 算法内容:
      • SingleTopViewPathway
      • SingleTopViewPathway
      • 经过一系列卷积操作
  • LanePredictionHead
    • 输入:特征bev_feature
    • 输出:处理后的特征bev_feature
    • 算法内容:
      • 一系列卷积操作
      • 将张量 x 中特定的维度范围应用 Sigmoid 函数,以便将这些值的值域从任意实数映射到 (0, 1) 区间内。这通常用于处理模型输出中的可见性或置信度分数,使得这些分数可以被解释为概率。
  • 模型最后输出:Laneatt_proposals_list, out, cam_height, cam_pitch, pred_seg_bev_map, uncertainty_loss

4. Loss计算

函数调用:

# 3D loss
loss_3d, loss_3d_dict = criterion(output_net, gt, pred_hcam, gt_hcam, pred_pitch, gt_pitch)
# Add lane attion loss
loss_att, loss_att_dict = model.module.laneatt_head.loss(laneatt_proposals_list, gt_laneline_img,
                                                        cls_loss_weight=args.cls_loss_weight,
                                                        reg_vis_loss_weight=args.reg_vis_loss_weight)

loss_seg = bceloss(pred_seg_bev_map, seg_bev_map)
# overall loss
loss = self.compute_loss(args, epoch, loss_3d, loss_att, loss_seg, uncertainty_loss, loss_3d_dict, loss_att_dict)
  • 3D loss
class Laneline_loss_gflat_3D(nn.Module):
    """
    Compute the loss between predicted lanelines and ground-truth laneline in anchor representation.
    The anchor representation is in flat ground space X', Y' and real 3D Z. Visibility estimation is also included.
    The X' Y' and Z estimation will be transformed to real X, Y to compare with ground truth. An additional loss in
    X, Y space is expected to guide the learning of features to satisfy the geometry constraints between two spaces

    loss = loss0 + loss1 + loss2 + loss2
    loss0: cross entropy loss for lane point visibility
    loss1: cross entropy loss for lane type classification
    loss2: sum of geometric distance betwen 3D lane anchor points in X and Z offsets
    loss3: error in estimating pitch and camera heights
    """
    def __init__(self, batch_size, num_types, anchor_x_steps, anchor_y_steps, x_off_std, y_off_std, z_std, pred_cam=False, no_cuda=False):
        super(Laneline_loss_gflat_3D, self).__init__()
        self.batch_size = batch_size
        self.num_types = num_types
        self.num_x_steps = anchor_x_steps.shape[0]
        self.num_y_steps = anchor_y_steps.shape[0]
        self.anchor_dim = 3*self.num_y_steps + 1
        self.pred_cam = pred_cam

        # prepare broadcast anchor_x_tensor, anchor_y_tensor, std_X, std_Y, std_Z
        tmp_zeros = torch.zeros(self.batch_size, self.num_x_steps, self.num_types, self.num_y_steps)
        self.x_off_std = torch.tensor(x_off_std.astype(np.float32)).reshape(1, 1, 1, self.num_y_steps) + tmp_zeros
        self.y_off_std = torch.tensor(y_off_std.astype(np.float32)).reshape(1, 1, 1, self.num_y_steps) + tmp_zeros
        self.z_std = torch.tensor(z_std.astype(np.float32)).reshape(1, 1, 1, self.num_y_steps) + tmp_zeros
        self.anchor_x_tensor = torch.tensor(anchor_x_steps.astype(np.float32)).reshape(1, self.num_x_steps, 1, 1) + tmp_zeros
        self.anchor_y_tensor = torch.tensor(anchor_y_steps.astype(np.float32)).reshape(1, 1, 1, self.num_y_steps) + tmp_zeros
        self.anchor_x_tensor = self.anchor_x_tensor/self.x_off_std
        self.anchor_y_tensor = self.anchor_y_tensor/self.y_off_std

        if not no_cuda:
            self.z_std = self.z_std.cuda()
            self.anchor_x_tensor = self.anchor_x_tensor.cuda()
            self.anchor_y_tensor = self.anchor_y_tensor.cuda()

    def forward(self, pred_3D_lanes, gt_3D_lanes, pred_hcam, gt_hcam, pred_pitch, gt_pitch):
        """

        :param pred_3D_lanes: predicted tensor with size N x (ipm_w/8) x 3*(2*K+1)
        :param gt_3D_lanes: ground-truth tensor with size N x (ipm_w/8) x 3*(2*K+1)
        :param pred_pitch: predicted pitch with size N
        :param gt_pitch: ground-truth pitch with size N
        :param pred_hcam: predicted camera height with size N
        :param gt_hcam: ground-truth camera height with size N
        :return:
        """
        sizes = pred_3D_lanes.shape
        # reshape to N x ipm_w/8 x 3 x (3K+1)
        pred_3D_lanes = pred_3D_lanes.reshape(sizes[0], sizes[1], self.num_types, self.anchor_dim)
        gt_3D_lanes = gt_3D_lanes.reshape(sizes[0], sizes[1], self.num_types, self.anchor_dim)
        # class prob N x ipm_w/8 x 3 x 1, anchor values N x ipm_w/8 x 3 x 2K, visibility N x ipm_w/8 x 3 x K
        pred_class = pred_3D_lanes[:, :, :, -1].unsqueeze(-1)
        pred_anchors = pred_3D_lanes[:, :, :, :2*self.num_y_steps]
        pred_visibility = pred_3D_lanes[:, :, :, 2*self.num_y_steps:3*self.num_y_steps]
        gt_class = gt_3D_lanes[:, :, :, -1].unsqueeze(-1)
        gt_anchors = gt_3D_lanes[:, :, :, :2*self.num_y_steps]
        gt_visibility = gt_3D_lanes[:, :, :, 2*self.num_y_steps:3*self.num_y_steps]

        # cross-entropy loss for visibility
        loss0 = -torch.sum(
            gt_visibility*torch.log(pred_visibility + torch.tensor(1e-9)) +
            (torch.ones_like(gt_visibility) - gt_visibility + torch.tensor(1e-9)) *
            torch.log(torch.ones_like(pred_visibility) - pred_visibility + torch.tensor(1e-9)))/self.num_y_steps
        # cross-entropy loss for lane probability
        loss1 = -torch.sum(
            gt_class*torch.log(pred_class + torch.tensor(1e-9)) +
            (torch.ones_like(gt_class) - gt_class) *
            torch.log(torch.ones_like(pred_class) - pred_class + torch.tensor(1e-9)))
        # applying L1 norm does not need to separate X and Z
        loss2 = torch.sum(
            torch.norm(gt_class*torch.cat((gt_visibility, gt_visibility), 3)*(pred_anchors-gt_anchors), p=1, dim=3))

        # compute loss in real 3D X, Y space, the transformation considers offset to anchor and normalization by std
        pred_Xoff_g = pred_anchors[:, :, :, :self.num_y_steps]
        pred_Z = pred_anchors[:, :, :, self.num_y_steps:2*self.num_y_steps]
        gt_Xoff_g = gt_anchors[:, :, :, :self.num_y_steps]
        gt_Z = gt_anchors[:, :, :, self.num_y_steps:2*self.num_y_steps]
        pred_hcam = pred_hcam.reshape(self.batch_size, 1, 1, 1)
        gt_hcam = gt_hcam.reshape(self.batch_size, 1, 1, 1)

        pred_Xoff = (1 - pred_Z * self.z_std / pred_hcam) * pred_Xoff_g - pred_Z * self.z_std / pred_hcam * self.anchor_x_tensor
        pred_Yoff = -pred_Z * self.z_std / pred_hcam * self.anchor_y_tensor
        gt_Xoff = (1 - gt_Z * self.z_std / gt_hcam) * gt_Xoff_g - gt_Z * self.z_std / gt_hcam * self.anchor_x_tensor
        gt_Yoff = -gt_Z * self.z_std / gt_hcam * self.anchor_y_tensor
        loss3 = torch.sum(
            torch.norm(
                gt_class * torch.cat((gt_visibility, gt_visibility), 3) *
                (torch.cat((pred_Xoff, pred_Yoff), 3) - torch.cat((gt_Xoff, gt_Yoff), 3)), p=1, dim=3))

        if not self.pred_cam:
            return loss0+loss1+loss2+loss3
        loss4 = torch.sum(torch.abs(gt_pitch-pred_pitch)) + torch.sum(torch.abs(gt_hcam-pred_hcam))
        return loss0+loss1+loss2+loss3+loss4
  • 函数说明
    • 输入:预测的3D结果pred_3D_lanes,真值gt_3D_lanes,预测的Camera高度pred_hcam,真值Camera高度gt_hcam,预测的pitch pred_pitch和真值的pitch gt_pitch
    • 输出:3D loss
    • 算法内容
      • 计算visibility的loss
      • 计算lane probability的loss
      • 计算offset的3D loss
      • 计算pitch loss

5. 总结和讨论

梳理了PersFormer 3D Lane这篇论文对应的开源代码。
在这里插入图片描述

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

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

相关文章

【Raven2靶场渗透】

文章目录 一、IP获取 二、信息收集 三、Flag1 四、漏洞利用 五、Flag2 六、Flag3 七、MSF UDF提权 八、CVE漏洞本地提权 一、IP获取 Kali IP:192.168.78.128 靶机IP:192.168.78.178 二、信息收集 端口和服务探测: nmap -sV -p- 192.168.78.178 开放…

启发式合并加树形dp

题目链接 令f【x】【0】表示不选根的x子树的最大贡献,f【x】【1】表示选根的x子树最大贡献,g【x】为max(f【x】【0】,f【x】【1】)。 如果我们要连接x和u1,那么贡献是: w【x】w【u1】f【u1】【0…

ASP.NET Core 基础 - 入门实例

一. 下载 1. 下载vs2022 Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com) 学生,个人开发者选择社区版就行,免费的. 安装程序一直下一步下一步就行,别忘了选择安装位置,如果都放在C盘的话,就太大了. 2. 选择工作负荷 准备工作完成 二. 创建新项目 三…

如何用密码保护你的 WordPress 管理员 (wp-admin) 目录

在维护 WordPress 网站时,确保 wp-admin 目录安全是非常重要的。为该目录添加密码保护可以有效提高网站安全性,防止未经授权的访问。这篇文章将介绍实现这一目标的两种方法。 1.为什么要为 wp-admin 目录添加密码保护 WordPress 管理员后台是网站的核心…

自动化集成应用钡铼DB系列防水分线盒

随着工业自动化的快速发展,如今的现场设备需要更高效、更稳定的信号采集和集成方案。钡铼技术的DB系列防水分线盒作为一种优秀的解决方案,成功地结合了先进的工业设计与耐用材料,为物流设备、食品加工设备、制药设备等多种工业应用提供了可靠…

《深入浅出WPF》学习笔记六.手动实现Mvvm

《深入浅出WPF》学习笔记六.手动实现Mvvm demo的层级结构,Mvvm常用项目结构 依赖属性基类实现 具体底层原理后续学习中再探讨,可以粗浅理解为,有一个全局对象使用list或者dic监听所有依赖属性,当一个依赖属性变化引发通知时,就会遍历查询对应的字典,通知View层进行…

目标检测之选择性搜索:Selective Search

文章目录 一.选择性搜索的具体算法二.保持多样性的策略三.给区域打分四.选择性搜索性能评估五.代码实现 论文地址: https://www.koen.me/research/selectivesearch/ 代码地址: https://github.com/AlpacaDB/selectivesearch 参考: https:/…

SpringBootWeb AOP

事务&AOP 1. 事务管理 1.1 事务回顾 在数据库阶段我们已学习过事务了,我们讲到: 事务是一组操作的集合,它是一个不可分割的工作单位。事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求。所以这组操作要…

kickstart自动安装脚本,pxe网络安装

目录 1 kickstart图形化生成脚本工具 1.1 安装apache 1.2 创建挂载镜像软链接 1.3 图形生成自动化脚本选项 1.4 修改生成的自动化脚本 1.5 将脚本放至网站根目录 2 安装系统 2.1 关闭DHCP自动分配 2.2 下载配置DHCP服务 2.3 重启DHCP服务 2.4 使用pxe方法安装系统(网…

YOLOv5与YOLOv8 训练准备工作(不包含环境搭建)

前言:我发现除了安装环境需要耗费大量时间以外,对于训练前的准备工作也要琢磨一段时间,所以本篇主要讲一下训练前需要准备的工作(主要是XML格式换为txt,以及划分数据集验证集,和训练参数的设置)…

8–9月,​Sui Move智能合约工作坊将在台北+线上举行

你对区块链和去中心化应用感兴趣吗?想深入学习Sui Move编程语言吗? 从8月10日到9月28日,Sui Mover社区将在每周六下午13:00–17:00举办精彩的工作坊,为期两个月,带你从零基础入门到高级进阶,全面掌握Sui M…

Django配置模板引擎

【图书介绍】《Django 5企业级Web应用开发实战(视频教学版)》_django 5企业级web应用开发实战(视频教学版)-CSDN博客 《Django 5企业级Web应用开发实战(视频教学版)》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) 本节主要介…

Linux之进程间通信(上)

目录 进程间通信的目的 进程通信的分类 进程通信之匿名管道 创建匿名管道 匿名管道的特点 匿名管道四种通信类型 在现实生活中,人们要进行合作,就必须进行交流,那么在进程之间,会存在交流的情景吗?答案是肯定的…

音频转换器在线哪个好?提升音频质量的转换工具

你是否曾梦想过将手机里的铃声变成自己的原创作品?或者,有没有想过将一段演讲录音转化为易于分享的MP3格式? 如果答案是肯定的,那么这款音频转换器mp3就是你寻找的答案。现在,让我们一起探索它的魅力吧! 一…

基于MATLAB机器学习、深度学习实践技术

近年来,MATLAB在机器学习和深度学习领域的发展取得了显著成就。其强大的计算能力和灵活的编程环境使其成为科研人员和工程师的首选工具。在无人驾驶汽车、医学影像智能诊疗、ImageNet竞赛等热门领域,MATLAB提供了丰富的算法库和工具箱,极大地…

浏览器用户文件夹详解 - Preferences(十)

1.Preferences简介 1.1 什么是Preferences文件? Preferences文件是Chromium浏览器中用于存储用户个性化设置和配置的一个重要文件。每当用户在浏览器中更改设置或安装扩展程序时,这些信息都会被记录在Preferences文件中。通过这些记录,浏览…

海思ISP记录七:低照度图像调整

Hi3519av100imx585 记录下低照度图像调整的流程和思路 一、环境与配置 环境:暗房不开灯,只有零散漏光和电脑光亮gain与帧率:根据手册我设置的是Again:31282;Dgain:8192;ISP Dgain:…

B1.1 关于应用程序员模型-概述

快速链接: . 👉👉👉 ARMv8/ARMv9架构入门到精通-[目录] 👈👈👈 付费专栏-付费课程 【购买须知】个人博客笔记导读目录(全部) B1.1 关于应用程序员模型–概述 本章节包含了应用程序开发所需的程序员模型信息。 本章节中的信息不同于支持和服务于操作系统下应用程…

1.MySQL面试题之innodb如何解决幻读

1. 写在前面 在数据库系统中,幻读(Phantom Read)是指在一个事务中,两次读取同一范围的数据集时,由于其他事务的插入操作,导致第二次读取结果集发生变化的问题。InnoDB 作为 MySQL 的一个存储引擎&#xff…

PyTorch深度学习实战(2)——PyTorch快速入门

PyTorch的简洁设计使得它易于入门,在深入介绍PyTorch之前,本文先介绍一些PyTorch的基础知识,以便读者能够对PyTorch有一个大致的了解,并能够用PyTorch搭建一个简单的神经网络。 1 Tensor Tensor是PyTorch中最重要的数据结构&#…