一、为什么需要滤波
在获取点云数据时 ,由于设备精度,操作者经验、环境等因素带来的影响,以及电磁波的衍射特性,被测物体表面性质变化和数据拼接配准操作过程的影响,点云数据中将不可避免的出现一些噪声。在点云处理流程中滤波处理作为预处理的第一步,对后续的影响比较大,只有在滤波预处理中将噪声点 ,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲面重建,可视化等后续应用处理。
二、PCL中的滤波
PCL中点云滤波模块提供了很多灵活实用的滤波处理算法,例如:双边滤波,高斯滤波,条件滤波,直通滤波,基于随机采样一致性滤波RANSAC等。滤波模块是作为 PCL的一个重要处理模块,其在应用中可以非常方便与其他点云处理流程协同使用。
一、直通滤波
直通滤波的作用是过滤掉在指定维度方向上取值不在给定值域内的点。
首先,指定一个维度以及该维度下的值域,其次,遍历点云中的每个点,判断该点在指定维度上的取值是否在值域内,删除取值不在值域内的点,最后,遍历结束,留下的点即构成滤波后的点云。直通滤波器简单高效,适用于消除背景等操作。
PCL中的pcl:PassThrough类实现对用户给定点云某个字段的限定下,对点云进行简单的基本过滤,例如限制过滤掉点云中所有Z字段不在某个范围内的点,该类的使用比较灵活但完全取决于用户的限定字段和对应条件。
设置限定字段的名称字符串field_ name,例如"z",等
void setFilterFieldName(const std::string &field_ name)
设置滤波限制条件
包括最小值limit_ min和最大值limit_max。该函数与set-FilterFieldName()一起使用,点云中所有点的setFilterFieldName()设置的字段的值未在用户所设定区间范围外的点将被删除。参数limit_min为允许的区间范围的最小值,默认为DBL_MIN, limit_max为允许的区间范围的最大值,默认为DBL_MAX.
void setFilterLimits(const double &limit_min,const double &limit_max)
设置返回滤波限制条件外的点还是内部点
limit_negative默认值为false,输出点云为在设定字段的设定范围内的点集,如果设置为true则刚好相反。警告:该方法将来将会被移除,用setNegative()函数代替。
void getFilterLimitsNegative (bool &limit_negative)
代码实例展示:
#include <iostream>
#include <ctime>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
int
main (int argc, char** argv)
{ srand(time(0));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
//填入点云数据
cloud->width = 5;
cloud->height = 1; //无序点
cloud->points.resize (cloud->width * cloud->height);
//生成随机点
for (size_t i = 0; i < cloud->points.size (); ++i)
{
cloud->points[i].x = rand () / (RAND_MAX + 1.0f)-0.5;
cloud->points[i].y = rand () / (RAND_MAX + 1.0f)-0.5;
cloud->points[i].z = rand () / (RAND_MAX + 1.0f)-0.5;
}
std::cerr << "Cloud before filtering: " << std::endl;
for (size_t i = 0; i < cloud->points.size (); ++i)
std::cerr << " " << cloud->points[i].x << " "
<< cloud->points[i].y << " "
<< cloud->points[i].z << std::endl;
// 创建滤波器对象
pcl::PassThrough<pcl::PointXYZ> pass;//创建滤波器对象
pass.setInputCloud (cloud); //设置待滤波的点云
pass.setFilterFieldName ("z"); //设置在Z轴方向上进行滤波
pass.setFilterLimits (0.0, 1.0); //设置滤波范围为0~1,在范围之外的点会被剪除
//pass.setFilterLimitsNegative (true);//是否反向过滤,默认为false
pass.filter (*cloud_filtered); //开始过滤
std::cerr << "Cloud after filtering: " << std::endl;
for (size_t i = 0; i < cloud_filtered->points.size (); ++i)
std::cerr << " " << cloud_filtered->points[i].x << " "
<< cloud_filtered->points[i].y << " "
<< cloud_filtered->points[i].z << std::endl;
return (0);
}
以上代码的输出结果如下:
Cloud before filtering:
0.0873413 0.418091 -0.331146
-0.367493 -0.240753 0.329285
0.258301 -0.415497 -0.326172
0.478882 -0.485962 -0.450592
0.0456543 0.17511 0.156433
Cloud after filtering:
-0.367493 -0.240753 0.329285
0.0456543 0.17511 0.156433
本此案例中设置的是对 z 轴进行滤波,滤波范围为0~1.0,所以z坐标为负的点已全部过滤掉。
二、条件滤波
在点云处理中,条件移除滤波器(ConditionalRemoval Filter)是一种有用的工具,它可以根据用户定义的条件来选择性地移除点云中的部分点。条件移除 滤波器主要应用于以下场景:
1.区域选择:根据用户定义的空间范围条件,选择性地保留或移除点云数据中的特定区域。
2.属性选择:根据点云数据的属性,如点的颜色、法向量、强度等,选择性地保留或移除符合条件的点
3.形状选择:根据点云数据的形状特征,如曲率、形状描述子等,选择性地保留或移除符合条件的点。
条件滤波器有两个子类,其中pcl::ConditionAndpcl::PointXYZRGB意味着“且”,pcl::ConditionOrpcl::PointXYZRGB意味着“或”。
关键函数:
1)条件A 且 条件B,使用该条件函数类。
pcl::ConditionAnd<pcl::PointXYZRGB>
2)条件A 或 条件B,使用该条件函数类。
pcl::ConditionOr<pcl::PointXYZRGB>
3)本函数设置true则保持原始点云的结构,点的数目没有减少,滤波掉的点采用nan代替。设置为false则不保存原始点云结构,滤波掉的点删除。
setKeepOrganized()
4)删除NAN点,与setKeepOrganized()方法联动使用
std::vector<int> mapping;
pcl::removeNaNFromPointCloud(*cloud_filtered, *cloud_filtered, mapping); // 去除nan点
代码展示:
#include <iostream>
#include <iostream>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/conditional_removal.h> //条件滤波
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main()
{
/****************条件滤波********************/
// 原始点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>());
pcl::io::loadPCDFile("D:/code/csdn/data/two_tree.pcd", *cloud); // 加载原始点云数据
// 结果点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);
// 字段
pcl::ConditionAnd<pcl::PointXYZRGB>::Ptr field(new pcl::ConditionAnd<pcl::PointXYZRGB>); // 字段
/*
pcl::ComparisonOps::GT // 大于
pcl::ComparisonOps::GE // 大于等于
pcl::ComparisonOps::LT // 小于
pcl::ComparisonOps::LE // 小于等于
pcl::ComparisonOps::EQ // 等于
*/
field->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZRGB>("z", pcl::ComparisonOps::GT, -27.0))); // z值大于-27.0
field->addComparison(pcl::FieldComparison<pcl::PointXYZRGB>::ConstPtr(new pcl::FieldComparison<pcl::PointXYZRGB>("z", pcl::ComparisonOps::LT, -24.5))); // “且”z值小于-27.0,若使用ConditionOr,则是“或”z值小于15
pcl::ConditionalRemoval<pcl::PointXYZRGB> filtered; // 条件滤波器
filtered.setCondition(field); // 字段条件
filtered.setInputCloud(cloud); // 输入点云
filtered.setKeepOrganized(false); // 设置true则保持原始点云的结构,点的数目没有减少,滤波掉的点采用nan代替。设置为false则不保存原始点云结构。
filtered.filter(*cloud_filtered); // 滤波
std::vector<int> mapping;
pcl::removeNaNFromPointCloud(*cloud_filtered, *cloud_filtered, mapping); // 去除nan点
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> raw_rgb(cloud);
view_raw->addPointCloud<pcl::PointXYZRGB>(cloud, raw_rgb, "raw cloud");
view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw cloud");
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_filtered(new pcl::visualization::PCLVisualizer("filter"));
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> filtered_rgb(cloud);
view_filtered->addPointCloud<pcl::PointXYZRGB>(cloud_filtered, filtered_rgb, "filtered cloud");
view_filtered->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered cloud");
while (!view_raw->wasStopped())
{
view_raw->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
while (!view_filtered->wasStopped())
{
view_filtered->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
三、双边滤波
双边滤波通过计算点云点周围的高斯权重,按照卷积核中元素强度值与中心点强度的近似情况来设置权重,强度值越相近,则该点权重越大。具体来说,双边滤波会考虑两个因素:空间距离和像素(或点云强度)差异。空间距离较近的点和像素值(或强度值)差异较小的点会被赋予更高的权重,从而在平滑过程中起到更大的作用。简单来说,就是取点云点附近的邻近点,然后通过其距离差异和强度差异,计算一个新的强度值出来,然后再赋值到该点上,以此实现强度的平滑。
原理如下:
(1)计算空间领域,确定每个点云点的空间领域,即其周围的邻近点。
(2)计算权重,对于领域内的每个点,计算其与中心点之间的空间距离权重和像素(或强度)差异权重。这两个权重相乘得到最终权重。
(3)加权平均,根据最终权重对领域内的点进行加权平均,得到中心点的新强度值。
(4)更新点云,将新的强度值赋值给中心点,并重复上述过程直到所有点都被处理过。
注意!双边滤波是对强度做平滑,不是对位置做平滑。
在PCL中,双边滤波涉及到的主要函数包括pcl::BilateralFilter或pcl::FastBilateralFilter。这些函数的关键参数包括:
输入点云:待处理的点云数据,通常包含位置和强度信息。
搜索方法:用于查找每个点的邻近点的方法,如KD树。
高斯搜索窗口大小:设置每次进行邻近搜索时考虑的领域范围大小。
标准差参数:包括空间域标准差(sigmaS)和强度域标准差(sigmaR),用于计算高斯权重。
样例代码如下:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/filters/bilateral.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
using namespace std;
void BilateralFilter(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in, pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out,
const double sigma_s, const double sigma_r)
{
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
//双边滤波
pcl::BilateralFilter<pcl::PointXYZ> bf;
bf.setInputCloud(cloud_in);
bf.setSearchMethod(kdtree);
bf.setHalfSize(sigma_s); // 双边滤波窗口一半。
bf.setStdDev(sigma_r); // 标准差参数
bf.filter(*cloud_out);
}
int main()
{
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("bunny.pcd", *cloud_in);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out(new pcl::PointCloud<pcl::PointXYZ>);
BilateralFilter(cloud_in, cloud_out,0.01, 0.01);
cout << "滤波前的点云个数:" << cloud_in->points.size() << endl;
cout << "滤波后的点云个数:" << cloud_out->points.size() << endl;
boost::shared_ptr<pcl::visualization::PCLVisualizer> view(new pcl::visualization::PCLVisualizer("3D_Viewer"));
int v1(0);
view->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
view->setBackgroundColor(0, 0, 0, v1);
view->addText("input cloudpoint", 10, 10, "v1", v1);
int v2(0);
view->createViewPort(0.5, 0.0, 1, 1.0, v2);
view->setBackgroundColor(0.1, 0.1, 0.1, v2);
view->addText("output cloudpoint", 10, 10, "v2", v2);
view->addPointCloud<pcl::PointXYZ>(cloud_in, "cloud_in", v1);
view->addPointCloud<pcl::PointXYZ>(cloud_out, "cloud_out", v2);
view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "cloud_in", v1);
view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "cloud_out", v2);
//view->addCoordinateSystem(1.0);
//view->initCameraParameters();
while (!view->wasStopped())
{
view->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
效果如下:
四、体素滤波
使用体素化网格方法实现下采样,即减少点的数量减少点云数据,并同时保存点云的形状特征,在提高配准,曲面重建,形状识别等算法速度中非常实用,PCL是实现的VoxelGrid类通过输入的点云数据创建一个三维体素栅格,容纳后每个体素内用体素中所有点的重心来近似显示体素中其他点,这样该体素内所有点都用一个重心点最终表示,对于所有体素处理后得到的过滤后的点云,这种方法比用体素中心逼近的方法更慢,但是对于采样点对应曲面的表示更为准确。
将整体点云体素化为一个三维体素栅格,使用每个体素栅格的质心点近似代表该体素块。
(1)对点云按照输入的立方体进行体素化处理,将所有的点云点分布进一个一个的小体素块中。
(2)计算每个体素块的质心点(注意要区别于体素滤波计算中心点)。
(3)所有计算出的体素块质心点组成组成新的点云点,合并得到最终结果。
如何理解体素化:如下图,将整体点云体素化为多个小体素块,用一个个正方形的小块代表该范围内的点云。
工作中往往都使用体素滤波;体素滤波可以在抽稀的同时,保持点云的原始形态,并且速度也较快。
在PCL中,计算质心点的函数为:
Eigen::Vector4f centroid; // 质心
pcl::compute3DCentroid(*cloud,centroid); // 计算质心
体素滤波的函数接口为:
pcl::VoxelGrid<pcl::PointXYZRGB>
设置最小体素块的xyz值:
setLeafSize()
相关示例代码为:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main()
{
/****************体素滤波********************/
// 原始点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPCDFile("D:/code/csdn/data/bunny.pcd", *cloud); // 加载原始点云数据
// 结果点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);
// 体素滤波函数实例化
pcl::VoxelGrid<pcl::PointXYZRGB> filter;
filter.setInputCloud(cloud);
filter.setLeafSize(0.003, 0.003, 0.003); // 每个体素块的xyz值
filter.filter(*cloud_filtered);
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
view_raw->addPointCloud<pcl::PointXYZRGB>(cloud, "raw cloud");
view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw cloud");
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_filtered(new pcl::visualization::PCLVisualizer("filter"));
view_filtered->addPointCloud<pcl::PointXYZRGB>(cloud_filtered, "filtered cloud");
view_filtered->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered cloud");
while (!view_raw->wasStopped())
{
view_raw->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
while (!view_filtered->wasStopped())
{
view_filtered->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
函数的输出结果为:
五、均匀采样滤波
均匀采样滤波基本上等同于体素滤波器,但是其不改变点的位置,在每个体素保留一个最接近体素中心的点,代替体素中所有点。下采样后,其点云分布基本 均匀,但是其点云的准确度要好于体素滤波,因为没有移动点的位置。
均匀采样算法:均匀采样通过构建指定半径的球体对点云进行下采样滤波,将每一个球内距离球体中心最
近的点作为下采样之后的点输出。
具体的相关接口如下:
pcl::UniformSampling<pcl::PointXYZ> us; //创建滤波器对象
us.setInputCloud(cloud); //设置待滤波点云
us.setRadiusSearch(0.05f); //设置滤波球体半径
us.filter(*cloud_filtered); //执行滤波,保存滤波结果于cloud_filtered
具体效果如下:
六、高斯滤波
高斯滤波是基于高斯核的卷积滤波实现,高斯过滤器相当于一个具有平滑性能的低通滤波器, 通过该类处理后的点云,相对比较平滑。高斯滤波可以应用于点云数据的XYZ坐标,通过高斯分布函数计算各点的高斯权重,然后按照每个点领域点的与其的距离和权重进行高斯处理,以平滑点云。具体什么是高斯分布,请百度百科即可,这里就不再说明。
高斯滤波效果举例:
高斯滤波的函数接口为:
pcl::VoxelGrid<pcl::PointXYZRGB>
代码展示:
//radius代表点云扫面的半径,这里需要用户计算获得
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <boost/random.hpp>
#include <pcl/console/time.h>
#include <pcl/filters/convolution_3d.h>
#include <pcl/kdtree/kdtree_flann.h>
using namespace std;
vector<vector<double>> DenoisingPD_GaussianKernel(vector<vector<double>> pointCloudOriginal,double radius) {
cout << "Gaussian denoising start:" << endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr inputcloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr outputcloud(new pcl::PointCloud<pcl::PointXYZ>);
//Read point cloud in the input file
for (int i = 0; i < pointCloudOriginal.size(); i++)
{
pcl::PointXYZ cloud_i;
cloud_i.x = pointCloudOriginal[i][0];
cloud_i.y = pointCloudOriginal[i][1];
cloud_i.z = pointCloudOriginal[i][2];
inputcloud->push_back(cloud_i);
}
//Set up the Gaussian Kernel
pcl::filters::GaussianKernel<pcl::PointXYZ, pcl::PointXYZ>::Ptr kernel(new pcl::filters::GaussianKernel<pcl::PointXYZ, pcl::PointXYZ>);
(*kernel).setSigma(4);
(*kernel).setThresholdRelativeToSigma(4);
std::cout << "Kernel made" << std::endl;
//Set up the KDTree
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
(*kdtree).setInputCloud(inputcloud);
std::cout << "KdTree made" << std::endl;
//Set up the Convolution Filter
pcl::filters::Convolution3D <pcl::PointXYZ, pcl::PointXYZ, pcl::filters::GaussianKernel<pcl::PointXYZ, pcl::PointXYZ> > convolution;
convolution.setKernel(*kernel);
convolution.setInputCloud(inputcloud);
convolution.setSearchMethod(kdtree);
convolution.setRadiusSearch(radius);
convolution.setNumberOfThreads(10);//important! Set Thread number for openMP
std::cout << "Convolution Start" << std::endl;
convolution.convolve(*outputcloud);
std::cout << "Convoluted" << std::endl;
vector<vector<double>> pCDenosing(pointCloudOriginal.size());
for (int i = 0; i < outputcloud->size(); i++) {
vector<double> pi(3);
pi[0] = outputcloud->at(i).x;
pi[1] = outputcloud->at(i).y;
pi[2] = outputcloud->at(i).z;
pCDenosing[i] = pi;
}
cout << "Gaussian denoising finished!" << endl;
}
七、半径滤波
半径滤波也叫基于连通分析的滤波,该方法的基本思想是假定原始点云中每个激光点在指定的半径邻域中至少包含一定数量的近邻点。原始点云中符合假设条件的激光点被视为正常点进行保留,反之,则视为噪声点并进行去除。该方法对原始激光点云中存在的一些悬空的孤立点或无效点具有很好的去除效果。半径滤波器以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该 点,数量小于给定值则剔除该点。
代码示例:
#include <pcl/io/pcd_io.h> //文件输入输出
#include <pcl/point_types.h> //点类型相关定义
#include <pcl/visualization/cloud_viewer.h> //点云可视化相关定义
#include <pcl/filters/radius_outlier_removal.h> //滤波相关
#include <pcl/common/common.h>
#include <iostream>
using namespace std;
int main()
{
//1.读取点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PCDReader r;
r.read<pcl::PointXYZ>("data\\table_scene_lms400.pcd", *cloud);
cout << "there are " << cloud->points.size() << " points before filtering." << endl;
//2.半径滤波
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filter(new pcl::PointCloud<pcl::PointXYZ>);
pcl::RadiusOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setRadiusSearch(0.02);
sor.setMinNeighborsInRadius(15);
sor.setNegative(false);
sor.filter(*cloud_filter);
//3.滤波结果保存
pcl::PCDWriter w;
w.writeASCII<pcl::PointXYZ>("data\\table_scene_lms400_Radius_filter.pcd", *cloud_filter);
cout << "there are " << cloud_filter->points.size() << " points after filtering." << endl;
system("pause");
return 0;
}
效果如下:
八、统计滤波
当使用半径滤波器等效果不大好的时候使用统计滤波器。统计滤波即是对点云中每个点进行邻域搜索,判断邻域搜索出的这块点云是否符合高斯分布。根据给定的参数,如均值和方差等,剔除在高斯期望之外的点。与高斯滤波相比:高斯滤波用于平滑点云并去噪;统计滤波只用于去噪。
具体操作步骤如下:
(1)对点云建KD树。
(2)点云中每个点进行邻域搜索,获取邻域搜索出的点云块。
(3)根据给定的阈值,判断每个点是否符合高斯期望,剔除期望值之外的点,得到结果点云。
关键函数部分:
(1)设置K邻域搜索点的个数;即每次选取点云点附近n个点作为邻域点。
setMeanK()
(2)设置高斯期望的计算阈值;
setStddevMulThresh()
代码实例:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main()
{
/****************统计滤波********************/
// 原始点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPCDFile("D:/code/csdn/data/bunny.pcd", *cloud); // 加载原始点云数据
// 结果点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);
// 统计滤波器
pcl::StatisticalOutlierRemoval<pcl::PointXYZRGB> filters; // 统计滤波器
filters.setInputCloud(cloud);
filters.setMeanK(6); // K邻域搜索的个数
filters.setStddevMulThresh(1.0); // 判断是否是离群点的阈值,大于该阈值,即为离群点(具体见4.关键函数)
filters.filter(*cloud_filtered);
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
view_raw->addPointCloud<pcl::PointXYZRGB>(cloud, "raw cloud");
view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw cloud");
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_filtered(new pcl::visualization::PCLVisualizer("filter"));
view_filtered->addPointCloud<pcl::PointXYZRGB>(cloud_filtered, "filtered cloud");
view_filtered->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered cloud");
while (!view_raw->wasStopped())
{
view_raw->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
while (!view_filtered->wasStopped())
{
view_filtered->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
具体实现效果如下:
九、投影滤波
投影滤波即用一组光线将物体的形状投射到一个平面上去,称为“投影”。 如站在阳光下,地面上的影子就是你对地面的投影;投影滤波可以用来作为点云相关报告、或者自动驾驶去畸变之后点云转换为图片。在投影时,PCL不同模型,设置参数的方式不同,需要注意。
正投影:光线垂直于待投影平面的投影。(PCL中该方法即用的正投影)
斜投影:光线与待投影平面倾斜的投影。
关键函数:
(1)设置投影类型
setModelType();
投影类型有如下几种:
SACMODEL_PLANE 平面模型
SACMODEL_LINE 直线模型
SACMODEL_CIRCLE2D 2d圆模型
SACMODEL_CIRCLE3D 3d圆模型
SACMODEL_SPHERE 球模型
SACMODEL_CYLINDER 圆柱模型
SACMODEL_CONE 圆锥模型
SACMODEL_TORUS 圆环模型,目前还未实现
SACMODEL_PARALLEL_LINE 和给定轴平行的直线
SACMODEL_PERPENDICULAR_PLANE 和给定轴垂直的平面
SACMODEL_NORMAL_PLANE 拟合一个平面,用于拟合的内点的法相量与最后输出的平面之间法相量的夹角必须小于某个阈值
SACMODEL_NORMAL_SPHERE 拟合一个球,用于拟合的内点的法相量与最后输出的球面之间法相量的夹角必须小于某个阈值
SACMODEL_PARALLEL_PLANE 拟合出和给定轴平行的平面
SACMODEL_NORMAL_PARALLEL_PLANE 拟合一个平面,其约束条件为 SACMODEL_NORMAL_PLANE + SACMODEL_PERPENDICULAR_PLANE。
SACMODEL_STICK 棒状模型
(2)设置模型参数
setModelCoefficients()
样例代码:
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/ModelCoefficients.h>
#include <pcl/filters/project_inliers.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
int main()
{
/****************投影滤波********************/
// 原始点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPCDFile("D:/code/csdn/data/bunny.pcd", *cloud); // 加载原始点云数据
// 结果点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZRGB>);
// 待投影平面(模型类型可以是文章中表述的几十种)
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients()); // 模型参数
coefficients->values.resize(4); // 平面模型的参数式为:Ax + By + Cz + D = 0;以下即为ABCD四个参数的值
coefficients->values[0] = 0;
coefficients->values[1] = 0;
coefficients->values[2] = 1.0;
coefficients->values[3] = 0;
pcl::ProjectInliers<pcl::PointXYZRGB> filters;
filters.setModelType(pcl::SACMODEL_PLANE); // 设置投影类型
filters.setInputCloud(cloud);
filters.setModelCoefficients(coefficients); // 设置模型参数
filters.filter(*cloud_filtered);
/****************展示********************/
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_raw(new pcl::visualization::PCLVisualizer("raw"));
view_raw->addPointCloud<pcl::PointXYZRGB>(cloud, "raw cloud");
view_raw->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw cloud");
boost::shared_ptr<pcl::visualization::PCLVisualizer> view_filtered(new pcl::visualization::PCLVisualizer("filter"));
view_filtered->addPointCloud<pcl::PointXYZRGB>(cloud_filtered, "filtered cloud");
view_filtered->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "filtered cloud");
while (!view_raw->wasStopped())
{
view_raw->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
while (!view_filtered->wasStopped())
{
view_filtered->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
return 0;
}
效果如下: