2DPASS激光雷达点云语义分割简介

导读

香港中文大学深圳深度比特实验室提出了一种基于二维图像先验辅助的激光雷达点云语义分割 (2DPASS)。不同于先前的多模态方法(训练和推理阶段均需要成对的图像和点云数据作为输入),该方法仅在训练阶段利用额外的图像数据,从相机数据中获取更丰富的语义和结构信息,并将其提炼蒸馏至三维语义分割网络中。在测试阶段,该方法可实现实时感知,无需图像数据输入,即可实现又快又准的三维语义分割,并在多个大型语义分割比赛(SemanticKITTI单帧、多帧和Nuscenes)都达到了最先进的水平
在这里插入图片描述论文地址:
https://arxiv.org/pdf/2207.04397.pdf
GitHub:
https://github.com/yanx27/2DPASS

一、研究动机

随着越来越多的方法同时使用相机和激光雷达传感器捕获互补信息,通过多模态数据融合的语义分割已经实现了巨大的进步。这些基于融合的方法首先将点云投影到图像平面上来建立三维点和二维像素之间的映射,基于该映射,这些模型将相应的图像信息融合到点云,并获得最终的语义分割结果。然而,上述方法却有以下不可避免的局限性:

1.在一些情况下,相机和激光雷达之间的视野不同,而无法建立点到像素的映射关系。如SemanticKITTI数据集中相机的视野仅占据激光雷达视野的一小部分(如下图),这极大地限制了基于融合的方法的应用。
2.基于融合的方法往往需要消耗更多的计算资源,因为它们在训练和推理阶段需要同时处理图像和点云数据,这为自动驾驶应用的实时性带来了巨大的挑战。
在这里插入图片描述

二、方法

为了解决上述两个问题,该团队提出了一种基于二维图像先验的训练方案,2D Priors Assisted Semantic Segmentation (2DPASS),以促进三维点云上的表征学习。2DPASS具有以下的优势:

通用性:2DPASS不限制所使用的三维语义分割模型的类型,可应用在多种已有的模型上提升其效果。
高效性:额外的二维图像仅在训练阶段使用,在部署模型进行推理时,2DPASS仅使用三维点云作为输入。
有效性:即使激光雷达与图片仅有一小部分的重叠区域,2DPASS依旧能有效地提升模型,并在两大基准数据集上达到最先进的水平。
如下图所示,2DPASS首先从原始相机图像中随机抽取一个区域作为二维输入,将裁剪后的图像和点云分别经过独立的2D和3D编码器,并提取两个主干网络中的多尺度特征。然后,其通过多尺度-多模态到单模态知识蒸馏 (Multi-Scale Fusion-to-Single Knowledge Distillation,MSFSKD) 增强三维网络。该方法即充分利用纹理和颜色感知的二维先验,同时保留原始的三维的特定知识。最后,2DPASS利用每个尺度的二维和三维特征生成语义分割预测,由三维点云标签进行监督。在实时推理过程中,2DPASS丢弃与图像相关的分支,与基于融合的多模态方法相比,有效地避免了额外的计算负担。
在这里插入图片描述
其中,MSFSKD是2DPASS的关键,其目的是利用多尺度的二维先验信息,通过融合再蒸馏的方式,提高每个尺度的三维特征表示。具体如下图所示:
在这里插入图片描述
首先,对于每个2D和3D主干网络的每个尺度,2DPASS都会提取相应的特征,并将其映射成原始点云的尺度。有了相互对齐的2D和3D特征,其采用先融合后蒸馏的方式将2D网络的先验信息在训练中传输给3D网络。
在这里插入图片描述

三、实验效果

在论文提交时,2DPASS在SemanticKITTI数据集的单帧和多帧语义分割比赛中均登顶榜首,并在Nuscenes数据集上也达到了最先进的精度。
在这里插入图片描述
同时,2DPASS对于其他的点云语义分割网络(如MinkowskiNet和SPVCNN)也能产生显著的提升。
在这里插入图片描述

四、可视化结果

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

五、结语

