动手学无人驾驶(4):基于激光雷达点云数据3D目标检测

上一篇文章《动手学无人驾驶(3):基于激光雷达3D多目标追踪》介绍了3D多目标追踪,多目标追踪里使用的传感器数据为激光雷达Lidar检测到的数据,本文就介绍如何基于激光雷达点云数据进行3D目标检测。

论文地址:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》

Github项目地址:https://github.com/sshaoshuai/PointRCNN

KITTI数据集百度云下载地址:百度网盘 请输入提取码 提取码: ct4q


在介绍论文前,大家可以先看看论文作者此前分享的3D目标检测报告,报告里对论文有详细介绍B站地址为:基于点云场景的三维物体检测算法及应用_哔哩哔哩_bilibili

基于点云场景的三维物体检测算法及应用

目录

1.KITTI数据集

2.PointRCNN

3.目标检测结果

参考资料


1.KITTI数据集

KITTI目标检测数据集(http://www.cvlibs.net/datasets/kitti/eval_object.php?obj_benchmark=3d)中共包含7481张训练图片和7518张测试图片,以及与之相对应的点云数据,label文件以及calib文件。这里以训练集编号为000008的场景为例介绍KITTI数据集中calib和label文件所含信息。

                                                                   (training/image_2/000008.png)

Calib文件:在KITTI数据采集过程中使用到了摄像头和激光雷达,采集到的数据坐标分别为摄像头坐标和激光雷达坐标下的测量值,这时就需要calib文件将摄像头与激光雷达进行标定,calib文件通过txt格式来保存。

velodyne:velodyne中存储着激光雷达点云采集到数据,数据以2进制格式存储(.bin)点云数据存储格式为(N,4)。N为激光线束反射点个数,4代表着:(x,y,z,r),分别返回在3个坐标轴上的位置和反射率

label文件:label文件为标注数据,以txt格式保存,000008.txt中标注的object内容如下:

Car 0.88 3 -0.69 0.00 192.37 402.31 374.00 1.60 1.57 3.23 -2.70 1.74 3.68 -1.29

Car 0.00 1 2.04 334.85 178.94 624.50 372.04 1.57 1.50 3.68 -1.17 1.65 7.86 1.90

Car 0.34 3 -1.84 937.29 197.39 1241.00 374.00 1.39 1.44 3.08 3.81 1.64 6.15 -1.31

Car 0.00 1 -1.33 597.59 176.18 720.90 261.14 1.47 1.60 3.66 1.07 1.55 14.44 -1.25

Car 0.00 0 1.74 741.18 168.83 792.25 208.43 1.70 1.63 4.08 7.24 1.55 33.20 1.95

Car 0.00 0 -1.65 884.52 178.31 956.41 240.18 1.59 1.59 2.47 8.48 1.75 19 -1.25

DontCare -1 -1 -10 800.38 163.67 825.45 184.07 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 859.58 172.34 886.26 194.51 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 801.81 163.96 825.20 183.59 -1 -1 -1 -1000 -1000 -1000 -10

DontCare -1 -1 -10 826.87 162.28 845.84 178.86 -1 -1 -1 -1000 -1000 -1000 -10

关于label文件的理解可以参考下面的代码:

class Object3d(object):''' 3d object label '''def __init__(self, label_file_line):data = label_file_line.split(' ')data[1:] = [float(x) for x in data[1:]]# extract label, truncation, occlusionself.type = data[0] # 'Car', 'Pedestrian', ...self.truncation = data[1] # truncated pixel ratio [0..1]self.occlusion = int(data[2]) # 0=visible, 1=partly occluded, 2=fully occluded, 3=unknownself.alpha = data[3] # object observation angle [-pi..pi]# extract 2d bounding box in 0-based coordinatesself.xmin = data[4] # leftself.ymin = data[5] # topself.xmax = data[6] # rightself.ymax = data[7] # bottomself.box2d = np.array([self.xmin,self.ymin,self.xmax,self.ymax])# extract 3d bounding box informationself.h = data[8] # box heightself.w = data[9] # box widthself.l = data[10] # box length (in meters)self.t = (data[11],data[12],data[13]) # location (x,y,z) in camera coord.self.ry = data[14] # yaw angle (around Y-axis in camera coordinates) [-pi..pi]def print_object(self):print('Type, truncation, occlusion, alpha: %s, %d, %d, %f' % \(self.type, self.truncation, self.occlusion, self.alpha))print('2d bbox (x0,y0,x1,y1): %f, %f, %f, %f' % \(self.xmin, self.ymin, self.xmax, self.ymax))print('3d bbox h,w,l: %f, %f, %f' % \(self.h, self.w, self.l))print('3d bbox location, ry: (%f, %f, %f), %f' % \(self.t[0],self.t[1],self.t[2],self.ry))

2.PointRCNN

3D目标检测模型PointRCNN借鉴了PointNet++和RCNN的思想,提出了自底向上的生成和调整候选检测区域的算法,网络结构如下图所示:

PointRCNN的网络结构分为两个阶段:第一阶段自底向上生成3D候选预测框;第二阶段在规范坐标中对候选预测框进行搜索和微调,得到更为精确的预测框作为检测结果。

第一阶段:对3D点云数据进行语义分割和前背景划分,生成候选预测框,有如下三个关键步骤:

  • 点云特征提取:通过PointNet++对点云数据进行编码和解码,提取点云特征向量。

  • 前景点分割:根据提取的点云特征向量,使用focal loss区分前景点和背景点。focal loss能有效地平衡前景点和背景点比例失衡问题,从而得到更为准确的分类效果。

  • 生成候选框:采用候选框箱模型(bin)的方法,将前背景点分割信息生成预测候选框。

举例来说,将候选框定义为参数(x,y,z,h,w,l,θ)表征的空间中的箱体,其中(x,y,z)为箱体中心坐标,( h,w,l)为箱体在中心坐标方向上的大小,θ为鸟瞰视角上(y方向从上往下看)箱体在x-z平面的角度。

bin的执行方式为:先根据前景点的分割信息粗分其所属的箱体;再在箱体内部对其做回归,得到箱体参数作为预测框;最后对预测框做NMS(Non-Max Suppress,非极大值抑制),得到最终预测候选框。

第二阶段:在规范坐标中微调候选预测框,获得最终的检测结果,有如下四个关键部分:

  • 区域池化:对候选框内每个点的特征进行池化。

  • 坐标转化:为了更好地获取局部信息,需要将多个候选区域中的前景点坐标(同一个坐标系)转化为局域坐标系中的规范坐标(以预测框为中心点的多个坐标系),如下图所示:

  • 特征编码:将规范坐标时丢失的深度信息、规范后的坐标信息、前后背景语义信息等经过多层感知机提取特征,作为每个点的编码特征。

  • 微调预测框:经过上一步编码后的特征,经PointNet++网络进行特征提取,最后回归得到局部坐标系下的3D预测框。

PointRCNN网络代码如下:

class PointRCNN(nn.Module):def __init__(self, num_classes, use_xyz=True, mode='TRAIN'):super().__init__()assert cfg.RPN.ENABLED or cfg.RCNN.ENABLEDif cfg.RPN.ENABLED:self.rpn = RPN(use_xyz=use_xyz, mode=mode)if cfg.RCNN.ENABLED:rcnn_input_channels = 128  # channels of rpn featuresif cfg.RCNN.BACKBONE == 'pointnet':self.rcnn_net = RCNNNet(num_classes=num_classes, input_channels=rcnn_input_channels, use_xyz=use_xyz)elif cfg.RCNN.BACKBONE == 'pointsift':pass else:raise NotImplementedErrordef forward(self, input_data):if cfg.RPN.ENABLED:output = {}# rpn inferencewith torch.set_grad_enabled((not cfg.RPN.FIXED) and self.training):if cfg.RPN.FIXED:self.rpn.eval()rpn_output = self.rpn(input_data)output.update(rpn_output)# rcnn inferenceif cfg.RCNN.ENABLED:with torch.no_grad():rpn_cls, rpn_reg = rpn_output['rpn_cls'], rpn_output['rpn_reg']backbone_xyz, backbone_features = rpn_output['backbone_xyz'], rpn_output['backbone_features']rpn_scores_raw = rpn_cls[:, :, 0]rpn_scores_norm = torch.sigmoid(rpn_scores_raw)seg_mask = (rpn_scores_norm > cfg.RPN.SCORE_THRESH).float()pts_depth = torch.norm(backbone_xyz, p=2, dim=2)# proposal layerrois, roi_scores_raw = self.rpn.proposal_layer(rpn_scores_raw, rpn_reg, backbone_xyz)  # (B, M, 7)output['rois'] = roisoutput['roi_scores_raw'] = roi_scores_rawoutput['seg_result'] = seg_maskrcnn_input_info = {'rpn_xyz': backbone_xyz,'rpn_features': backbone_features.permute((0, 2, 1)),'seg_mask': seg_mask,'roi_boxes3d': rois,'pts_depth': pts_depth}if self.training:rcnn_input_info['gt_boxes3d'] = input_data['gt_boxes3d']rcnn_output = self.rcnn_net(rcnn_input_info)output.update(rcnn_output)elif cfg.RCNN.ENABLED:output = self.rcnn_net(input_data)else:raise NotImplementedErrorreturn output

3.目标检测结果

介绍完KITTI数据集和PointRCNN模型后,现在用作者预训练好的模型进行目标预测。

1)首先是准备数据,从百度网盘里下载KITTI数据后按如下方式排放数据:

