[转]PCL几种采样方法

Posted hackerl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]PCL几种采样方法相关的知识,希望对你有一定的参考价值。

(1)下采样  Downsampling

一般下采样是通过构造一个三维体素栅格,然后在每个体素内用体素内的所有点的重心近似显示体素中的其他点,这样体素内所有点就用一个重心点来表示,进行下采样的来达到滤波的效果,这样就大大的减少了数据量,特别是在配准,曲面重建等工作之前作为预处理,可以很好的提高程序的运行速度,

#include <pcl/io/pcd_io.h>#include <pcl/filters/voxel_grid.h>intmain(int argc, char** argv){    // 创建点云对象    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);    pcl::PointCloud<pcl::PointXYZ>::Ptr filteredCloud(new pcl::PointCloud<pcl::PointXYZ>);    // 读取PCD文件    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0)    {        return -1;    }    // 创建滤波对象    pcl::VoxelGrid<pcl::PointXYZ> filter;    filter.setInputCloud(cloud);    // 设置体素栅格的大小为 1x1x1cm    filter.setLeafSize(0.01f, 0.01f, 0.01f);    filter.filter(*filteredCloud);}

实验结果(略)

(2)

均匀采样:这个类基本上是相同的,但它输出的点云索引是选择的关键点在计算描述子的常见方式。

这里的filter.compute 是错误的,但是我也没搞明白应该怎么写,有懂得还望能告知一声!

#include <pcl/io/pcd_io.h>#include <pcl/keypoints/uniform_sampling.h>intmain(int argc, char** argv){    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);    pcl::PointCloud<pcl::PointXYZ>::Ptr filteredCloud(new pcl::PointCloud<pcl::PointXYZ>);    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0)    {        return -1;    }    // Uniform sampling object.    pcl::UniformSampling<pcl::PointXYZ> filter;    filter.setInputCloud(cloud);    filter.setRadiusSearch(0.01f);    // We need an additional object to store the indices of surviving points.    pcl::PointCloud<int> keypointIndices;    filter.compute(keypointIndices);    pcl::copyPointCloud(*cloud, keypointIndices.points, *filteredCloud);}

(3)增采样 :增采样是一种表面重建方法,当你有比你想象的要少的点云数据时,增采样可以帮你恢复原有的表面(S),通过内插你目前拥有的点云数据,这是一个复杂的猜想假设的过程。所以构建的结果不会百分之一百准确,但有时它是一种可选择的方案。所以,在你的点云云进行下采样时,一定要保存一份原始数据!

#include <pcl/io/pcd_io.h>#include <pcl/surface/mls.h>int main(int argc,char** argv){// 新建点云存储对象pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);pcl::PointCloud<pcl::PointXYZ>::Ptr filteredCloud(new pcl::PointCloud<pcl::PointXYZ>);    // 读取文件    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0)    {        return -1;    }    // 滤波对象    pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ> filter;    filter.setInputCloud(cloud);    //建立搜索对象    pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree;    filter.setSearchMethod(kdtree);    //设置搜索邻域的半径为3cm    filter.setSearchRadius(0.03);    // Upsampling 采样的方法有 DISTINCT_CLOUD, RANDOM_UNIFORM_DENSITY    filter.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ>::SAMPLE_LOCAL_PLANE);    // 采样的半径是    filter.setUpsamplingRadius(0.03);    // 采样步数的大小    filter.setUpsamplingStepSize(0.02);    filter.process(*filteredCloud);}

实验的结果

原始图像可视化:

 

(4)表面重建

深度传感器的测量是不准确的,和由此产生的点云也是存在的测量误差,比如离群点,孔等表面,可以用一个算法重建表面,遍历所有的点云和插值数据,试图重建原来的表面。比如增采样,PCL使用MLS算法和类。执行这一步是很重要的,因为由此产生的点云的法线将更准确。

