3D cloud point detection

data

点云数据的维度是(N * 4),N是point数量,每帧点云的N可能是变化的,4是xyz和反射率
dataset中bbox一般用7个数表示(x,y,z,w,l,h,theta)

pointNet

pointNet是用于点云分类或分割任务的网络。
点云与图像最大的区别是图像是结构化的数据,而点云的点是非结构化的无序的数据点,换言之点云的点的顺序是可以随意变换的。因此本文的思想是,找到一种对称函数,对点云顺序不敏感,无论点云顺序如何,经过对称函数映射后结果趋向统一,而不能影响最终的结果。

无序输入的对称函数 (MLP)

为了使模型对输入排列保持不变,存在三种策略:1)将输入排序为规范顺序; 2)将输入视为一个序列来训练RNN,但通过各种排列来扩充训练数据; 3)使用一个简单的对称函数来聚合来自每个点的信息。这里,一个对称函数将 n 个向量作为输入,并输出一个对输入顺序不变的新向量。
虽然排序听起来像是一个简单的解决方案,但在高维空间中实际上不存在稳定的排序。因此,排序并不能完全解决排序问题,并且随着排序问题的持续存在,网络很难学习从输入到输出的一致映射。

使用 RNN 的想法将点集视为顺序信号,并希望通过使用随机排列的序列训练 RNN,RNN 将变得对输入顺序保持不变。然而,在“OrderMatters”中,作者已经表明顺序确实很重要,不能完全省略。虽然 RNN 对小长度(几十个)序列的输入排序具有相对较好的鲁棒性,但很难扩展到数千个输入元素,这是点集的常见大小。根据经验,我们还表明基于 RNN 的模型的性能不如我们提出的方法。

我们的想法是在点集的元素上,用对称函数近似一般函数:f ({x 1 , . . . , x n }) ≈ g(h(x 1 ), . . . , h(x n )) g是对称函数。根据经验,我们的基本模块非常简单:通过多层感知器网络MPL近似 h(x),通过组合一个单变量函数h(x)的和一个最大池化函数近似 g(x) 。通过实验发现这很有效。通过一个 组h(x) ,我们可以学习到多个f(x)来捕捉集合的不同特征。虽然我们的关键模块看起来很简单,但它具有有趣的属性,并且可以在一些不同的应用程序中实现强大的性能。
在这里插入图片描述
注意上面的MLP方向,如上图第一个MLP,3 → 64,这个相当于三个点的全连接,仅关联点内不同的数值!
如果是分类任务,可以直接拿上图的1024特征进行分类了!

联合校准网络(T-net)

在后续的改进算法中,这个结构被证明不太重要,此处省略介绍

分割任务

分割需要二维特征,因此分割把1024特征复制N份以后,再拼接上原来的N * 64维特征(左边的MLP输出),变为N * 1088的维度,这样该特征中既包含了每个点的独特的特征,也包含了全局信息。
在这里插入图片描述
完整结构如下,重点看如下的分割分支,经过两次MLP后获得m * n的特征图,用于分割任务,n就是输入点数,然后就可以奇回归得到每个点所属的类别了。
在这里插入图片描述
参考易懂视频: pointNet讲解参考

VoxelNet

利用了稀疏点云的结构特性,直接在稀疏的3D点上进行操作,并通过高效的并行处理体素网格来获得性能的提升。
将点云数据量化到一个个Voxel里,常见的有VoxelNet 和SECOND
在这里插入图片描述

voxel Stacked Voxel Feature Encoding

输入 T * N * 7 , T:非空体素数量,N每个体素内的点数(大于N的随机抽样N个,小于N的补零),7是点云点的维度,四个数加相对偏移
最后输出 T * C,然后映射到空间位置,获得稀疏的向量 C D H W
结构,多个VFE(下图)提取特征,最后经过一个全连接,然后映射C D H W
在这里插入图片描述
具体操作是这样的,首先将点云数据通过一个全连接层,得到每个点对应的特征(Point-wise Feature),得到的特征利用Element-wise maxpool得到局部聚合特征(Locally Aggregated Feature),局部聚合特征和每个点的特征拼接起来,作为下一个VFE层的输入。代码如下(其中FCN为全连接层):