PointRCNN
├── data
│   ├── KITTI
│   │   ├── ImageSets
│   │   ├── object
│   │   │   ├──training
│   │   │      ├──calib & velodyne & label_2 & image_2 
│   │   │   ├──testing
│   │   │      ├──calib & velodyne & image_2
├── lib
├── pointnet2_lib
├── tools

2)将预训练好的模型放入/tools文件夹下,执行如下命令,此时会对验证集进行预测:

python eval_rcnn.py --cfg_file cfgs/default.yaml --ckpt PointRCNN.pth --batch_size 1 --eval_mode rcnn --set RPN.LOC_XZ_FINE False

预测结果如下,这里预测的平均准确率略小于原作者预测的。

# 原作者预测结果
Car AP@0.70, 0.70, 0.70:
bbox AP:96.91, 89.53, 88.74
bev  AP:90.21, 87.89, 85.51
3d   AP:89.19, 78.85, 77.91
aos  AP:96.90, 89.41, 88.54
# 使用预训练模型输出的结果
Car AP@0.70, 0.70, 0.70:
bbox AP:90.3697, 78.9661, 76.0439         
bev  AP:88.8698, 75.5572, 69.7311
3d   AP:83.3413, 66.9504, 60.1443
aos  AP:90.30, 78.51, 75.45