#include <pcl/io/pcd_io.h>#include <pcl/surface/mls.h>#include <pcl/visualization/pcl_visualizer.h>#include <pcl/visualization/cloud_viewer.h>#include <boost/thread/thread.hpp>intmain(int argc, char** argv){        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);        pcl::PointCloud<pcl::PointNormal>::Ptr smoothedCloud(new pcl::PointCloud<pcl::PointNormal>);    if (pcl::io::loadPCDFile<pcl::PointXYZ>(argv[1], *cloud) != 0)    {        return -1;    }    // Smoothing object (we choose what point types we want as input and output).    pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> filter;    filter.setInputCloud(cloud);    // Use all neighbors in a radius of 3cm.    filter.setSearchRadius(0.03);    // If true, the surface and normal are approximated using a polynomial estimation    // (if false, only a tangent one).    filter.setPolynomialFit(true);    // We can tell the algorithm to also compute smoothed normals (optional).    filter.setComputeNormals(true);    // kd-tree object for performing searches.    pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree;    filter.setSearchMethod(kdtree);    filter.process(*smoothedCloud);  boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("smooth"));viewer->addPointCloud<pcl::PointNormal>(smoothedCloud,"smoothed");while(!viewer->wasStopped())  { viewer->spinOnce(100);boost::this_thread::sleep(boost::posix_time::microseconds(1000000)); }}

运行即可查看结果

                                                               原始图像(加了颜色)

                                             增采样平滑后(没有颜色信息)

 

微信公众号号可扫描二维码一起共同学习交流


---------------------
作者:Being_young
来源:CNBLOGS
原文:https://www.cnblogs.com/li-yao7758258/p/6527969.html
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

PCL中常用的高级采样方法


0. 简介

我们在使用PCL时候,常常不满足于常用的降采样方法,这个时候我们就想要借鉴一些比较经典的高级采样方法。这一讲我们将对常用的高级采样方法进行汇总,并进行整理,来方便读者完成使用

1. 基础下采样

1.1 点云随机下采样

点云下采样是对点云以一定的采样规则重新进行采样,目的是在保证点云整体几何特征不变的情况下,降低点云的密度,进而可以降低相关处理的数据量和算法复杂度。随机下采样顾名思义,随机下采样就似乎在原始点云中随机采样一定点数的点。这种方法最终得到的点云数量也是固定的。

pcl::PointCloud<PointT>::Ptr cloud_sub(new pcl::PointCloud<PointT>);  //随机下采样点云
pcl::RandomSample<PointT> rs; //创建滤波器对象
rs.setInputCloud(cloud); //设置待滤波点云
rs.setSample(20000); //设置下采样点云的点数
//rs.setSeed(1); //设置随机函数种子点
rs.filter(*cloud_sub); //执行下采样滤波,保存滤波结果于cloud_sub

PCL中常用的高级采样方法_点云

1.2 体素下采样

体素下采样的原理如图1所示,首先将点云空间进行网格化,也称体素化,即图1(b),网格化后的每一个格子称为体素,在这些划分为一个个极小的格子中包含一些点,然后对这些点取平均或加权平均得到一个点,以此来替代原来网格中所有的点,即图1©中蓝色的点。显然,网格选取越大则采样之后的点云越少,处理速度变快,但会对原先点云过度模糊,网格选取越小,则作用相反。

PCL中常用的高级采样方法_点云_02

pcl::VoxelGrid<pcl::PointXYZ> sor;    //创建体素网格采样处理对象
sor.setInputCloud(cloud); //设置输入点云
sor.setLeafSize(0.01f, 0.01f, 0.01f); //设置体素大小,单位:m
sor.filter(*cloud_filtered); //进行下采样

1.3 均匀采样

均匀采样的原理类似于体素化网格采样方法,同样是将点云空间进行划分,不过是以半径=r的球体,在当前球体所有点中选择距离球体中心最近的点替代所有点,注意,此时点的位置是不发生移动的。球体半径选取越大则采样之后的点云越少,处理速度变快,但会对原先点云过度模糊,网格选取越小,则作用相反。

pcl::UniformSampling<pcl::PointXYZ> form;   // 创建均匀采样对象
form.setInputCloud(cloud); //设置输入点云
form.setRadiusSearch(0.02f); //设置半径大小,单位:m
form.filter(*after_cloud); //执行滤波处理

1.4 增采样

增采样的特点是可极大的增加点云数据,但由于内插点的不确定性会导致最后输出的结果不一定准确。

//创建增采样对象
pcl::MovingLeastSquares<pcl::PointXYZ,pcl::PointXYZ> filter;
filter.setInputCloud(cloud); //设置输入点云
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree; //定义搜索方法
filter.setSearchMethod(kdtree); //设置搜索方法
filter.setSearchRadius(0.03); //设置搜索邻域的半径为3cm
//Upsampling 采样的方法还有 DISTINCT_CLOUD, RANDOM_UNIFORM_DENSITY
filter.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ>::SAMPLE_LOCAL_PLANE); //对点云进行上采样
filter.setUpsamplingRadius(0.03); //设置采样半径大小,3cm
filter.setUpsamplingStepSize(0.02); //设置采样步长大小,2cm
filter.process(*after_cloud); //执行采样操作