# Voxel Feature Encoding layer
class VFE(nn.Module):

    def __init__(self,cin,cout):
        super(VFE, self).__init__()
        assert cout % 2 == 0
        self.units = cout // 2
        self.fcn = FCN(cin,self.units)

    def forward(self, x, mask):
        # point-wise feauture
        pwf = self.fcn(x)
        #locally aggregated feature
        laf = torch.max(pwf,1)[0].unsqueeze(1).repeat(1,cfg.T,1)
        # point-wise concat feature
        pwcf = torch.cat((pwf,laf),dim=2)
        # apply mask
        mask = mask.unsqueeze(2).repeat(1, 1, self.units * 2)
        pwcf = pwcf * mask.float()

        return pwcf

经过计算后,需要将原来空点的特征置0(体素内点数不够补零的情况),这样就不会影响后续计算,也即上述代码中的mask参数的作用。
从上图和上述代码可以看到VFE的输入是T * N * Cin,输出是T * N * Cout,VFE重复多次,第一次的时候,Cin=7,最后一层时,经过全连接,最终的维度变为,T * C,这样就可以根据体素位置映射为C D H W
VFE设计分析:上图中voxel中有三个点(3*7),经过全连接后变成3 * C,这里是点内特征融合(仍然对应上图的三种颜色),经过一个池化,repeat,拼接后,这样就相当于进行了一次点之间的特征融合。

Convolutional Middle Layers

经过FLN之后,输出变为128×10×400×352(128代表特征维度,后面是voxel的个数)。

用ConvMD ​代表一个M维的卷积层。三维的时候,k=(k,k,k)卷积核是3维的

经过Conv3D(128,64,3,(2,1,1),(1,1,1)),Conv3D(64,64,3,(1,1,1),(0,1,1)),Conv3D(64,64,3,(2,1,1),(1,1,1))之后,输出变为64×2×400×352,经过reshape,变为128×400×352。所以输入RPN的feature map的大小就是128×400×352.

RPN

RPN网路的输入是通过 Convolutional Middle Layers出来的特征图,3个全卷积的block,每次都是下采样,如下图所示,将后两个上采样,然后与第一个block拼接,将得到的拼接特征用于目标回归。此处RPN类似FPN的作用。
在这里插入图片描述

SECOND

SECOND用到了稀疏卷积

卷积本质上是矩阵运算,稀疏卷积就是稀疏的矩阵运算,如何对稀疏的矩阵进行高效的运算,一种方法就是把稀疏的矩阵中有效的数字提取出来,把稀疏的大矩阵变成稠密的小矩阵再进行运算,大小之间如何转换,方法是哈希映射。
稀疏卷积,讲的真的不错:稀疏卷积通俗理解

pointPillars

PointPillars的最大贡献是在VoxelNet中Voxel的基础上提出了一种改进版本的点云表征方法Pillar,可以将点云转换成伪图像,进而通过2D卷积实现目标检测。PointPillars整个网络结构分为三个部分:最大的创新点是PFN部分

  • Pillar Feature Net:将输入的点云转换为稀疏的Pseudo image
  • Backbone:处理Pseudo image得到高层的特征
  • Detection Head:检测和回归3D框
    在这里插入图片描述

Point Feature Net

在这里插入图片描述

输入的尺度是如何变化的

P * N * D(30000 x 20 x 9)->P * N * C(30000 x 20 x 64)-> P * C(30000 * 64)->H * W * C(512 * 512 * 64)

其中P代表选取的Pillar数量,N是每个Pillar存储的最大点云数量,D是点云的属性,P和N都是超参数,需要根据激光雷达的线束和Pillar中点云数量设置,论文中使用取P=30000,N=20。如果某个Pillar中的点云数量大于20个,则多余的丢弃,若少于20个,则用0 padding补充。
最后一步转换P * C(30000 * 64)->H * W * C(512 * 512 * 64),也叫point scatter,把pillar平铺到bev图上。具体细节再看!

Stacked Pillars是如何变成Learned Features的?

上述的转换过程非常简单,可以表述为PND(30000 x 20 x 9)->PNC(30000 x 20 x 64)-> P*C(30000 * 64),对应的处理流程可参考代码如下,先是经过一个Linear+BN+ReLU,然后通过MaxPooling操作将每个Pillar中最大响应的点云提取出来。上述中的Linear+BN+ReLU可以堆叠,论文使用了最简单的方法
在这里插入图片描述

Learned Features 是如何变化为Pseudo Images? point scatter