本文介绍了一个基于二维先验辅助的激光雷达点云语义分割算法2DPASS,其在模型训练阶段从多模态数据中获取更丰富的语义和结构信息将其提炼到点云分割网络中。该方法具有良好的通用性,且在推理时仅基于点云数据输入即可实现又快又准的三维语义分割。该方法在SemanticKITTI数据集单帧和多帧语义分割,以及Nuscenes数据集都达到了最先进的水平。

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于PCL库实现道路可行驶区域检测的代码: ```c++ #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/point_types.h> #include <pcl/filters/voxel_grid.h> #include <pcl/filters/passthrough.h> #include <pcl/segmentation/sac_segmentation.h> #include <pcl/segmentation/extract_clusters.h> #include <pcl/features/normal_3d_omp.h> #include <pcl/surface/mls.h> #include <pcl/visualization/pcl_visualizer.h> int main(int argc, char** argv) { // Load point cloud data pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPCDFile<pcl::PointXYZ>("input_cloud.pcd", *cloud); // Voxel grid filter to downsample the data pcl::VoxelGrid<pcl::PointXYZ> vg; vg.setInputCloud(cloud); vg.setLeafSize(0.05f, 0.05f, 0.05f); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>); vg.filter(*cloud_filtered); // Pass through filter to remove points outside the range pcl::PassThrough<pcl::PointXYZ> pass; pass.setInputCloud(cloud_filtered); pass.setFilterFieldName("z"); pass.setFilterLimits(0.0f, 2.0f); pass.filter(*cloud_filtered); // Plane segmentation to extract the ground plane pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients); pcl::PointIndices::Ptr inliers(new pcl::PointIndices); pcl::SACSegmentation<pcl::PointXYZ> seg; seg.setOptimizeCoefficients(true); seg.setModelType(pcl::SACMODEL_PLANE); seg.setMethodType(pcl::SAC_RANSAC); seg.setDistanceThreshold(0.1); seg.setInputCloud(cloud_filtered); seg.segment(*inliers, *coefficients); if (inliers->indices.size() == 0) { std::cerr << "Failed to segment the ground plane." << std::endl; return -1; } // Extract the road surface points pcl::ExtractIndices<pcl::PointXYZ> extract; extract.setInputCloud(cloud_filtered); extract.setIndices(inliers); extract.setNegative(false); pcl::PointCloud<pcl::PointXYZ>::Ptr road_surface(new pcl::PointCloud<pcl::PointXYZ>); extract.filter(*road_surface); // Cluster extraction to separate the road surface points from other points std::vector<pcl::PointIndices> cluster_indices; pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec; ec.setClusterTolerance(0.2); ec.setMinClusterSize(100); ec.setMaxClusterSize(100000); ec.setInputCloud(road_surface); ec.extract(cluster_indices); // Extract the largest cluster as the road surface pcl::PointCloud<pcl::PointXYZ>::Ptr road(new pcl::PointCloud<pcl::PointXYZ>); int max_size = 0; for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it) { if (it->indices.size() > max_size) { max_size = it->indices.size(); extract.setIndices(boost::make_shared<const pcl::PointIndices>(*it)); extract.filter(*road); } } // Compute the surface normals pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>); pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>); pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne; ne.setInputCloud(road); ne.setSearchMethod(tree); ne.setRadiusSearch(0.5); ne.compute(*normals); // Smooth the surface normals pcl::MovingLeastSquares<pcl::PointXYZ, pcl::Normal> mls; mls.setInputCloud(road); mls.setInputNormals(normals); mls.setSearchRadius(0.5); mls.setPolynomialFit(true); mls.setPolynomialOrder(2); mls.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::Normal>::SAMPLE_LOCAL_PLANE); mls.setUpsamplingRadius(0.2); mls.setUpsamplingStepSize(0.1); pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_smoothed(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::Normal>::Ptr normals_smoothed(new pcl::PointCloud<pcl::Normal>); mls.process(*cloud_smoothed); mls.getOutputNormals(*normals_smoothed); // Visualization pcl::visualization::PCLVisualizer viewer("Road Detection"); viewer.addPointCloud<pcl::PointXYZ>(cloud_smoothed, "cloud"); viewer.addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud_smoothed, normals_smoothed, 10, 0.5, "normals"); viewer.spin(); return 0; } ``` 该代码实现了以下步骤: 1. 从PCD文件中加载点云数据。 2. 使用体素网格滤波器对数据进行下采样。 3. 使用通行滤波器删除超出范围的点。 4. 使用平面分割法提取地面平面。 5. 从地面平面中提取道路表面点。 6. 使用欧几里得聚类法将道路表面点与其他点分开。 7. 提取最大聚类作为道路表面。 8. 计算道路表面的表面法向量。 9. 对表面法向量进行平滑处理。 10. 可视化结果。 该代码的输出是可视化窗口,显示道路可行驶区域的点云数据和法向量。可以使用键盘和鼠标控制视角。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值