1.5 滑动最小二乘法采样

滑动最小二乘法采样的原理是将点云进行了滑动最小二乘法的映射,使得输出的点云更加平滑。

pcl::PointCloud<pcl::PointNormal>::Ptr smoothedCloud(new pcl::PointCloud<pcl::PointNormal>);   //定义法线
pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> filter;
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree; //定义搜索方法
filter.setInputCloud(cloud); //设置输入点云
filter.setUpsamplingMethod(); //增加密度较小区域的密度对于holes的填补却无能为力,具体方法要结合参数使用
filter.setSearchRadius(10);// 用于拟合的K近邻半径。在这个半径里进行表面映射和曲面拟合。半径越小拟合后曲面的失真度越小,反之有可能出现过拟合的现象。
filter.setPolynomialFit(true); //对于法线的估计是有多项式还是仅仅依靠切线。true为加多项式;false不加,速度较快
filter.setPolynomialFit(3); // 拟合曲线的阶数
filter.setComputeNormals(true); // 是否存储点云的法向量,true 为存储,false 不存储
filter.setSearchMethod(kdtree); //设置搜索方法
filter.process(*smoothedCloud); //处理点云并输出

2. 最远点采样(Farthest Point Sampling)

这个PCL代码目前已经集成到​​https://github.com/farthest_point_sampling.hpp​​​。这里我们来单独看一下调用代码,这里可以看到PCL中支持直接调用farthest_sampling这个函数可以实现最远点采样。最远点采样(Farthest Point Sampling)是一种非常常用的采样算法,由于能够保证对样本的均匀采样,被广泛使用,像3D点云深度学习框架中的PointNet++对样本点进行FPS采样再聚类作为感受野,3D目标检测网络VoteNet对投票得到的散乱点进行FPS采样再进行聚类,6D位姿估计算法PVN3D中用于选择物体的8个特征点进行投票并计算位姿。​​FPS算法原理​​:

  1. 输入点云有N个点,从点云中选取一个点P0作为起始点,得到采样点集合S=P0;
  2. 计算所有点到P0的距离,构成N维数组L,从中选择最大值对应的点作为P1,更新采样点集合S=P0,P1;
  3. 计算所有点到P1的距离,对于每一个点Pi,其距离P1的距离如果小于L[i],则更新L[i] = d(Pi, P1),因此,数组L中存储的一直是每一个点到采样点集合S的最近距离;
  4. 选取L中最大值对应的点作为P2,更新采样点集合S=P0,P1,P2;
  5. 重复2-4步,一直采样到N’个目标采样点为止。
std::vector<pcl::PointCloud<pcl::PointXYZ>> input_point_clouds(1);
std::vector<pcl::PointCloud<pcl::PointXYZ>> output_point_clouds;

ASSERT_NE(pcl::io::loadPLYFile<pcl::PointXYZ>(STR(INPUT_POINT_CLOUD_PATH),
input_point_clouds[0]), -1) << "Couldnt read file test point cloud file";
farthest_sampling::samplePointCloudsCuda(input_point_clouds, output_point_clouds, 4096);
boost::filesystem::path output_path = STR(OUTPUT_POINT_CLOUD_PATH);
if (output_path.has_parent_path() && !boost::filesystem::exists(output_path.parent_path()))

boost::filesystem::create_directories(output_path.parent_path());

pcl::io::savePLYFile(STR(OUTPUT_POINT_CLOUD_PATH), output_point_clouds[0]);
ASSERT_EQ(output_point_clouds[0].size(), 4096);

3. 法线空间采样

NormalSpaceSampling即:法线空间采样,它在法向量空间内均匀随机抽样,使所选点之间的法线分布尽可能大,结果表现为地物特征变化大的地方剩余点较多,变化小的地方剩余点稀少,可有效保持地物特征。实现方法如下:

  1. 首先计算每个点的K领域,然后计算点到领域点的法线夹角值,以此来近似达到曲率的效果并提高计算效率,因为曲率越大的地方,夹角值越大。
  2. 设置一个角度阈值,当点的领域夹角值大于阈值时被认为是特征明显的区域,其余区域为不明显区域。
  3. 对明显和不明显区域进行均匀采样,采样数分别为U*(1-V)和U*V,U是目标采样数,V是均匀采样性。

