单目3D车辆检测全流程实战分享-附完整代码

完整的项目代码在这里哦~

基于M3D-RPN实现单目3D检测 - 飞桨AI Studio

1. 项目说明

  当前,3D 检测作为核心技术点,在机器人、增强现实等场景下应用广泛,发挥着至关重要的作用。传统依赖激光雷达的 3D 检测方法存在传感器昂贵难以部署,点云缺失纹理信息,分辨率低等诸多问题。

  针对于此,开发单目 3D 检测模型,有效的利用图像相对于点云的种种优势,可以降低产业落地门槛,更广泛简单的部署到实际应用场景中。

  单目的 3D 目标检测近几年一直是研究的热点,虽然往算法中添加先验知识,能够一定程度的提升准确率,但是也增加了获取标签的难度和算法设计的复杂性。

 图 1 - 单目 3D 检测示例

欢迎扫码获取视频课程讲解,加入单目3D检测技术交流群,欢迎志同道合的同学:

方案难点:

  • 深度信息缺失,由 2D 图像预测 3D 位置困难
  • 相机传感器敏感,受环境影响(夜晚、雨天)等较大
  • 图像层面,遮挡、截断等问题严重影响感知精度

2. 安装说明

2.1 环境要求

  • Python >= 3.6
  • paddlepaddle >= 2.0.2
  • cuda >= 9
  • boost 库
  • 常见 Python 库

In [ ]

# 安装库
! pip install shapely

2.2 解压数据及代码

项目代码在 M3D-RPN-2.0.tar 文件中,数据集在 kitti.tar 文件中,解压到合适路径即可使用。

In [ ]

# 如果希望解压到其他目录
# 可选择其他路径(默认 /home/aistudio )
! tar xf ~/data/data141443/kitti.tar
! unzip -qo ~/data/data141443/M3D-RPN-2.0.zip
! rm -rf __MACOSX

2.3 安装依赖

In [ ]

# 删除已有软连接
! rm -rf ~/M3D-RPN-2.0/dataset/kitti_split1/training
! rm -rf ~/M3D-RPN-2.0/dataset/kitti_split1/validation

In [ ]

%cd ~/M3D-RPN-2.0/
! python dataset/kitti_split1/setup_split.py
! sh dataset/kitti_split1/devkit/cpp/build.sh
! cd lib/nms && make

3. 数据准备

3.1 数据介绍

KITTI 数据集是一个用于自动驾驶场景下的计算机视觉算法测评数据集,由德国卡尔斯鲁厄理工学院(KIT)和丰田工业大学芝加哥分校(TTIC)共同创立。包含场景有市区、乡村和高速公路。本案例使用公开的 KITTI 数据集用于训练测试,共有 14999 张图片,分为训练集 3712 张,验证集 3769 张,测试集 7518 张。示例图片如下图所示:

图 2 - KITTI 数据集示例图片

3.2 数据结构

整个数据集包含图片 images,标签 labels 和相机参数 calib,每个标签文件种包含以下字段:

type---物体类别

truncated---是否截断

occluded---是否遮挡

alpha---观测角

bbox---障碍物2D框

dimension---障碍物的3D大小

location---障碍物的3D底面中心点位置

rotation_y---障碍物的朝向角

最终数据集文件组织结构为:

kitti
└── training
    ├── calib
    ├── image_2
    └── label_2

4. 模型选择

单目 3D 检测提供两种选择:基于 anchor 的方案和 anchor-free 的方案

基于anchor:从图像中估计出 3D 检测框,也无需预测中间的 3D 场景表示,可以直接利用一个区域提案网络,生成给点图像的 3D 锚点。不同于以往与类别无关的 2D 锚点,3D 锚点的形状通常与其语义标签有很强的相关性。

Anchor-free:将 2D 检测方法 CenterNet 扩展到基于图像的 3D 检测器,该框架将对象编码为单个点(对象的中心点)并使用关键点估计来找到它。此外,几个平行的头被用来估计物体的其他属性,包括深度、尺寸、位置和方向。

采用 anchor 的方法使用了 3D 障碍物的平均信息作为先验知识,3D 检测效果实际落地更好,所以我们采用经典的基于 anchor 的方法。在骨干网络部分,我们选择的是 DenseNet,这种网络建立的是前面所有层与后面层的密集连接,实现特征重用,有着省参数,扛过拟合等优点。我们提供了如下不同版本

图 3 - 不同版本 DenseNet

根据单目 3D 检测实时性的要求,这里我们选择了 DenseNet121 作为我们的骨干网络。

5. 模型训练

训练被拆分成了热身配置和主要配置。详细信息可查看 config 中的配置。

首先,在启动模型训练之前,可以修改配置文件中相关内容, 主要包括数据集的地址以及类别数量。对应到配置文件中的位置如下所示:

基础配置
  solver_type: 		'sgd'
  lr:  			0.004
  momentum: 		0.9
  weight_decay: 	0.0005
  max_iter: 		50000
  snapshot_iter: 	10000
  display: 		20
  do_test: 		True
数据集路径
  dataset_test: 'kitti_split1'
  datasets_train:
    name: 		'kitti_split1'
    anno_fmt: 		'kitti_det'
    im_ext: 		'.png'
    scale: 		1

还有一些其他的配置诸如优化器配置标签信息检测器样本等,可以在 config 目录下查看。

  • 启动热身配置训练 (不包含 depth-aware)

