【版权声明】
本文为博主原创文章,未经博主允许严禁转载,我们会定期进行侵权检索。
参考书籍:《人工智能点云处理及深度学习算法》
ImVoxelNet是一种基于RGB图像的三维目标检测模型,发表在WACV 2022 《ImVoxelNet: Image to Voxels Projection for Monocular and Multi-View General-Purpose 3D Object Detection》,论文地址为“https://arxiv.org/abs/2106.01178”,官方程序地址为“https://github.com/saic-vul/imvoxelnet”。ImVoxelNet是一种端到端的基于单目或多视图RGB图像的深度学习卷积神经网络3D目标检测检测方法。其中,多视图RGB图像来源于多个传感器或多帧数据,并且该模型结构能够兼容室内和室外场景。截至目前,ImVoxelNet在SUN RGB-D数据集上同类预测任务排名种仍然保持第1名。数据来源于paperwithcode官网,如下图所示,网址为“https://paperswithcode.com/sota/monocular-3d-object-detection-on-sun-rgb-d”。
ImVoxelNet论文与参考程序同时考虑了室内和室外场景,程序中输入数据集分别为KITTI和SUN RGB-D。室内和室外训练程序命令分别如下:
python tools/train.py configs/imvoxelnet/imvoxelnet_4x8_kitti-3d-car.py
python tools/train.py configs/imvoxelnet/imvoxelnet_4x2_sunrgbd-3d-10class.py
ImVoxelNet模型结构如下图所示,主要包括图像特征提取、体素分割、3D CNN和Head结构。图像特征采用了ResNet50神经网络进行提取。模型对空间进行体素划分,并且通过其在图像范围内对应坐标与图像特征进行关联,将相应位置的图像特征作为体素特征。经过该步骤后,模型得到了空间中各个体素的特征。基于三维点云的深度学习算法通常是将空间划分为体素化之后,采用PointNet等方式提取体素特征,即VFE层输出。因此,ImVoxelNet将图像特征作为VFE层输出并与体素相对应。那么,模型实际上得到了与基于点云模型一样的输出。因此,后续模型结构网络可按照相同方式进行设计。例如,3D CNN层作用可看作是Middle中间层的一种实现方式。Head则是对分类和回归结果的预测。该模型在室内情况下采用了FCOS模型中的Head结构;在室外情况下则直接使用了BEV视图上的基于二维卷积的Head结构。
下面以室内情况详细介绍ImVoxelNet模型过程,输入数据集为KITTI。
1 主干网络
ImVoxelNet模型主干网络采用了ResNet50结构,输出四种不同尺寸的特征图,分别对应4倍、8倍、16倍和32倍下采样。输入图像的特征维度为3x416x1312,输出特征维度依次为256x104x328、512x52x164、1024x26x82和2048x13x41。根据之前分析,通常情况下,随着网络深度增加,特征图尺寸减少,通道数量增加,即特征视野范围和特征属性维度增加。主干网络函数入口为self.backbone(img)。
2 Neck网络
ImVoxelNet模型的Neck网络采用了特征金字塔(FPN)结构。特征金字塔对主干网络不同尺寸特征从最深层特征(尺寸最小)逐一上采样并与更浅一层特征进行融合,从而使浅层特征融合了深层特征以获取全局性更强的特征信息。特征融合采用求和方式实现,因而要求特征图的维度完全一致。特征图维度包括batch size、通道和尺寸。其中,特征图尺寸通过上采样来达成一致;通道数量则通过二维卷积操作来实现一致。ImVoxelNet模型FPN的融合通道数为64,即主干网络输出的特征通道数量经过二维卷积操作后均为64。模型将融合后的最浅层特征作为FPN的输出,特征维度为64x104x328,入口函数为x = self.neck(x)[0]。
ImVoxelNet模型FPN结构即配置如下。
FPN(
(lateral_convs): ModuleList(
(0): ConvModule(
(conv): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1))
)
(1): ConvModule(
(conv): Conv2d(512, 64, kernel_size=(1, 1), stride=(1, 1))
)
(2): ConvModule(
(conv): Conv2d(1024, 64, kernel_size=(1, 1), stride=(1, 1))
)
(3): ConvModule(
(conv): Conv2d(2048, 64, kernel_size=(1, 1), stride=(1, 1))
)
)
(fpn_convs): ModuleList(
(0): ConvModule(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)
(1): ConvModule(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)
(2): ConvModule(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)
(3): ConvModule(
(conv): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)
)
)
x = self.neck(x)[0]
x1 conv2d(256, 64) 4x64x104x328 x1
x2 conv2d(512, 64) 4x64x52x164 x2
x3 conv2d(1024, 64) 4x64x26x82 x3
x4 conv2d(2048, 64) 4x64x13x41 x4
x4 x3 x2 x1
upsample
Add upsanple add upsample add
特征融合后的x1 x2 x3 x4
x1 conv2d(64, 64) 4x64x104x328 x1
x2 conv2d(64, 64) 4x64x52x164 x2
x3 conv2d(64, 64) 4x64x26x82 x3
x4 conv2d(64, 64) 4x64x13x41 x4
out = x1 4x64x104x328 neck
self.n_voxels 216x248x12
3 生成点特征
ImVoxelNet模型将空间划分成216x248x12个体素网格空间,并将每个体素中心看作一个点云中的一个点,从而得到一个完整的点云Points。模型共计生成216x248x12=642816个点。整个体素空间的BEV视图上各个网格设置两种anchor,anchor尺寸为3.9、1.6、1.56,方向分别为0和Π。每个anchor的7个参数组成分别为x、y、z、l、w、h、θ。Anchor的维度为248x216x1x2x7。
全部体素中心点投影到图像坐标系,并转换到与RGB图像相同分辨率。模型通过对图像特征进行插值得到体素投影后图像的特征,从而Points投影后每个位置获取了来源于图像的特征,进而相当于为每个体素的都赋予了特征属性,即点云空间的特征属性。特征维度为642816x64(64x216x248x12),即点的数量和点的特征维度。由于该特征是由图像特征插值而来,因而特征属性数量保持不变,均为64。 模型用valid_preds(1x216x248x12)来标记特征全为0的体素点,可理解为相应体素内不含有点云。
获取空间体素或点特征之后,ImVoxelNet结构的后续网络与常规的三维目标检测结构一致,其主要采用了FCOS模型中的模型方法。
4 Neck3d
Neck3d采用4组残差模块(ResModule,参考ResNet18)和两个卷积模块,共计10个3d卷积模块来进行进行特征提取。提取后特征维度由64x216x248x12更新为256x248x216,深度方向特征维度降为1,特征图尺寸为248x216,特征属性维度为256。三维特征图转变为二维特征图,即BEV视图特征。这是由于在室外场景下,我们主要关注BEV视图上可能存在的目标。此外,转换为二维特征图有利于降低后续运算量并使用二维卷积提取更深层次特征。
OutdoorImVoxelNeck(
(model): Sequential(
(0): ResModule(
(conv0): ConvModule(
(conv): Conv3d(64, 64, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
(conv1): ConvModule(
(conv): Conv3d(64, 64, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(activation): ReLU(inplace=True)
)
(1): ConvModule(
(conv): Conv3d(64, 128, kernel_size=(3, 3, 3), stride=(1, 1, 2), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
(2): ResModule(
(conv0): ConvModule(
(conv): Conv3d(128, 128, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
(conv1): ConvModule(
(conv): Conv3d(128, 128, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(activation): ReLU(inplace=True)
)
(3): ConvModule(
(conv): Conv3d(128, 256, kernel_size=(3, 3, 3), stride=(1, 1, 2), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
(4): ResModule(
(conv0): ConvModule(
(conv): Conv3d(256, 256, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
(conv1): ConvModule(
(conv): Conv3d(256, 256, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(bn): BatchNorm3d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(activation): ReLU(inplace=True)
)
(5): ConvModule(
(conv): Conv3d(256, 256, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 0), bias=False)
(bn): BatchNorm3d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(activate): ReLU(inplace=True)
)
)
)
5 Head和损失函数
ImVoxelNet模型的Head结构包括目标分类、方向分类和位置回归三部分,三者损失函数分别为FocalLoss、CrossEntropyLoss和SmoothL1Loss,参配置如下所示:
Anchor3DHead(
(loss_cls): FocalLoss()
(loss_bbox): SmoothL1Loss()
(loss_dir): CrossEntropyLoss(avg_non_ignore=False)
(conv_cls): Conv2d(256, 2, kernel_size=(1, 1), stride=(1, 1))
(conv_reg): Conv2d(256, 14, kernel_size=(1, 1), stride=(1, 1))
(conv_dir_cls): Conv2d(256, 4, kernel_size=(1, 1), stride=(1, 1))
)
分类head:256x248x216特征经过conv_cls(256x2)得到2x248x216个预测结果。
位置head:256x248x216特征经过conv_reg(256x14)得到14x248x216个预测结果。
方向head:256x248x216特征经过conv_reg(256x4)得到4x248x216个预测结果。
6 顶层结构
ImVoxelNet模型的顶层结构的入口函数如下所示,主要包含了特征提取、Head和损失函数等部分。
def forward_train(self, img, img_metas, gt_bboxes_3d, gt_labels_3d,
**kwargs):
x, valid_preds = self.extract_feat(img, img_metas)
# For indoor datasets ImVoxelNet uses ImVoxelHead that handles
# mask of visible voxels.
if self.coord_type == 'DEPTH':
x += (valid_preds, )
losses = self.bbox_head.loss(*x, gt_bboxes_3d, gt_labels_3d, img_metas)
return losses
7 室内情况
ImVoxelNet模型在室内场景下采用了SUN RGB-D数据集,模型总体结构保持一致。下面重点介绍与室外场景不一致之处。室内场景下模型的训练命令如下:
python tools/train.py ../configs/imvoxelnet/imvoxelnet_4x2_sunrgbd-3d-10class
输入图像维度为3x512x672,经过ResNet结构的主干网络后得到4种不同尺度特征,维度分别为256x128x168、512x64x84、1024x32x42、2048x16x21,并在FPN操作之后得到256x128x168维特征。室内场景体素数量设置为40x40x16,共计25000个体素。类似地,根据图像特征和上采样操作,模型得到各个体素特征,维度为256x40x40x16。
Neck3d网络采用采用ResNet FPN网络结构计算得到3种不同尺度特征,128x40x40x16、128x20x20x8、128x10x10x4。这与室内情况存在两种差异。首先,特征尺度数量增加为3,原因在于室外情形下仅检测一种目标,而室内情形则检测多个目标。多尺度特征意味着多特征图视野,从而可匹配不同尺寸的目标。其次,特征空间保持为三维空间,而室外情形则转为BEV视图空间。这是因为室内我们不仅关注水平面上的目标,也需要将高度方向上的目标进行充分区分。室外条件下,垂直方向上目标出现叠加的情况较少。相比之下,室内条件则较容易出现垂直方向上的目标重叠情况。
针对室内情况,ImVoxelNet模型的bbox_head结构主要采用了FCOS HEAD结构,并且增加了中心度预测。其结构配置参数如下所示。
x = self.bbox_head(x)ImVoxelHead(
(center_loss): CrossEntropyLoss(avg_non_ignore=False)
(bbox_loss): RotatedIoU3DLoss()
(cls_loss): FocalLoss()
(conv_center): Conv3d(128, 1, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(conv_reg): Conv3d(128, 7, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1), bias=False)
(conv_cls): Conv3d(128, 10, kernel_size=(3, 3, 3), stride=(1, 1, 1), padding=(1, 1, 1))
(scales): ModuleList(
(0): Scale()
(1): Scale()
(2): Scale()
中心度
def _get_centerness(face_distances):
"""Compute point centerness w.r.t containing box.
Args:
face_distances (Tensor): Face distances of shape (B, N, 6),
(dx_min, dx_max, dy_min, dy_max, dz_min, dz_max).
Returns:
Tensor: Centerness of shape (B, N).
"""
x_dims = face_distances[..., [0, 1]]
y_dims = face_distances[..., [2, 3]]
z_dims = face_distances[..., [4, 5]]
centerness_targets = x_dims.min(dim=-1)[0] / x_dims.max(dim=-1)[0] * \
y_dims.min(dim=-1)[0] / y_dims.max(dim=-1)[0] * \
z_dims.min(dim=-1)[0] / z_dims.max(dim=-1)[0]
return torch.sqrt(centerness_targets)
8 【python三维深度学习】python三维点云从基础到深度学习_python3d点云从基础到深度学习-CSDN博客
【版权声明】
本文为博主原创文章,未经博主允许严禁转载,我们会定期进行侵权检索。
更多python与C++技巧、三维算法、深度学习算法总结、大模型请关注我的博客,欢迎讨论与交流:https://blog.csdn.net/suiyingy,或”乐乐感知学堂“公众号。Python三维领域专业书籍推荐:《人工智能点云处理及深度学习算法》。