是通过Scatter运算实现的。在PointPillars网络结构图中从Point cloud构造Stacked Pillars的过程中,记录了每个Pillar对应的x,y坐标,也就是上图中的Pillar Index,其维度是P*2,P是Pillar的数量,2是x和y对应的坐标。在Learned Features构造Pseudo Images的时候根据Pillar Index,将Pillar填充到对应的Pseudo Images上。
这一步的方法比较简单,就是通过每个点的Pillar索引值将上一步生成的(C,P)张量转换回其原始的Pillar坐标用来创建大小为(C,H,W)的伪图像。这里需要解释一下伪图像的高度H和宽度W是怎么来的:在第一步对点云进行Pillar划分的时候会设置XY平面上点云坐标的范围和每个Pillar的大小,假设X轴的范围是[0,69.12],Y轴的范围是[-39.68,39.68],每个Pillar的大小是``0.16x0.16,那么以X轴表示宽,Y轴表示高,一个Pillar表示一个像素的话,那么这个伪图像的宽W = (69.12 - 0) / 0.16 = 432,高H = (39.68 -(-39.68)) / 0.16 = 496。如果有落到同一个pillar的多个点怎么办?均值吗?需再研究

点云属性为啥是9

在这里插入图片描述

centerpoint

centerpoint是基于heatmap的3d检测算法,前面的backbone可以使用voxelNet或pointpillar等,heat map后连接检测head,分别回归目标中心点、长宽高、角度(只有水平方向上的yaw角)、以及速度。稍微有点特殊之处主要有两方面

  • 一个是在普通的heat map head上,又添加了一个二阶段检测结构
  • 回归目标的速度,用来做目标跟踪
    在这里插入图片描述

二阶段

二阶段金回归目标的置信度和3d box(中心点和长宽高,待确认),二阶段的实现方式比较交单,一阶段有了每个目标框的具体位置,二阶段根据目标框的边框中心点位置(如上图,每个框的四个点),到backbone的最后一层的对应位置提取相应位置的特征,四个点的位置是精细化的,backbone最后一层特征是特征点格式,因此使用了双线性插值的方法,取出特征送入二阶段网络。
二阶段具体实现如下,每帧点云图像取N个目标进行二阶段回归,N固定。拿到特征后,进行了一次reshape(代码有注释),目标数N reshape到batchsize里,这样做的好处有二,第一实现较简单,第二所有目标(不同帧的目标以及相同帧的不同目标)共享参数,减小了参数量。

def forward(self, batch_dict, training=True):
        """
        :param input_data: input dict
        :return:
        """
        batch_dict['batch_size'] = len(batch_dict['rois'])
        if training:
            targets_dict = self.assign_targets(batch_dict)
            batch_dict['rois'] = targets_dict['rois']
            batch_dict['roi_labels'] = targets_dict['roi_labels']
            batch_dict['roi_features'] = targets_dict['roi_features']
            batch_dict['roi_scores'] = targets_dict['roi_scores']

        # RoI aware pooling
        if self.add_box_param:
            batch_dict['roi_features'] = torch.cat([batch_dict['roi_features'], batch_dict['rois'], batch_dict['roi_scores'].unsqueeze(-1)], dim=-1)
		# reshape,将目标数N reshape到batchsize里,这样后续实现比较简单,另外所有目标共享参数,减小参数量
        pooled_features = batch_dict['roi_features'].reshape(-1, 1,
            batch_dict['roi_features'].shape[-1]).contiguous()  # (BxN, 1, C)

        batch_size_rcnn = pooled_features.shape[0]
        pooled_features = pooled_features.permute(0, 2, 1).contiguous() # (BxN, C, 1)

        shared_features = self.shared_fc_layer(pooled_features.view(batch_size_rcnn, -1, 1))
        rcnn_cls = self.cls_layers(shared_features).transpose(1, 2).contiguous().squeeze(dim=1)  # (B, 1 or 2)
        rcnn_reg = self.reg_layers(shared_features).transpose(1, 2).contiguous().squeeze(dim=1)  # (B, C)

centerPoint的跟踪任务

在这里插入图片描述在这里插入图片描述

head回归目标在x轴和y轴的方向上的速度,用来辅助跟踪任务。

  • 如何回归速度,训练的时候,不仅输入当前帧的点云,还有前一帧的点云以及他们之间的时间差,学习两帧点云的差异,加上时间差,这样就可以学习目标的平均速度
  • 有了速度如何跟踪,推理的时候,同样输入上一帧点云和时间差,这样网络输出了每个目标的速度、位置等,根据目标的位置、速度以及时间差,可以反推目标在上帧点云的位置,然后再和上一帧的目标进行匹配(贪心算法),完整跟踪。

point transformer