参考资料

飞桨火力全开,重磅上线3D模型:PointNet++、PointRCNN!

PointRCNN地址:https://github.com/sshaoshuai/PointRCNN

论文:《PointRCNN: 3D Object Proposal Generation and Detection from Point Cloud》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一步步编写操作系统 37 一级页表与虚拟地址2

接上节,分页机制是建立在分段机制之上,与其脱离不了干系,即使在分页机制下的进程也要先经过逻辑上的分段才行,每加载一个进程,操作系统按照进程中各段的起始范围,在进程自己的4GB虚拟地址空间中寻找可有空间…

PointNet:3D点集分类与分割深度学习模型

之前的一篇博客《动手学无人驾驶(4):基于激光雷达点云数据3D目标检测》里介绍到了如何基于PointRCNN模型来进行3D目标检测,作者使用的主干网是PointNet,而PointNet又是基于PointNet来实现的。今天写的这篇博客就是对Po…

计算机视觉那些事儿(1):基本任务

本文主要介绍深度学习在计算机视觉领域(Computer vision)基本任务中的应用,包括分类、检测、分割(语义与实体)。 目录 引言 分类(Classification) 目标检测(Object Detection) T…

一步步编写操作系统 38 一级页表与虚拟地址3

接上,页是地址空间的计量单位,并不是专属物理地址或线性地址,只要是4KB的地址空间都可以称为一页,所以线性地址的一页也要对应物理地址的一页。一页大小为4KB,这样一来,4GB地址空间被划分成4GB/4KB1M个页&a…

《Python编程:从入门到实践》速查表

本文是Python畅销书《Python:从入门到实践》速查表。 随书配套视频观看地址:https://www.bilibili.com/video/av35698354 目录 1.Overview 2.Lists 3.Dictionaries 4.If and While Loops 5.Functions 6.Classes 7.Files and Exceptions 8.Testin…

一步步编写操作系统 39 二级页表1

前面讲述了页表的原理,并以一级页表做为原型讲述了地址转换过程。既然有了一级页表,为什么还要搞个二级页表呢?理由如下: 一级页表中最多可容纳1M(1048576)个页表项,每个页表项是4字节&#xf…

PointNet++详解与代码

在之前的一篇文章《PointNet:3D点集分类与分割深度学习模型》中分析了PointNet网络是如何进行3D点云数据分类与分割的。但是PointNet存在的一个缺点是无法获得局部特征,这使得它很难对复杂场景进行分析。在PointNet中,作者通过两个主要的方法…

