点云处理之点云滤波去噪

目录

点云滤波简介

什么是点云滤波?

为什么要点云滤波?

常用滤波器

直通滤波器

体素滤波器(下采样)

均匀采样滤波器(下采样)

统计滤波器(去噪)

条件滤波

半径滤波(去噪)

投影滤波

模型滤波

高斯滤波(去噪、平滑)

双边滤波(平滑)

总结


点云滤波简介

什么是点云滤波?

点云滤波作为常见的点云处理算法,一般是点云处理的第一步,对后续处理有很重要作用。滤波

有很多方面也有很多种功能,比如去除噪声点、离群点、点云平滑以及空洞、数据压缩等

为什么要点云滤波?

原始点云数据往往包含大量散列点、孤立点,在获取点云数据时 ,由于设备精度,操作者经验环

境因素带来的影响,以及电磁波的衍射性,被测物体表面性质变化和数据拼接配准操作过程的影

响,点云数据中讲不可避免的出现一些噪声,而且采样的分辨率也不同。

在点云处理流程中滤波处理作为预处理的第一步,对后续的影响比较大,只有在滤波预处理中将 噪声点,离群点,孔洞,数据压缩等按照后续处理定制,才能够更好的进行配准,特征提取,曲 面重建,可视化等后续应用处理。其类似于信号处理中的滤波。

PCL中点云滤波方案

PCL中总结了几种需要进行点云滤波处理的情况,这几种情况分别如下:

  1.   点云数据密度不规则需要平滑。
  2.   因为遮挡等问题造成离群点需要去除。
  3.   大量数据需要进行下采样(Downsample) 
  4.   噪声数据需要去除。

对应的方法如下:

  1. 按具体给定的规则限制过滤去除点。
  2. 通过常用滤波算法修改点的部分属性。
  3. 对数据进行下采样。
     

常用滤波器

直通滤波器

直通滤波器就是根据点云的属性在点的属性上设置范围,对点进行滤波,保留范围内的或保留范围外的。

  1. 指定一个维度以及该维度下的值域
  2. 遍历点云中的每个点,判断该点在指定维度上的取值是否在值域内,删除取值不在值域内的点
  3. 遍历结束,留下的点即构成滤波后的点云。
#include <pcl/filters/passthrough.h>
// 原点云获取后进行滤波
pcl::PassThrough<pcl::PointXYZ> pass;// 创建滤波器对象
pass.setInputCloud (cloud);//设置输入点云
pass.setFilterFieldName ("z");//滤波字段名被设置为Z轴方向
pass.setFilterLimits (0.0, 1.0);//可接受的范围为(0.0,1.0)
//pass.setFilterLimitsNegative (true);//设置保留范围内 还是 过滤掉范围内
pass.filter (*cloud_filtered); //执行滤波,保存过滤结果在cloud_filter

体素滤波器(下采样)

体素滤波器可以达到向下采样同时不破坏点云本身几何结构的功能,但是会移动点的位置。 此外体素滤波器可以去除一定程度的噪音点及离群点。主要功能是用来进行降采样
它的原理是根据输入的点云,首先计算一个能够刚好包裹住该点云的立方体,然后根据设定的分辨率,将该大立方体分割成不同的小立方体。对于每一个小立方体内的点,计算他们的质心,并用该质心的坐标来近似该立方体内的若干点。

ApproximateVoxelGrid的不同在于这种方法是利用每一个小立方体的中心来近似该立方体内的若干点。相比于 VoxelGrid,计算速度稍快,但也损失了原始点云局部形态的精细度

#include <pcl/filters/voxel_grid.h>
// VoxelGrid
pcl::VoxelGrid<pcl::PCLPointCloud2> sor;
sor.setInputCloud (cloud);
sor.setLeafSize (0.01f, 0.01f, 0.01f);
sor.filter (*cloud_filtered);
// Approximate 体素格滤波器
pcl::ApproximateVoxelGrid<pcl::PointXYZ> approximate_voxel_filter;
approximate_voxel_filter.setLeafSize (0.2, 0.2, 0.2);
approximate_voxel_filter.setInputCloud (input_cloud);
approximate_voxel_filter.filter (*filtered_cloud)

均匀采样滤波器(下采样)

