3D 目标检测深度学习方法之 voxel-represetnation 内容综述(一)
date
Mar 3, 2022
Last edited time
Mar 27, 2023 09:03 AM
status
Published
slug
3D目标检测深度学习方法之voxel-represetnation内容综述(一)
tags
DL
CV
3D
summary
转载
type
Post
origin
Field
Plat
1.1 point2voxle 过程
上一篇文章有讲到过,这里复制过来:
- 设置 Voxelization 参数(每个 voxel 可以存放点的个数 (
max_points_number
),voxel 长宽高的大小whl
)
- 对依次每一个点,根据其对应的坐标(x,y,z)得到该点在 voxel 的索引。
- 根据索引判断该 voxel 种是否已经存在
max_points_number
个点,如果存在,则将该点直接丢弃,如果不满足,则将该点加入到该 voxel 中。
- 计算 voxel 特征
为了方便理解笔者做了一个简单的示意,如下下图的过程则是 Voxelization 的过程,如果
max_points_number
设置为 3,那么红色点如果是比较后加入到对应的 voxel,那么就会被丢弃。![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F97173954-df49-458e-bfc5-ef03122b56c0%2Fe45b2090-ca0a-414d-a266-93a65f961a85.png?table=block&id=35687287-5038-49c4-a08c-10ec89b598e7&cache=v2)
这里我们可以注意到笔者提到的信息损失则是来自于第三步的点的丢失。
这里其实还有设置一个最大 Voxel 的个数, 在常用的spconv
库里是这么写的. 那么可见信息损失其实还发生在对 Voxel 的选取上.
1.2 voxel 特征提取 VFE(Voxel Feature Encoding)
voxel 特征提取的含义就是将 point 的特征转化为 voxel 特征,是紧紧承接上一步的内容,上一步得到了很多的 voxel,每一个 voxel 中包含了少于
max_points_number
个点,这一步就是如何根据 voxel 中的 point 特取得到 voxel 特征,这里笔者介绍两种常用的特征提取方法:- MLP 提取,即是对 voxel 中的点采用几层全连接层将 voxel 中的 Point 信息映射到高维,最后再在每个特征维度上使用
maxpooling()
得到 voxel 的特征,但是这种方法必须保证每一个 voxel 中的点数一样,所以所有的 voxel 就仅仅只有两种状态,要么是空的,要么是有max_points_number
个点的(可以采用重采样的方式保证每一个 voxel 的点数一样多),一般来说这种特征提取的方式每一个 voxel 的点数比较多
- 均值特征。顾名思义,即是将 voxel 的 point 的特征(坐标 + 反射强度)直接取平均,目前这是比较优的
point_fea2voxel_feature
的方法
经过上述的 voxel 特征,我们就得到了每一个 voxel 的特征,就可以采用感知能力强大的 CNN 结构了。至此, 我们可以得到维度为
[batch_size, voxel_number, voxel_feature]
大小的 voxel1.3 voxel backbone
这一部分,在我们得到了 voxel 特征后,就是进一步提取到更加全局的特征,上文中的 voxel 特征仅仅是在一个 voxel 中的特征,甚至连 local feature 都没有很好的提取到,就 voxel backbone 的发展经历了下面的两个阶段。
1.3.1 3DCNNbackbone
voxel-based 的方法开始起步的时候,采用的 voxel 特征提取是上述中的第一种 MLP 提取方式,所对应的 backbone 的方法也就是采用比较容易实现的 3D CNN,如下图笔者做一了一个简单的示意,通过 voxel 特征提取我们得到了 point2voxel 的特征,下图中左边表示的内容(C 表示特征维度),其中空的 voxel 表示在原始空间没有点的在该 voxel 内,3D CNN 则是对这样一个 (H,W,L,C) 的四维张量做 3D 空间中的卷积,笔者示意图的 kernel 大小为
2*2*2
(一般情况下是 3*3*3
的大小),真实情况中空的 voxel 比示意图中还要多得多,这我们在日常生活中也是可以体验到的。下图中的黄色 kernel 也是三维的,经过 4 次 stride=2 的卷积再加一次额外的高度维的 stride=2 的卷积后得到了右图的 feature map , 但是这个时候任然是三维的 feature map.![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F4952a8c7-2db8-4c5e-bd48-71df679380b7%2Fd3a7156a-5851-4ac7-889e-59053d80c962.png?table=block&id=be767f4c-a4f9-4f75-bff9-c8af972bfb36&cache=v2)
经过下图的操作,即将高度维的特征直接压缩到特征维中,变成了二维的 feature map。所以此后就可以直接采用二维 RPN 网络结构对三维物体进行目标检测。这个会在后续的 RPN Head 的网络中讲到。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F329fbe93-d653-432f-ad4c-f5f49ad76683%2Fec2ac774-0040-46ba-aeed-f485572a7698.png?table=block&id=a5060e7e-e56b-412c-af82-0206ab7e35e8&cache=v2)
从上面的示意图和介绍中,我们不难发现至少有如下的几个问题:
- 3D 卷积的 kernel 为三维 kernel,因此会存在参数量巨大的问题,可能不好学习或者导致过拟合。
- 输入的整个场景的 voxels 含有很多空的 voxel,但是在卷积过程需要将其的特征填充为 0,是很占显存的,同时时间效率也降低。
- backbone 特征提取实际上是逐渐将 H 维度降低为 2,最后再压缩为 1,所提取到的特征更加偏向于 BEV 视图的特征,如何更好的利用点云的整体信息是可以考虑的
- 从上面的图中可以看出,在输入场景中是有很多空 backbone 的,这虽然对 3D CNN 是一种显存的损失,但是却维持了三维物体的几何结构,但是经过 Backbone 的 CNN 过程,会导致原本是空的 voxel 变得有信息,从而丢失了几何结构信息,这是一个很值得考虑的问题。
1.3.2 稀疏卷积 backbone
上文提到的内容中提到过 3D CNN 存在一个比较重要的问题是空的 voxels 会导致显存的浪费,所以后续的研究者 yanyan(SECOND(Sensors18))采用稀疏卷积减少显存占用,具体的可以参看 second 原文(SECOND: Sparsely Embedded
Convolutional Detection)或者笔者觉得 讲的也很清晰的文章 3D backbone(3D Backbone Network for 3DObject Detection)。
其实稀疏卷积的含义就是只对含有点的 voxel 做卷积输入,如果是空的 voxel 就直接不参与卷积计算,但是实际上随着卷积过程的推进,由于卷积的膨胀性质,会出现更多的非空的 voxel。这个问题后续引入了 “流型卷积层” 来缓解这个膨胀问题。
这里先给出 SECOND 中的结构图如下,实际上可以看的出来就是将 3D 卷积充分利用二维 map 映射关系来做。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F22758539-2def-42b9-a971-1d66da013228%2F7de89837-145e-47ff-869a-9ce26482a081.png?table=block&id=6698bf94-57bf-472c-8037-09448b2f01db&cache=v2)
笔者这里画出一个简化的内容如下,实际上就是先将原始空间中的非空的 voxel 的空间索引记录起来,将其特征排成一列 map,卷积操作也是通过计算索引来完成的,也就是说最终的结果仅仅是在二维中通过索引计算得到的,最后将 final-feature-map 通过最终的空间索引还回成 voxel 表达即可。一样的,为了使用二维 RPN 网络,一般的设计都是和上面一样将 H 层直接压缩到特征。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F8f417283-3257-4df0-9f9c-f08efcab6942%2Fbe3f3efc-fbc9-4450-9baa-11a9873bb46d.png?table=block&id=1f27f969-3861-4c77-81c9-114d7c5c4b4e&cache=v2)
1.3.3 backbone 小结
上面介绍了两种按照先后顺序发展起来的 backbone 方法,其中 3D CNN 和 voxel 特征提取中提到的 MLP 特征提取相配合(因为但是 3D CNN 还没有稀疏卷积表达形式,所以考虑到显存消耗,在 voxelization 时参数设置比较粗糙,所以每一个 voxel 中的点比较多,采用 MLP 特征提取是比较合适的)。其中 3D 稀疏卷积表达是当前流行的 backbone 设计基础结构,极大的解放了显存占用,因此可以在 3D 稀疏卷积上设计各种高效的 Backbone 结构。但是从 CVPR19 到 CVPR20 一段时间内的 voxel -backbone 都是采用如下的 encoder 的结构。这里直接截取 PV-RCNN(CVPR20)的网络结构的一部分,看的出来 3D 稀疏卷积的部分仅仅是一个下采样卷积特征提取的过程,最后的 To BEV 也就是上面笔者所画的将 H 层压缩到特征维度的操作。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F8a59330f-65d8-4bb7-b074-4f44da89bae8%2Fe1170bb6-f69b-4523-8f61-66cf729cd320.png?table=block&id=ad12593c-b375-4099-9e64-0aace29866a5&cache=v2)
1.3.4 补充知识
前面提到了 3Dbackbone 至少存在着四个值得考虑的问题,其中最大的显存占用问题已经通过稀疏表达的形式得到了比较好的解决,那么针对第四点问题,也就是卷积膨胀使得几何结构模糊化的问题,笔者有两点可以介绍,一点是今年的 CVPR 的文章 SA-SSD,(在前面的文章中有细致的讲解),如下图所示,为了使得 voxel-backbone 结构能够拥有原始几何结构的感知能力,作者在原始点中添加了两项附加任务,分别做语义分割和中心点预测,最后的目标检测结构得到了很好的几何结构的感知能力。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fb08e4ab0-7d6d-40c8-8577-c4c8b9e7d496%2F5f1fa1d4-0287-4ba5-a328-2fe578f25886.png?table=block&id=d868c383-8e86-423a-9dfb-b85240474c06&cache=v2)
第二点是笔者在实验中的思考,实际上在使用稀疏卷积的同时会配合使用‘流型卷积’结构,最原始的出处是 SubmanifoldSparse Convolutional Networks(FAIR,NIPS17),具体的内容就是卷积过程带着膨胀的性质,实际上会模糊掉边界几何信息,如下图所示。所以研究者就提出卷积输出的 grid 需要在有本来就有特征的 grid 才有输出,换句话说,就是在原来本身不是空的 voxle 才是具有输出的,而原本是空的,但是周围 kernel 邻域内存在非空的 voxels 是不可以输出的,这样就能一定上保持比较重要的几何结构。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fafce5a48-c9d1-4094-903a-43f693aacd36%2F1178aff1-1ecb-46bc-bbeb-2651b003dd4d.png?table=block&id=ca3ea2a4-a30b-4a8f-9def-ab69145467cd&cache=v2)
但是换个思考方式,卷积特征提取本身是在提取到更高维的特征,可能后续的 feature-map 在我们看来的边界模糊再机器感知中是能够被理解的也不一定。但是在 3D 卷积结构中,实验表明加了流型学习层的效果都比较好。这里笔者给出代码中的常用的设计格式如下,一般都是两层的‘流型学习’层加上一层的‘稀疏卷积’层(这样仅仅只做一个 stride=2,所以一般下采样会得到设计 4 个这样的结构做到 8× 的下采样)。
1.3 二维 RPN
为什么叫二维 RPN,因为从上面的 backbone 中我们最后将 H 维度直接压缩到特征维度中,直接将三维 feature-map 转化为了二维特征图,所以在二维特征图上,我们可以进一步采用二维优秀的 RPN 结构做三维目标检测任务,尽管 voxel-representation 的 RPN 结构有挺多的设计,但是就最基础高效的设计都是采用 Voxle-net(CVPR18) 上的 RPN 设计。如下所示,非常清晰的一个下采样然后上采样 concat 的结构。就不多提。在做 cls 和 reg 之前,笔者假设我们的 feature-map 大小是
[B,H,W,C]
,那么做 cls 的话和真正的二维检测都是一样的直接在特征维度上映射到 1,即 [B,H,W,1]
;我们知道在二维检测中回归只需要回归 Bbox 的左上角和右下角的坐标即可,但是在 3D 目标检测中,我们需要回归七个维度,分别是中心点坐标(X,Y,Z),长宽高(W,H,L)以及朝向(dir),但是在实际实验中,会回归预定设置的朝向的倍数个参数量,即比如 anchor 预先设定的值为 90° 和 180°,那么我们需要对每一个 anchor 都回归这样的七个维度,也就是 14,同样对 cls 也需要分类两个。这些问题在一些文章中有一定的改进。ICCV19 的 STD 采用球划分,就不会有方向性,更加优秀的 anchor 设计方式。![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F68231d8a-301e-46ab-9159-8acec6eb7aeb%2F6775924f-1615-41cb-9c14-ca42123f643a.png?table=block&id=14129970-99da-43d6-8fe5-c311c6b64b34&cache=v2)
2.1 速度上的改进
在第一小节中,笔者提到 3DCNN 时间消耗太大,因此在 18 年的 SENSORS 研究者提取采用稀疏卷积代替 3DCNN,大大的减少了参数量和显存消耗,这里不多介绍这篇文章。后续在 19 年 CVPR 上,pointpillars 充分利用 BEV 视图特性,如下图,我们前文介绍到的都是划分 voxel,这篇文章直接划分 pillars,这样的好处可以直接将 3D CNN 过程省略掉,FPS 高达恐怖的 60,但是不可避免的是这样做信息感知能力变差,需要进一步做更多的优化工作。这也促使我们在思考这样的一个问题,BEV 视图下的特征是否真的已经足够做 3D 目标检测?
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fae0cd300-3b14-4363-9fd8-18c8f5e4759c%2Faee442e8-84e6-4095-9a1b-7ed4f02ef561.png?table=block&id=28ccd976-05bf-42ed-9b3f-586291456f29&cache=v2)
进一步的 在 NIPS19 上有一篇出自韩松实验的文章 Point-Voxel CNN for Efficient 3DDeep Learning,想要通过 voxel 结构代替 pointnet++ 特征提取,也就是希望借助 voxel 特征提取的快速性(好像不是很搭边),如下图可以看的出来,实际上也就是将 pointnet++ 采用 voxel-backbone 代替掉,然后再采用插值的方式回到点中。最后也将该方法测试在 pointR-CNN 中得到了不错的效果。
![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ff3c66c5f-1331-40f3-8f0f-f2a10b2ed558%2F3ec2ffd4-12e0-4ac6-b50c-4eaaf094c58b.png?table=block&id=cf8169be-cf3c-4259-abaa-e415f705b37c&cache=v2)
目前在 voxel-representation 上从速度着手的研究工作并不是很多(得益于稀疏卷积),但是在 point-based 的方法中是很多从速度方面着手的,比较好的有今年 VPR20 的 3D-SSD,之前有写过比较细致的研究文章,有兴趣可以看一看研究者是如何思考这样的问题的。
2.2 精度上的改进
这一部分比较空旷,内容比较多,所以这一篇文章就先开个头,大概列举一下笔者所看到过的文章中主要的方向:
- refine
- loss
- fusion
- backboe -structure
- others
paper -list 内容看上去不少了,所以这个最核心的内容就做下次的分享内容吧。这次后续再介绍一点
3.1. point 和二维图像的变换
很多研究工作采用了二维图像和 3D 点云融合的方式,以增大信息量,但是这其中如何确定一个点所对应的二维图像的像素点的位置呢,就 KITTI 而言,可以做如下补充。
就 KITTI 的数据而言,有
(1) 我们下载的点云投影到相机平面的数据是 calib_velo_to_cam.txt,表示的是点云到相机的定位文件。在 KITTI 中还有文件 calib_cam_to_cam.txt(相机到相机的标定)。
(2) 相机和点云的坐标定义:相机(x:右,y:下,z:前) 。点云(x:前,y:左,z:上),也就证实了上文中投影在相机前面的点时采用 z>0 为判定条件。
(3) 计算点云到图像的投影矩阵,如下展开说:
3.1.1 核心思想
计算点云到图像的投影矩阵需要三个参数,分别是 P_rect(相机内参矩阵)和 R_rect(参考相机 0 到相机 xx 图像平面的旋转矩阵)以及 Tr_velo_to_cam(点云到相机的 [RT] 外参矩阵)。
3.1.2 计算投影矩阵(matlab)
% 计算点云到图像平面的投影矩阵
R_cam_to_rect= eye(4);
R_cam_to_rect(1:3,1:3)= calib.R_rect{1}; % 参考相机 0 到相机 xx 图像平面的旋转矩阵
P_velo_to_img= calib.P_rect{cam+1}R_cam_to_rectTr_velo_to_cam; % 内外参数
3.1.3 点云投影到图像
投影矩阵乘以点云坐标。在此之前需要把点云填充到四维的齐次坐标,也就是加 1。即把前三维作为输入因此需要转化为齐次坐标。投影矩阵是 4* 4 的。最后和 point 相乘就可以得到在二维图像中的位置了