【版权声明】
本文为博主原创文章,未经博主允许严禁转载,我们会定期进行侵权检索。
参考书籍:《人工智能点云处理及深度学习算法》
SMOKE是一种基于关键点估计的单阶段单目三维目标检测模型,发表在CVPRW 2020《SMOKE: Single-Stage Monocular 3D Object Detection via Keypoint Estimation》,论文下载地址为“https://arxiv.org/abs/2002.10111”。该模型关键之处在于使用关键点检测来进行三维目标检测,高效生成目标候选框。作者指出传统2D候选框生成是存在冗余的,并且会给3D检测带来不可忽略的噪声。模型另外一个创新点在于提出一种新的方法来构建3D预测框以提升训练收敛性和检测精度。该模型结构相对简洁,不需要复杂的预处理和后处理等数据处理步骤。模型发布时在单目检测KITTI数据集上取得了最好成绩,排名如下图所示。图片来源于paperwithcode官网,地址为“KITTI Cars Moderate Benchmark (Monocular 3D Object Detection) | Papers With Code”。
图 SMOKE模型在KITTI数据集上排名情况
1 总体结构
SMOKE模型总体结构如下图所示,包括图像特征提取、关键点分类和3D预测框回归等步骤。图像特征采用DLA-34网络结构,通过多尺度特征融合输出H/4 x W/4 x 256维度特征。模型根据图像特征得到关键点预测结果,并在真实关键点对应位置进行3D候选框预测。3D候选框预测过程中采用了新的编解码方式,下文将详细介绍。
图 SMOKE模型总体结构
SMOKE模型总体计算过程如下图所示:
图SMOKE模型总体计算过程
2 模型详解
2.1 图像特征提取
SMOKE模型主干网络采用了DLA-34结构,该结构详细介绍请参考论文《Deep Layer Aggregation》,论文地址为“https://arxiv.org/abs/1707.06484”。DLA模型结构如下图所示,其关键特征在于将多种尺度特征进行融合。CenterNet也采用了该结构。很多关键点检测模型(例如人体关键点检测)会用到HRNet网络结构。这两种主干网络结构在具有一定的相似性,均表现为通过交叉融合的方式来提取特征。
图DLA模型结构
SMOKE模型对原始DLA结构进行了两处改善。首先,模型将普通卷积替换成可变形卷积((deformable convolution,DCN))。其次,模型改变了批归一化方法,用GN(Group Normalization)替代BN,这是因为GN被证明对batch size和训练噪声的敏感程度更低。
主干网络通过DLA得到6种尺度特征,分别为16x384x1280、32x192x640、64x96x320、128x48x160、256x24x80、512x12x40。Neck层进一步融合成64x96x320维度特征,该特征作为self.extract_feat部分输出,即图像提取到的最终特征。原始输入图像尺寸为384x1280,特征图尺寸缩小为它的1/4大小。
图像特征提取入口函数self.extract_feat如下所示。
def extract_feat(self, img):
"""Directly extract features from the backbone+neck."""
x = self.backbone(img)
if self.with_neck:
x = self.neck(x)
return x
2.2 检测头Head
SMOKE Head结构根据特征图预测各个特征点分类和位置回归偏差,包括分类Head和回归Head。64x96x320维度特征经过卷积Conv2d(64, 256)、Conv2d(256, 3)和Sigmoid操作得到3x96x320类别得分(类别预测热力图,center2d_heatmap),其中3表示预测目标类别个数。64x96x320维度特征经过卷积Conv2d(64, 256)和Conv2d(256, 8)操作得到8x96x320位置预测结果(pred_reg)。其中,8个维度分别表示3个坐标、3个尺寸和2个角度(正弦和余弦)参数。
SMOKE模型的检测头HEAD参数如下所示。
SMOKEMono3DHead(
(loss_cls): GaussianFocalLoss()
(loss_bbox): L1Loss()
(loss_dir): CrossEntropyLoss(avg_non_ignore=False)
(cls_convs): ModuleList()
(reg_convs): ModuleList()
(conv_cls_prev): ModuleList(
(0): ConvModule(
(conv): Conv2d(64, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(gn): GroupNorm(32, 256, eps=1e-05, affine=True)
(activate): ReLU(inplace=True)
)
)
(conv_cls): Conv2d(256, 3, kernel_size=(1, 1), stride=(1, 1))
(conv_reg_prevs): ModuleList(
(0): ModuleList(
(0): ConvModule(
(conv): Conv2d(64, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(gn): GroupNorm(32, 256, eps=1e-05, affine=True)
(activate): ReLU(inplace=True)
)
)
)
(conv_regs): ModuleList(
(0): Conv2d(256, 8, kernel_size=(1, 1), stride=(1, 1))
)
)
2.3 标签计算
SMOKE模型是基于关键点对目标进行预测,因而需要生成真实目标的热力图。模型为每个类别生成高斯热力图,主要参数为三维目标框中心在特征图上投影后的位置和目标宽高尺寸大小。
关键输入参数描述如下。
- centers2d:三维目标在图像中的目标中心,Kx2,K为目标个数。
- gt_bbox:图像中目标的二维标注框,采用左上右下标注方式,Kx4。
- gt_bbox_3d:相机坐标系下目标标签,Kx7。
标签输出如下:
- center_heatmap_target:目标高斯热力图标签,3x96x320。
- gt_centers2d:三维目标在特征图中的目标中心,K x 2。
- gt_labels3d:真实目标类别标签,K。
- Indices:是否存在真实目标的标签,一批数据补齐到最大目标数量时的mask。
- reg_indices:各个目标对应的原始图像是否进行了仿射变换增强,K。
- gt_locs:相机坐标系下三维目标中心位置,K x 3。
- gt_dims:相机坐标系下三维目标尺寸大小,K x 3。
- gt_yaws:相机坐标系下三维目标方向角度,K x 1。
- gt_cors:相机坐标系下三维目标顶点坐标,K x 8 x 3。
2.4 预测结果解码
SMOKE Head的位置预测结果pred_reg维度为8x96x320,筛选出真实目标特征图中心点centers2d对应位置的预测结果pred_regression,从而得到Kx8个预测位置结果。特征图中目标中心pred_reg[:, 1:3]加上预测偏差得到预测的目标中心在特征图中的位置。该预测位置通过逆矩阵将特征trans_mats_inv图中心变换到图像中心,并进一步通过逆矩阵cam2imgs_inv将图像中心变换到相机坐标系下位置,从而得到预测的三维目标结果pred_locations(Kx3)。
根据尺寸pred_reg[:, 3:6]和方向pred_reg[:, 6:8]预测偏差,模型解析出预测其对应取值pred_dimensions(Kx3)和pred_orientations(Kx1)。
针对预测结果,模型构建了三种预测的3D预测框。每种预测框输入参数为位置、尺寸和方向,并且其中两个由真实目标框决定,剩余1个由预测框决定。这一点与之前介绍的模型有明显区别。之前介绍的模型中3D预测框大多完全由预测框来决定。SMOKE构建的3D预测候选框分别为bbox3d_yaws、bbox3d_dims、bbox3d_locs,维度均为Kx7。关键程序如下所示。
pred_regression = transpose_and_gather_feat(pred_reg, centers2d_inds)
bbox3d_yaws = self.bbox_coder.encode(gt_locations, gt_dimensions, orientations, img_metas)
bbox3d_dims = self.bbox_coder.encode(gt_locations, dimensions, gt_orientations, img_metas)
bbox3d_locs = self.bbox_coder.encode(locations, gt_dimensions, gt_orientations, img_metas)
pred_bboxes = dict(ori=bbox3d_yaws, dim=bbox3d_dims, loc=bbox3d_locs)
2.5 损失函数
SMOKE模型的损失函数包括热力图损失和回归损失。热力图损失输入为center2d_heatmap和center2d_heatmap_target,相应损失函数为GaussianFocalLoss。回归损失包括位置、尺寸和方向损失。模型将这三部分的预测结果均转变为3D预测框,其损失通过预测框和真实框之间8个顶点的差异来进行计算,相应损失函数L1Loss。关键程序如下所示。
# GaussianFocalLoss
loss_cls = self.loss_cls(center2d_heatmap, center2d_heatmap_target, avg_factor=avg_factor)
# L1Loss
loss_bbox_oris = self.loss_bbox(pred_bboxes['ori'].corners[reg_inds, ...], target_labels['gt_cors'][reg_inds, ...])
# L1Loss
loss_bbox_dims = self.loss_bbox(pred_bboxes['dim'].corners[reg_inds, ...], target_labels['gt_cors'][reg_inds, ...])
# L1Loss
loss_bbox_locs = self.loss_bbox(pred_bboxes['loc'].corners[reg_inds, ...], target_labels['gt_cors'][reg_inds, ...])
# self.loss_bbox
loss_bbox = loss_bbox_dims + loss_bbox_locs + loss_bbox_oris
loss_dict = dict(loss_cls=loss_cls, loss_bbox=loss_bbox)
2.6 顶层结构
SMOKE模型顶层结构包含如下两部分。
- 特征提取:self.extract_feat,通过DLA-34结构提取8x96x320维度特征,见(1)部分。
- 损失计算:损失计算部分包括了Head、标签计算、预测结果解码和损失函数四部分。
SMOKE顶层结构入口程序如下所示。
def forward_train(self, img, img_metas, gt_bboxes, gt_labels, gt_bboxes_3d, gt_labels_3d, centers2d, depths, attr_labels=None, gt_bboxes_ignore=None):
x = self.extract_feat(img)
losses = self.bbox_head.forward_train(x, img_metas, gt_bboxes, gt_labels, gt_bboxes_3d, gt_labels_3d, centers2d, depths, attr_labels, gt_bboxes_ignore)
return losses
3 模型训练
SMOKE模型官方程序地址为“https://github.com/lzccccc/SMOKE”,而本节基于mmdetection3d框架中的实现程序进行介绍,其输入数据集为KITTI,并且预测Car、Pedestrian和Cyclist这3类目标。模型训练命令为“python tools/train.py configs/smoke/smoke_dla34_pytorch_dlaneck_gn-all_8x4_6x_kitti-mono3d.py”。运行训练命令可得到如下图所示训练结果。
图 SMOKE训练结果示意图
4 【python三维深度学习】python三维点云从基础到深度学习_python3d点云从基础到深度学习-优快云博客
【版权声明】
本文为博主原创文章,未经博主允许严禁转载,我们会定期进行侵权检索。
更多python与C++技巧、三维算法、深度学习算法总结、大模型请关注我的博客,欢迎讨论与交流:https://blog.youkuaiyun.com/suiyingy,或”乐乐感知学堂“公众号。Python三维领域专业书籍推荐:《人工智能点云处理及深度学习算法》。