均匀采样滤波基本上等同于体素滤波器,但是其不改变点的位置。下采样后,其点云分布基本均匀,但是其点云的准确度要好于体素滤波,因为没有移动点的位置
均匀采样算法:

均匀采样通过构建指定半径的球体对点云进行下采样滤波,将每一个球内距离球体中心最近的点作为下采样之后的点输出。

体素滤波是建立立方体,均匀采样是建立一个球。

#include <pcl/keypoints/uniform_sampling.h>
// 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);

统计滤波器(去噪)

统计滤波器主要用于去除明显离群点。 离群点特征在空间中分布稀疏。定义某处点云小于某个密度,既点云无效。计算每个点到其最近的k个点平均距离。则点云中所有点的距离应构成高斯分布。根据给定均值与方差,可剔除方差之外的点。

#include <pcl/filters/statistical_outlier_removal.h>
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud (cloud);
sor.setMeanK (50); //设置考虑查询点临近点数
sor.setStddevMulThresh (1.0);//设置判断是否为离群点的阀值
sor.filter (*cloud_filtered);
// 然后,使用同样的参数再次调用该滤波器,但是利用函数setNegative设置使输出取外点,以获取离群点数据(也就是原本滤除掉的点)。
sor.setNegative (true);
sor.filter (*cloud_filtered);

条件滤波

条件滤波器通过设定滤波条件进行滤波,删除不符合用户指定的一个或者多个条件。直通滤波器是一种较简单的条件滤波器。

#include <pcl/filters/conditional_removal.h>
pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond (new pcl::ConditionAnd<pcl::PointXYZ>()); //创建条件定义对象
//添加在Z字段上大于0的比较算子
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::GT, 0.0)));
//添加在Z字段上小于0.8的比较算子
range_cond->addComparison (pcl::FieldComparison<pcl::PointXYZ>::ConstPtr (new pcl::FieldComparison<pcl::PointXYZ> ("z", pcl::ComparisonOps::LT, 0.8)));
// 创建滤波器并用条件定义对象初始化
pcl::ConditionalRemoval<pcl::PointXYZ> condrem;
condrem.setCondition (range_cond);
condrem.setInputCloud (cloud);
condrem.setKeepOrganized(true); //设置保持点云的结构
condrem.filter (*cloud_filtered); // 执行滤波

半径滤波(去噪)

半径滤波器以某点为中心画一个圆计算落在该圆中点的数量,当数量大于给定值时,则保留该 点,数量小于给定值则剔除该点。
主要还是用于去除离群点在一定程度上可以用来筛选边缘点

#include <pcl/filters/radius_outlier_removal.h>
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.8);//设置半径为0.8的范围内找临近点
outrem.setMinNeighborsInRadius (2);//设置查询点的邻域点集数小于2的删除
outrem.filter (*cloud_filtered); //

投影滤波

将点投影到一个参数化模型上,这个参数化模型可以是平面、圆球、圆柱、锥形等进行投影滤波。 把三维点云投影到二维图像上,然后用图像处理的方法进行处理。

// 填充ModelCoefficients的值,使用ax+by+cz+d=0平面模型,其中 a=b=d=0,c=1 也就是X——Y平面
//定义模型系数对象,并填充对应的数据
pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients());
coefficients->values.resize(4);
coefficients->values[0] = coefficients->values[1] = 0;
coefficients->values[2] = 1.0;
coefficients->values[3] = 0;

// 创建ProjectInliers对象,使用ModelCoefficients作为投影对象的模型参数
pcl::ProjectInliers<pcl::PointXYZ> proj; //创建投影滤波对象
proj.setModelType(pcl::SACMODEL_PLANE); //设置对象对应的投影模型
proj.setInputCloud(cloud);//设置输入点云
proj.setModelCoefficients(coefficients);//设置模型对应的系数
proj.filter(*cloud_projected);//投影结果存储


模型滤波

根据点到模型的距离,设置距离阈值过滤非模型点, 基于模型的点分割操作,将模型外的点从点云中剔除。

