(1)下采样 Downsampling
一般下采样是通过构造一个三维体素栅格,然后在每个体素内用体素内的所有点的重心近似显示体素中的其他点,这样体素内所有点就用一个重心点来表示,进行下采样的来达到滤波的效果,这样就大大的减少了数据量,特别是在配准,曲面重建等工作之前作为预处理,可以很好的提高程序的运行速度,
#include <pcl/io/pcd_io.h>
#include <pcl/filters/voxel_grid.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>);
// 读取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)
均匀采样:这个类基本上是相同的,但它输出的点云索引是选择的关键点在计算描述子的常见方式。
#include <pcl/io/pcd_io.h>
#include <pcl/keypoints/uniform_sampling.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;
}
// 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>
int
main(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));
}
}
运行即可查看结果
原始图像(加了颜色)
增采样平滑后(没有颜色信息)
1、MLS基础
mls算法本质上和最小二乘一样,是一种拟合数据的算法。区别在于mls是局部的,即通过系数向量和基函数分别对数据中不同位置的节点区域进行拟合,需要计算出全部节点域的拟合函数的参数。而传统的最小二乘是全局的,采用所有的数据进行最小化平方和,不能过滤掉噪声点。
对于二维数据点,其拟合公式如下:
其中:
w为权函数,一般采用三次样条曲线,如果权函数为常量,则为一般的加权最小二乘算法。
n表示为包含在权函数w支持域中的节点数。
p(x)表示基函数,对于不同的数据维度和需要拟合的目标可以选择不同阶数的基函数。
a(x)表示系数向量,我们最后就需要计算出a向量的值。
u表示在x处的取值。
J表示在节点x位置的模型函数。
计算流程可以分为三步:
1,对J函数求导,得到一个线性方程组。
2,对线性方程组计算,求得a向量的值。
3,重建节点x附近的拟合函数,计算出拟合函数。
具体原理部分设计的数学计算太多,参看链接。
原理部分参考:
移动最小二乘法MLS(Moving Lest Squares)简要介绍
无网格法与Matlab程序设计(7)——移动最小二乘(MLS)形函数
对于三维数据点,计算步骤相似:
1,计算局部K邻域范围内的的超平面,超平面的法向量即为该处的的法线。
2,进行最小二乘拟合,最终得到拟合面的点坐标。
2、法线计算
PCL中可以先进行曲面重建,再根据曲面计算法线。
pcl::search::KdTree<pcl::PointXYZ>::Ptr kd_tree(new pcl::search::KdTree<pcl::PointXYZ>);
//法线
pcl::PointCloud<pcl::PointNormal>::Ptr normal(new pcl::PointCloud<pcl::PointNormal>);
//实例化移动最小二乘类
pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointNormal> mls;
mls.setInputCloud(cloud);
mls.setComputeNormals(true);
mls.setPolynomialFit(true);
mls.setPolynomialOrder(2); //设置多项式阶数
mls.setSearchMethod(kd_tree);
mls.setSearchRadius(0.05); //设置kdtree搜索半径
mls.setNumberOfThreads(4);
mls.process(*normal); //使用mls方法计算法线并进行曲面重建
3、上采样
PCL中上采样方法是通过计算邻域内拟合MLS局部曲面,然后根据曲面计算法线和点云间的插值坐标,最后将插值坐标映射到输入点云内。
算法原理部分非常复杂。。想研究的直接去看论文。
PCL提供了三种采样方式。
SAMPLE_LOCAL_PLANE:每个输入点的局部平面将使用 upsampling_radius_ (半径)和 upsampling_step_ (移动步长)参数以圆形方式采样。
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree;
UpSample.setSearchMethod(tree);
UpSample.setSearchRadius(0.1);
//移动最小二乘
UpSample.setUpsamplingMethod(pcl::MovingLeastSquares<pcl::PointXYZ, pcl::PointXYZ>::SAMPLE_LOCAL_PLANE);
UpSample.setUpsamplingRadius(0.04);
UpSample.setUpsamplingStepSize(0.02);
UpSample.process(*CloudUp);
本文转自:https://blog.csdn.net/shixin_0125/article/details/105153554?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-105153554-blog-124042946.topnsimilarv1&spm=1001.2101.3001.4242.1&utm_relevant_index=3