一步步编写操作系统 40 内存分页下用户程序与操作系统的关系

分页的第一步要准备好一个页表,我们的页表是什么样子呢?现在我们要设计一个页表啦。 设计页表其实就是设计内存布局,不过在规划内存布局之前,我们需要了解用户进程与操作系统之间的关系。 前面讲保护模式时,我们知道…

一步步编写操作系统 41 快表tlb 简介

分页机制虽然很灵活,但您也看到了,为了实现虚拟地址到物理地址的映射,过程还是有些麻烦的。先要从CR3寄存器中获取页目录表物理地址,然后用虚拟地址的高10位乘以4的积做为在页目录表中的偏移量去寻址目录项pde,从pde中…

50个最有用的Matplotlib数据分析与可视化图

本文介绍了数据分析与可视化中最有用的50个数据分析图,共分为7大类:Correlation、Deviation、RankIng、Distribution、Composition、Change、Groups 原文链接:https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-t…

视觉SLAM十四讲(1):预备知识

最近在学习高翔博士的《视觉SLAM十四讲》(第二版),算是初学本书,配套资源还算蛮丰富的,有代码(第一版和第二版都有),B站上也有高翔博士对第一版录制的讲解视频,真的是很贴…

一步步编写操作系统 43 汇编语言和c语言的理解

也许有的同学喜欢用汇编语言来实现操作系统,觉得用汇编来写程序似乎更简单直接,可控性比较强,有种“一切尽在掌握”的赶脚。而用c语言实现操作系统这件事,虽然轻松很多,但似乎隐约感觉到有些慌张。因为虽然c语言相对来…

视觉SLAM十四讲(2):初识SLAM

这一讲主要介绍视觉SLAM的结构,并完成第一个SLAM程序:HelloSLAM。 目录 2.1 小萝卜的例子 单目相机 双目相机 深度相机 2.2 经典视觉SLAM框架 2.3 SLAM问题的数学表述 2.4 编程实践 Hello SLAM 使用cmake 使用库 【高翔】视觉SLAM十四讲2.1 小…

一步步编写操作系统 44 用c语言编写内核1

先来个简单的,欢迎我们神秘嘉宾——main.c。这是我们第一个c语言代码。 1 int main(void) { 2 while(1); 3 return 0; 4 }它没法再简单啦,简单的程序似乎能帮助咱们更容易的理解所学的知识,哈哈,我说的是似乎,其实&…

从零实现一个3D目标检测算法(1):3D目标检测概述

本文是根据github上的开源项目:https://github.com/open-mmlab/OpenPCDet整理而来,在此表示感谢,强烈推荐大家去关注。使用的预训练模型也为此项目中提供的模型,不过此项目已更新为v0.2版,与本文中代码略有不同。 本文…

一步步编写操作系统 45 用c语言编写内核2

在linux下用于链接的程序是ld,链接有一个好处,可以指定最终生成的可执行文件的起始虚拟地址。它是用-Ttext参数来指定的,所以咱们可以执行以下命令完成链接: ld kernel/main.o -Ttext 0xc0001500 -e main -o kernel/kernel.bin …

使用OpenCV库快速求解相机内参

本文主要介绍如何使用OpenCV库函数求解相机内参。具体可查阅官网:https://docs.opencv.org/master/dc/dbb/tutorial_py_calibration.html。 关于相机内参的求解还有很多其它的工具,如使用MATLAB求解会更方便,直接调用MATLAB中的APP即可。 1.背…

一步步编写操作系统 46 用c语言编写内核3

再把上节代码贴出来, 1 //int main(void) { 2 int _start(void) { 3 while(1); 4 return 0; 5 }有没有同学想过,这里写一个_start函数,让其调用main函数如何?其实这是可以的,main函数并不是第一个函数,它实…

一步步编写操作系统 47 48 二进制程序运行方式

操作系统并不是在功能上给予用户的支持,这种支持是体现在机制上。也就是说,单纯的操作系统,用户拿它什么都做不了,用户需要的是某种功能。而操作系统仅仅是个提供支持的平台。 虽然我们是模仿linux来写一个黑屏白字的系统&#x…

百度顶会论文复现(1):课程概述

最近百度推出了一款重磅课程《全球顶会论文作者,28天免费手把手带你复现顶会论文》。这个课程真的是很硬核的课程,这里简单记录下自己的学习过程。 文章目录1. 课程设计思路和安排2. 课程大纲1. 课程设计思路和安排 课程设计思路如下,共分为…