//x^2 + y^2 + z^2 = 1
pcl::ModelCoefficients sphere_coeff;
sphere_coeff.values.resize (4);
sphere_coeff.values[0] = 0;
sphere_coeff.values[1] = 0;
sphere_coeff.values[2] = 0;
sphere_coeff.values[3] = 1;
pcl::ModelOutlierRemoval<pcl::PointXYZ> sphere_filter;
sphere_filter.setModelCoefficients (sphere_coeff);
sphere_filter.setThreshold (0.05);
sphere_filter.setModelType (pcl::SACMODEL_SPHERE);
sphere_filter.setInputCloud (cloud);
sphere_filter.filter (*cloud_sphere_filtered);

高斯滤波(去噪、平滑)

GaussianKernel是基于高斯核的卷积滤波实现,高斯过滤器相当于一个具有平滑性能的低通滤波器, 通过该类处理后的点云,相对比较平滑。

pcl::filters::Convolution<pcl::PointXYZRGB, pcl::PointXYZRGB> convolution;
Eigen::ArrayXf gaussian_kernel(5);
gaussian_kernel << 1.f/16, 1.f/4, 3.f/8, 1.f/4, 1.f/16;

convolution.setBordersPolicy(pcl::filters::Convolution<pcl::PointXYZRGB, pcl::PointXYZRGB>::BORDERS_POLICY_IGNORE);
convolution.setDistanceThreshold (static_cast<float> (0.1));
convolution.setInputCloud (inputCloud);
convolution.setKernel (gaussian_kernel);
convolution.convolve(*cloud);

双边滤波(平滑)

双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。一定程度上拟补了高斯滤波的缺点。 双边滤波对高斯噪声效果比较好。

pcl::FastBilateralFilter<pcl::PointXYZ> fbf;

PCL中filters模块

  • pcl::PassThrought<pcl::PointXYZ> pass; 直通滤波器
  • pcl::VoxelGrid< pcl::PointXYZ > vox;体素滤波器
  • pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;统计滤波器
  • pcl::RadiusOutlierRemoval<pcl::PointXYZ> rador;半径滤波器
  • pcl::UniformSamplinq<pcl::PointXYz> unisam;均匀采样
  • pcl::ConditionalRemoval<pcl::PointXYZ> condr;条件滤波器
  • pcl::ProjectInliers<pcl::PointXYZ> proj;投影滤波器
  • pcl::ModelOutlierRemoval<pcl::PointXYZ> modr;模型滤波器
  • pcl::BilateralFilter<pcl::PointXYZ> bf:双边滤波
  • pcl::filters::GaussianKernal<PointInT,PointOutT>高斯滤
  • pcl::ExtractIndices<pcl::PointxYz> extr;索引提取
  • 空间裁剪滤波
    • pcl::Clipper3D<pcl::PointXYZ>
    • pcl::BoxCliper3D<pcl::PointXYZ>
    • pcl::CropBox<pcl::PointXYZ>
    • pcl::CropHull<pcl::PointXYZ>