point transformer和上面介绍的pointNet属于一个流派,基于点对点云进行处理,这种结构仅能用于分类和分割任务,不能用于目标检测。
自注意力机制适合处理点云的原因:

  • 排列不变性:3D点云是无序的,即点的顺序不应影响最终的处理结果。自注意力机制天然具有排列不变性,因为它通过对所有点对的关系进行建模来处理输入,而不依赖于任何特定的输入顺序。
  • 捕捉全局上下文:点云数据通常覆盖了3D空间中的对象或场景,理解这些数据需要捕捉点之间复杂的空间关系。自注意力机制能够有效地捕捉这些关系,因为它为每对点赋予一个注意力权重,这反映了它们之间的相对重要性。

Point Transformer Layer

点云中每个点都和周围最近的N个点进行attention,这里的self attention和之前了解的不太一样?
在这里插入图片描述
Χ(i)是与x_i临近的N个点的集合,也就是要与临近的N个点进行特征融合,上式中的几个函数主要是mlp或linear,如下图所示。
在这里插入图片描述

整体结构

结构如下图,可以进行分类或分割,上图分割,下图分类。
分割模型,整体类似于unet的对称结构,先上采样,然后进行下采样,与cnn不同,这里上下采样所采用的结构分别是transition up和transition down。
在这里插入图片描述

point transformer v2

也是点云分割网络

lidar-rcnn

二阶段检测网络,可以后接在任何检测网络后。以pointNet作为主干网络。它的主干部分由5个mlp和一个max-pooling组成,不论是参数量还是计算量都非常小。
类似于Faster RCNN的ROI Pooling,我们直接去原始点云上抠取proposal内的点云xyz,并将其旋转平移至proposal坐标系(图2),之后将它们输入进PointNet,由分类和回归两个loss来监督网络的训练。值得一提的是一个3D框是有7个自由度的(框中心xyz、长宽高、角度),所以我们的回归目标也是7个变量。我们二阶段网络的输入是原始点云,并不依赖于一阶段所训练的特征,所以它可以很灵活地适用于各种3D检测器上。
在这里插入图片描述

代码调试

model输入
输入子点云和pre_bbox,实际上pre_bbox在网络中没有使用,上面的模型图可以看出实际只有1个输入
子点云pts(256 12 1024)
pre_bbox(256, 7)

model输出
分类logits(256, 2),如果是多分类,2替换为类别加1,以及centers(3个数)、sizes(3个数)、heading(角度,1个数)

datasets
datasets输入:proposal、gt(与proposalIOU最大或最近)、点云全图
点云crop是在data_processor数据预处理脚本中进行的,调用extract_points(),默认外扩三米。
crop获得子点云后,还会添加上frame_idx(时序)信息,如果没有时序,无需进行该处理。
该脚本还会为每个一阶段预测proposal匹配一个gt_box,通过iou匹配,生成正负样本标签。

datasets里会根据IOU再进行relabel,生成二阶段的类比标签,例如如果只有一类,标签只有0 1,表示正负样本。
datesets点云处理(process_pcd):还会根据proposal坐标,把点云坐标平移到proposal中心为圆点,只平移x/y坐标。还会根据proposal角度旋转点云,这样二阶段只需要预测角度的差值(gt和一阶段proposal角度的差值)即可。此外还会计算点云点到proposal六个平面的距离,拼接到点云后面,组成12维数据。

快速3D线段检测是一种从无组织点云中提取线段信息的方法。无组织点云是由离散的3D点构成的集合,没有特定的排列顺序。线段检测能够从这些点中提取出与线段相关的信息,如线段的位置、方向和长度。 在快速3D线段检测算法中,首先需要对点云数据进行预处理,例如去除噪声点、滤波和下采样等操作,以提高后续线段检测的效率和准确性。然后,采用适当的方法对预处理后的点云进行分割,将点云划分为具有相似特征的子集。这个过程可以使用聚类方法或其他分割算法来实现。 接下来,通过计算每个分割子集的特征值,如点云的法线方向、曲率或密度信息,来判断是否存在线段。利用这些特征值,可以通过阈值或其他准则来筛选出可能表示线段的子集。对于筛选出的子集,可以采用拟合线模型的方法,如最小二乘法或RANSAC算法,来拟合出具体的线段参数,如起点、终点和方向等。 最后,通过对拟合线模型进行验证和评估,可以排除拟合误差较大的线段,提高线段拟合的准确性。例如,可以计算残差或拟合误差,来评估线段的质量,并对不满足一定阈值的线段进行排除。 总之,快速3D线段检测从无组织点云中提取线段信息的过程包括预处理、分割、特征提取、拟合和验证等多个步骤。通过这些步骤的组合,可以实现高效准确地检测出无组织点云中的线段信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值