PCL中常用的高级采样方法_数据_03

// 创建基于邻域的法向估计类对象
// // 基于omp并行加速,需配置开启OpenMP
// pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> ne;
// ne.setNumberOfThreads(10);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
// 创建一个空的kdtree对象,并把它传递给法线估计对象,
// 用于创建基于输入点云数据的邻域搜索kdtree
pcl::search::KdTree<pcl::PointXYZ>::Ptr \\
tree(new pcl::search::KdTree<pcl::PointXYZ>());
// 传入待估计法线的点云数据,智能指针
ne.setInputCloud(cloud_src);
// 传入kdtree对象,智能指针
ne.setSearchMethod(tree);
// 设置邻域搜索半径
ne.setRadiusSearch(0.1f); // 设置半径时,要考虑到点云空间间距
// // 也可以设置最近邻点个数
// ne.setKSearch(25);
// 设置视点源点,用于调整点云法向(指向视点),默认(0,0,0)
ne.setViewPoint(0,0,0);
// 计算法线数据
ne.compute(*cloud_normals);

// 通过concatenateFields函数将point和normal组合起来形成PointNormal点云数据
pcl::PointCloud<pcl::PointNormal>::Ptr \\
cloud_with_normal(new pcl::PointCloud<pcl::PointNormal>());
pcl::PointCloud<pcl::PointNormal>::Ptr \\
cloud_with_normal_sampled(new pcl::PointCloud<pcl::PointNormal>());
pcl::concatenateFields(*cloud_src, *cloud_normals, *cloud_with_normal);

// 创建法向空间采样(模板)类对象
pcl::NormalSpaceSampling<pcl::PointNormal, pcl::Normal> nss;
// 设置xyz三个法向空间的分类组数,此处设置为一致,根据具体场景可以调整
const int kBinNum = 8;
nss.setBins(kBinNum, kBinNum, kBinNum);
// 如果传入的是有序点云,此处可以尝试设置为true
nss.setKeepOrganized(false);
// 设置随机种子,这样可以保证同样的输入可以得到同样的结果,便于debug分析
nss.setSeed(200); // random seed
// 传入待采样的点云数据
nss.setInputCloud(cloud_with_normal);
// 传入用于采样分析的法线数据,需与传入点云数据一一对应
nss.setNormals(cloud_normals);
// 设置采样总数,即目标点云的总数据量
const float kSampleRatio = 0.1f;
nss.setSample(cloud_with_normal->size()*kSampleRatio);
// 执行采样并带出采样结果
nss.filter(*cloud_with_normal_sampled);

4. 泊松盘采样

泊松盘采样(possion disk sampling)的特点是任何两个点的距离都不会隔得太近。

比如下图,左边是随机生成的点,右边是泊松盘采样生成的点。

PCL中常用的高级采样方法_点云_04


具体流程如下

  1. 设定好两个点之间最近的距离r,以及采样点所在空间的维度n,比如2维平面
  2. 在空间里生成足够多的网格,保证不接触的两个网格之间的点的距离大于r,并且网格数量足够多保证每个网格至多只需装一个采样点就能满足采样数量。为了最优化,一般取网格边长为r/\\sqrtn。
  3. 随机生成一个点,再创建两个数组,第一个是处理数组,第二个是结果数组,即最终的输出数组。把这个点放进处理数组中和结果数组中。
  4. 如果处理数组非空,从中随机选择一个点,如下图的红点,并把这个点从处理数组中删除。如果处理数组是空的,直接输出结果数组并结束算法。
  5. 设定最小距离minr,比如r,最大距离maxr,比如2*r。以红点为中心生成一个圆环,如下图灰色圆环,在这个圆环中生成一个采样点,如下图蓝点。

PCL中常用的高级采样方法_算法_05

以上是关于[转]PCL几种采样方法的主要内容,如果未能解决你的问题,请参考以下文章

PCL中常用的高级采样方法

PCL:ApproximateVoxelGrid❤️近似体素重心下采样

PCL 滤波采样——MLS平滑

PCL 滤波采样——MLS平滑

PCL:RandomSample ❤️ 随机下采样

PCL 滤波采样——体素采样