<think>好的,我现在需要帮助用户在C#中实现点云的统计滤波方法。首先,我需要回忆统计滤波的基本原理。统计滤波通常用于除离群点,通过计算每个点的邻域距离分布,移除那些距离均值超过一定标准差的点。这需要遍历点云中的每个点,计算其到最近k个邻居的平均距离,然后根据整体统计分布设定阈值。 接下来,用户需要的是C#的示例代码或教程。但据我所知,C#在点云处理方面可能没有像PCL(C++)或MATLAB那样现成的库。不过,用户可能希望手动实现或者使用现有的库,比如用一些.NET兼容的库。可能需要先确认是否有可用的库,比如通过NuGet查找。例如,MathNet.Numerics可能用于数学计算,但点云处理可能需要自定义数据结构。 假设用户没有现成的库,需要手动实现。那么步骤可能包括:读取点云数据(可能从文件加载或从传感器获取)、构建空间索引(如KD树)以高效查询邻近点、计算每个点的平均距离、统计均值标准差、设定阈值并过滤离群点。 在C#中,处理点云数据通常需要定义Point结构,包含X、Y、Z坐标。然后,构建KD树来加速邻近搜索。这里需要注意,手动实现KD树可能比较复杂,但可以使用现有的库,比如KdTree库(如果可用)。如果无法使用第三方库,可能需要自己实现一个简单的邻近搜索,但效率可能较低,尤其是在大数据集时。 代码的大致流程可能是: 1. 定义点结构。 2. 加载点云数据(可能需要读取PLY或XYZ文件)。 3. 构建空间索引。 4. 对每个点,查找k近邻,计算平均距离。 5. 计算所有平均距离的均值标准差。 6. 设定阈值(均值±n*标准差),过滤掉超出范围的点。 7. 输出过滤后的点云。 需要注意的问题包括:如何高效处理大规模点云,避免O(n^2)的时间复杂度。使用KD树可以将搜索复杂度降低到O(n log n),但实现KD树需要一定的工作量。此外,统计滤波的参数(如k近邻的数量、标准差倍数)需要根据具体场景调整。 可能的示例代码结构可能如下: - 使用List<Point3D>存储点云。 - 实现或引用KD树来查询k近邻。 - 遍历每个点,计算平均距离。 - 收集所有平均距离,计算均值标准差。 - 过滤点云。 由于C#没有内置的点云处理库,可能需要用户自己实现这些步骤。因此,在回答中需要提供代码框架,并指出可能需要第三方库或自行实现的部分。同时,参考之前用户提到的PCL库MATLAB代码,但转换为C#的实现思路。 最后,需要验证代码的正确性,比如处理小规模数据测试,确保滤波效果。此外,还需考虑内存管理性能优化,尤其是在处理大型点云时。</think>在C#中实现点云统计滤波可分为以下步骤,包含核心算法实现参数调优建议: ### 一、点云统计滤波实现步骤 1. **数据结构定义** ```csharp public class Point3D { public float X { get; set; } public float Y { get; set; } public float Z { get; set; } } ``` 2. **核心算法实现**(需配合空间索引) ```csharp public List<Point3D> StatisticalOutlierRemoval( List<Point3D> points, int kNeighbors = 50, float stdDevMulThresh = 1.0f) { // 计算每个点的平均距离 var avgDistances = new List<float>(); foreach (var point in points) { var neighbors = KdTree.FindNearestNeighbors(point, kNeighbors + 1); // +1排除自身 avgDistances.Add(neighbors.Average(n => Distance(point, n))); } // 计算统计阈值 var stats = new Statistics(avgDistances); float threshold = stats.Mean + stdDevMulThresh * stats.StandardDeviation; // 过滤离群点 return points.Where((p, i) => avgDistances[i] < threshold).ToList(); } ``` ### 二、关键技术实现要点 1. **空间索引构建**(示例使用KdTree): ```csharp public class KdTree { // 使用开源库如KdTreeLib或自行实现 public static List<Point3D> FindNearestNeighbors(Point3D query, int k) { /* 具体实现 */ } } ``` 2. **距离计算函数**: ```csharp private static float Distance(Point3D p1, Point3D p2) { return (float)Math.Sqrt( Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2) + Math.Pow(p1.Z - p2.Z, 2)); } ``` ### 三、参数调优建议 1. **邻域数量(kNeighbors)**: - 初始值建议:20-100 - 过大导致过度平滑,过小无法有效 2. **标准差倍数(stdDevMulThresh)**: - 初始值建议:1.0-2.0 - 值越小越激进 ### 四、性能优化方案 1. **空间索引加速**: - 推荐使用KdTree或Octree加速邻近搜索 - 比暴力搜索提升约10-100倍速度[^1] 2. **并行计算优化**: ```csharp Parallel.ForEach(points, point => { // 并行处理每个点的邻域搜索 }); ``` 3. **内存管理**: - 使用ArrayPool减少GC压力 - 分块处理超大规模点云 ### 五、完整示例代码框架 ```csharp // 读取PLY文件(需实现PLY解析器) var pointCloud = PlyReader.Read("input.ply"); // 执行统计滤波 var filteredCloud = StatisticalOutlierRemoval( pointCloud, kNeighbors: 50, stdDevMulThresh: 1.5f); // 保存结果 PlyWriter.Write("filtered.ply", filteredCloud); ``` 实际开发中建议使用成熟的数学计算库(如MathNet.Numerics)进行统计计算,并采用优化的空间索引结构。对于工程应用场景,可考虑使用HelixToolkit.SharpDX等三维处理库进行可视化验证。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SOC罗三炮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值