In [ ]

! python train.py --conf=kitti_3d_multi_warmup

6. 模型评估

评估默认配置:output/kitti_3d_multi_warmup/conf.pkl

pkl配置
  model: 		"model_3d_dilate"
  solver_type: 		"sgd"
  lr: 			0.004
  momentum: 		0.9
  max_iter: 		50000
  snapshot_iter: 	10000
  do_test: 		"True"
  test_scale: 		512
  crop_size: 		[512, 1760]
  mirror_prob: 		0.5
  distort_prob: 	-1
  dataset_test: 	"kitti_split1"
  datasets_train:
    name: 			"kitti_split1"
    anno_fmt: 			"kitti_det"
    im_ext: 			".png"
    scale: 			1

可视化 pkl 配置文件方法

In [ ]

import pickle
import numpy as np

PKL_PATH = 'output/kitti_3d_multi_warmup/conf.pkl'

f = open(PKL_PATH,'rb')
data = pickle.load(f)
print(data)

In [1]

%cd ~/M3D-RPN-2.0
! python test.py 
  --conf_path output/kitti_3d_multi_warmup/conf.pkl 
  --weights_path output/kitti_3d_multi_warmup/weights/iter50000.0_params.pdparams

预模型 output/kitti_3d_multi_warmup/weights/iter50000.0_params.pdparams 效果

Car

Easy Mod Hard
2D detection 87.27 81.74 66.60
3D BEV 24.93 18.58 16.69
3D detection 19.10 15.69 13.15

Ped

Easy Mod Hard
2D detection 72.47 58.28 50.07
3D BEV 4.12 4.55 3.44
3D detection 3.77 3.45 3.07

Cyclist

Easy Mod Hard
2D detection 63.97 45.97 39.73
3D BEV 11.72 10.16 10.16
3D detection 10.56 10.07 10.07

7. 模型优化

本小节侧重展示在模型迭代过程中优化精度的思路

数据过滤:根据 bbox 可见程度、大小来过滤每个 bbox 标签,根据有无保留 bbox 过滤每张图片,整体平衡前后背景,保证训练的稳定性。

数据增强:主要使用 RandomFlip、Resize 两种数据增强策略

Anchor定义:模型输出

2D anchor定义3D anchor定义

后处理优化: 根据将 3D 相关信息组成 3D 框,投影到图像上得到投影的八点框,取八点最小外接包围框与 2D 预测结果算 IOU,通过不断的调整旋转角 ry 或深度 z,来使得 IOU 最大。此算法利用了 2D 检测的结果要比 3D 检测的结果准确的先验知识,用 2D 框来纠正预测的 3D 属性,来达到优化 3D 定位精度的目的。整体框架如下图所示:

经过调整后,在 car 类前后效果对比如下:

3D detection Easy Mod Hard
优化前 16.57 13.82 12.30
优化后 19.09 15.70 13.15
增量 +2.52 +1.88 +0.85

8. 模型推理

推理过程包括两个步骤:1)导出推理模型 2)执行推理代码

导出推理模型

PaddlePaddle 框架保存的权重文件分为两种:支持前向推理和反向梯度的训练模型和只支持前向推理的推理模型。二者的区别是推理模型针对推理速度和显存做了优化,裁剪了一些只在训练过程中才需要的 tensor,降低显存占用,并进行了一些类似层融合,kernel 选择的速度优化。因此可执行如下命令导出推理模型。

In [2]

! python export_model.py 
  --conf_path output/kitti_3d_multi_warmup/conf.pkl 
  --weights_path output/kitti_3d_multi_warmup/weights/iter50000.0_params.pdparams

生成的推理模型位于 inference 目录,里面包含三个文件,分别为

  • inference.pdmodel
  • inference.pdiparams
  • inference.pdiparams.info。

其中 inference.pdmodel 用来存储推理模型的结构, inference.pdiparams 和 inference.pdiparams.info 用来存储推理模型相关的参数信息。

结果保存在 inference_result 目录下。

In [4]

! python infer.py 
  --conf_path output/kitti_3d_multi_warmup/conf.pkl

9. 模型可视化

In [ ]

! python vis.py

10. 模型部署

使用飞桨原生推理库 paddle-inference,用于服务端模型部署

总体上分为三步:

  1. 创建 PaddlePredictor,设置所导出的模型路径
  2. 创建输入用的 PaddleTensor,传入到 PaddlePredictor 中
  3. 获取输出的 PaddleTensor ,将结果取出
#include "paddle_inference_api.h"
 
// 创建一个 config,并修改相关设置
paddle::NativeConfig config;
config.model_dir = "xxx";
config.use_gpu = false;
// 创建一个原生的 PaddlePredictor
auto predictor =
      paddle::CreatePaddlePredictor<paddle::NativeConfig>(config);
// 创建输入 tensor
int64_t data[4] = {1, 2, 3, 4};
paddle::PaddleTensor tensor;
tensor.shape = std::vector<int>({4, 1});
tensor.data.Reset(data, sizeof(data));
tensor.dtype = paddle::PaddleDType::INT64;
// 创建输出 tensor,输出 tensor 的内存可以复用
std::vector<paddle::PaddleTensor> outputs;
// 执行预测
CHECK(predictor->Run(slots, &outputs));
// 获取 outputs ...

更多内容详见 > C++ 预测 API介绍

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>