【论文解读】单目3D目标检测 CUPNet(ICCV 2021)

简介: 本文分享单目3D目标检测,CUPNet 模型的论文解读,了解它的设计思路,论文核心观点,模型结构,以及效果和性能。

 本文分享单目3D目标检测,CUPNet 模型的论文解读,了解它的设计思路,论文核心观点,模型结构,以及效果和性能。

image.gif

一、CUPNet简介

CUPNet是基于几何约束和回归方式输出3D框信息,在不依赖dcn的情况下获得了较好的性能。

它也是一款两阶段的单目3d检测器,先回归2D框信息,在ROI区域进一步提取特征,生成3D框信息。

开源地址:https://github.com/SuperMHP/GUPNet

论文地址:Geometry Uncertainty Projection Network for Monocular 3D Object Detection


二、论文核心观点

论文核心观点,主要包括为两点:

    • 1、物体高度估计误差,对深度计算有着较大的影响。
    • 2、模型训练的稳定性。在模型训练初期,物体高度的预测往往存在较大偏差,也因此导致了深度估算偏差较大。较大误差往往导致网络训练困难,从而影响整体网络性能。
    • 3、推断可靠性问题。如果物体的高度预测存在较大偏差,相应计算出的深度值也会存在较大误差。



    三、模型框架

    CUPNet是一个两阶段的框架,实现单目3d检测的。模型结构如下:

    Backbone:DLA34

    Neck:DLAUp

    第一部分 2D 检测:3个分支

      • 分支一 通过输出heatmap,预测所有类别的中心点(默认类别为3)。
      • 分支二 预测的2D框中心点的偏移。
      • 分支三 预测2D框的size。

      第二部分 3D 检测:4个分支

        • 分支一 预测偏航角。
        • 分支二 预测3D框的size。
        • 分支三 预测中心点的深度值,和和其不确定性(深度学习偏差)。
        • 分支四 预测2D框中心点与真实的3D投影坐标之间的偏移。

        模型结构如下图所示:(基于CenterNet的2D检测+ROI特征提取+基础3D检测头)

        image.gif

        整体的模型结构,可分为4步:

          1. 输入图像,经过主干网络提取特征。
          2. 基于CenterNet的2D框预测部分,用于输出热力图,信息包括:2D中心点、偏移量、2D框的尺寸。
          3. 提取出ROI的特征。
          4. 利用所提取的ROI特征,输入到不同的网络头,以获得物体3D框信息,包括:偏转角度、尺寸、深度值、物体3d框中心在图像投影点的偏移量。

          在第四步时,首先估计出3D框除了“深度值”以外的所有参数,然后2D框与3D框的高度将被输入到GUP模块中,提取出最终的depth。

          image.gif

          在输入端,图像将缩放到384×1280的大小,然后经过DLA的backbone获得96×320,通道数为64的特征图。随后经过三个简单的网络头输出2d信息

          2D 检测3个分支结构代码:

          self.heatmap = nn.Sequential(nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
                                      nn.ReLU(inplace=True),
                                      nn.Conv2d(self.head_conv, 3, kernel_size=1, stride=1, padding=0, bias=True))
          self.offset_2d = nn.Sequential(nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
                                      nn.ReLU(inplace=True),
                                      nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
          self.size_2d = nn.Sequential(nn.Conv2d(channels[self.first_level], self.head_conv, kernel_size=3, padding=1, bias=True),
                                      nn.ReLU(inplace=True),
                                      nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))

          image.gif

          3D 检测4个分支结构代码:

          self.depth = nn.Sequential(nn.Conv2d(channels[self.first_level]+2+self.cls_num, self.head_conv, kernel_size=3, padding=1, bias=True),
                                          nn.BatchNorm2d(self.head_conv),
                                          nn.ReLU(inplace=True),nn.AdaptiveAvgPool2d(1),
                                          nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
          self.offset_3d = nn.Sequential(nn.Conv2d(channels[self.first_level]+2+self.cls_num, self.head_conv, kernel_size=3, padding=1, bias=True),
                                          nn.BatchNorm2d(self.head_conv),
                                          nn.ReLU(inplace=True),nn.AdaptiveAvgPool2d(1),
                                          nn.Conv2d(self.head_conv, 2, kernel_size=1, stride=1, padding=0, bias=True))
          self.size_3d = nn.Sequential(nn.Conv2d(channels[self.first_level]+2+self.cls_num, self.head_conv, kernel_size=3, padding=1, bias=True),
                                          nn.BatchNorm2d(self.head_conv),
                                          nn.ReLU(inplace=True),nn.AdaptiveAvgPool2d(1),
                                          nn.Conv2d(self.head_conv, 4, kernel_size=1, stride=1, padding=0, bias=True))
          self.heading = nn.Sequential(nn.Conv2d(channels[self.first_level]+2+self.cls_num, self.head_conv, kernel_size=3, padding=1, bias=True),
                                          nn.BatchNorm2d(self.head_conv),
                                          nn.ReLU(inplace=True),nn.AdaptiveAvgPool2d(1),
                                          nn.Conv2d(self.head_conv, 24, kernel_size=1, stride=1, padding=0, bias=True))

          image.gif


          四、损失函数

          CUPNet 的损失由7部分组成,

          2D 框检测损失:3部分

            • 分支一 通过输出heatmap,预测所有类别的中心点;使用 Focal Loss 函数。
            • 分支二 预测的2D框中心点的偏移;使用 L1 Loss 函数。
            • 分支三 预测2D框的size;使用 L1 Loss 函数。

            image.gif

            3D Detection损失:4部分

              • 分支一 预测偏航角。类别使用交叉熵损失,偏航角使用L1 Loss。
              • 分支二 预测3D框的size。长和宽为L1 Loss,权重占2/3,3D 高使用laplacian_aleatoric_uncertainty_loss() 函数,权重占1/3。
              • 分支三 预测中心点的深度值,和和其不确定性;使用 laplacian_aleatoric_uncertainty_loss() 函数。
              • 分支四 预测2D框中心点与真实的3D投影坐标之间的偏移;使用 L1 Loss 函数。

              image.gif



              五、核心观点——3D高度估计误差 引起深度推断误差

              在相机投影模型引入几何信息,在估计深度时采用“相似三角形模型”,如下图所示:

              depth 是深度值、f是焦距、h_3d是实际物体高度、h_2d是图像中物体的高度。(h_3d、h_2d这种表述方式是打字比较方便,有些编辑公式支持)

              image.gif

              在该关系下,depth估计可以转化为先估计h_3d与h_2d,再通过投影的方式得到。如果物体的高度预测存在较大偏差,相应计算出的深度值也会存在较大误差。

              下面作者做了实验,由 ±0.1m 3D 高度抖动引起的深度偏移的可视化示例,图中画了一些BEV鸟瞰图的例子来展示误差放大的效果。

              image.gif

              该图中,横轴和纵轴的单位均为米,纵轴对应深度方向。 绿色框表示原始投影输出。 蓝色和红色框分别是由 +0.1m和 -0.1m 3D高度偏差引起的移位框。

              从图中可以看出,即使高度估计误差只有0.1m,也可能导致4m的深度值偏差

              解决方案:

              解决的问题其实是投影模型中的误差放大现象。我们对投影模型的输入加入一个微小的偏置,其输出则会变成原始的投影结果不确定性项(偏差项)引发的depth误差的和:

              image.gif

              详细设计看GUP模块



              六、核心内容——GUP模块、HTL模块

              6.1 提出GUP模块处理推断可靠性问题

              如果只会输出单一的深度值,作者提出Geometry Uncertainty Projection (GUP) 模块,它输出深度值+不确定度,这里的不确定度是用来表征当前深度值的可靠性。

              思路流程:预测物体3D高度 → 做映射得到深度值 → 预测偏移量 → 深度值+偏移量得到最终的不确定度

              a)预测物体3D高度。作者这里引入了Laplace distribution的假设,也就是假设物体3D高度的预测值是符合拉普拉斯分布的。

              拉普拉斯分布可以用两个变量描述,均值和方差。期望3D高度预测值,均值越接近于ground-truth,方差=1最好。方差越趋近于1,表明当前高度预测值越稳定。

              通过设计损失函数,让网络朝着我们希望的方向发展:

              image.gif

              b)做映射得到深度值,如下式:

              image.gif

              将预测出的3D高度带入,即可得到深度值。

              c)预测偏移量,给深度值又加了一层不确定度的保障。

              d)深度值+偏移量得到最终的不确定度。假设深度值和偏移量是符合拉普拉斯分布,最终的深度估计值可以有如下表示:

              image.gif

              希望depth的均值无限接近于真值,其方差无限趋近于1。也就得到了下式的损失函数

              image.gif


              6.2 提出HTL模块处理训练稳定性问题

              在训练开始时,对h_2d和h_3d的预测都很不准确,会误导整体训练,损害训练表现。

                • 作者提出了一个多级task学习策略Hierarchical Task Learning (HTL) ,来进行多任务学习。
                • 既然所有模块合在一起训练不稳定,那就分开好了,分级训练,为不同模块指定不同的训练权重,用以控制其在模型训练中的重要性。



                七、实验对比与模型效果

                在KITTI 测试集上的 3D物体检测,用以粗体突出显示最佳结果

                image.gif

                在KITTI 验证集,汽车类别,进行消融实验:

                image.gif

                模型效果:

                image.gif

                image.gif

                image.gif

                image.gif

                分享完成~

                相关文章
                |
                前端开发 Java 开发者
                深入理解Spring Boot中的@Service注解
                【4月更文挑战第22天】在 Spring Boot 应用开发中,@Service 注解扮演着特定的角色,主要用于标识服务层组件。本篇技术博客将全面探讨 @Service 注解的概念,并提供实际的应用示例,帮助开发者理解如何有效地使用这一注解来优化应用的服务层架构
                3298 1
                |
                Linux Python
                bypy:使用Linux命令行上传及下载百度云盘文件(远程服务器大文件传输必备)
                bypy:使用Linux命令行上传及下载百度云盘文件(远程服务器大文件传输必备)
                bypy:使用Linux命令行上传及下载百度云盘文件(远程服务器大文件传输必备)
                |
                XML 存储 分布式计算
                hbase构建二级索引解决方案
                HBase的一级索引就是rowkey,我们仅仅能通过rowkey进行检索。假设我们相对Hbase里面列族的列列进行一些组合查询,就只能全表扫描了。表如果较大的话,代价是不可接受的,所以要提出二级索引的方案。
                1372 0
                hbase构建二级索引解决方案
                |
                算法框架/工具 C++ Python
                根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
                根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
                1531 0
                |
                机器学习/深度学习 计算机视觉
                YOLOv11改进策略【损失函数篇】| WIoU v3:针对低质量样本的边界框回归损失函数
                YOLOv11改进策略【损失函数篇】| WIoU v3:针对低质量样本的边界框回归损失函数
                2306 6
                |
                Java 关系型数据库 MySQL
                mysql5.7 jdbc驱动
                遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
                2919 1
                |
                Linux
                如何让linux显示器命令行界面不显示
                若需在命令行界面快速关闭屏幕,可选方法包括:使用`setterm --blank force`立即关闭屏幕;利用`sudo vbetool dpms off`将显示器置于低功耗模式;在X server环境下执行`xset dpms force off`关闭屏幕;或是针对笔记本通过`echo 0 | sudo tee /sys/class/backlight/*/brightness`关闭背光。以上任一方法均可实现屏幕关闭,部分操作需要管理员权限,屏幕通常会在按键或移动鼠标后自动恢复。
                2291 9
                WK
                |
                机器学习/深度学习 算法
                为什么Sigmoid函数比Tanh函数更好
                在神经网络中,Sigmoid和Tanh函数各有优劣,选择取决于具体应用场景。Sigmoid函数输出范围为(0,1),适合二分类问题,但存在梯度消失和非零中心化的问题;Tanh函数输出范围为(-1,1),以0为中心,有利于加速收敛,但同样涉及较大的计算复杂度。两者均存在梯度消失风险,但在多数情况下,Tanh梯度问题较轻。随着技术发展,ReLU等新型激活函数因能有效缓解梯度消失并提高计算效率,已成为许多任务的首选。因此,不能简单地说Sigmoid比Tanh更好,需依据任务需求和网络结构进行选择。
                WK
                1097 1
                |
                网络协议 Java Go
                技术笔记:KCP协议学习
                技术笔记:KCP协议学习
                987 0
                |
                机器学习/深度学习 算法 C语言
                5.2.1 Backbone(特征提取) 5.2.2 Neck(多尺度检测)
                这篇文章介绍了YOLOv3目标检测模型中的Backbone(特征提取)部分,详细阐述了使用Darknet53作为骨干网络来提取图像特征的方法,并通过代码示例展示了如何实现Darknet53网络结构以及如何查看不同层级输出特征图的形状,同时还讨论了Neck(多尺度检测)的概念,解释了如何通过特征图的尺寸放大和融合来实现对不同尺寸目标的检测。