PCL中的统计学滤波离群点去噪-StatisticalOutlierRemoval

本文介绍了PCL库中的StatisticalOutlierRemoval模板类,用于在点云数据中通过统计方法检测并移除稀疏离群噪点,通过k近邻搜索计算点与其邻居的平均距离并基于高斯分布设定阈值进行判断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用是去除稀疏离群噪点。在采集点云的过程中,由于测量噪声的影响,会引入部分离群噪点,它们在点云空间中分布稀疏。在估算点云局部特征(例如计算采样点处的法向量和曲率变化率)时,这些噪点可能导致错误的计算结果,从而使点云配准等后期处理失败。统计滤波器的主要思想是假设点云中所有的点与其最近的k个邻居点的平均距离满足高斯分布,那么,根据均值和方差可确定一个距离阈值,当某个点与其最近k个点的平均距离大于这个阈值时,判定该点为离群点并去除。统计滤波器的实现原理如下:首先,遍历点云,计算每个点与其最近的k个邻居点之间的平均距离;其次,计算所有平均距离的均值μ与标准差σ,则距离阈值dmax可表示为dmax=μ+α×σ,α是一个常数,可称为比例系数,它取决于邻居点的数目;最后,再次遍历点云,剔除与k个邻居点的平均距离大于dmax的点。

PCL中的源码记录:

template <typename PointT> void
pcl::StatisticalOutlierRemoval<PointT>::applyFilterIndices (std::vector<int> &indices)
{
  // Initialize the search class
  if (!searcher_)
  {
    if (input_->isOrganized ())
      searcher_.reset (new pcl::search::OrganizedNeighbor<PointT> ());
    else
      searcher_.reset (new pcl::search::KdTree<PointT> (false));
  }
  searcher_->setInputCloud (input_);

  // The arrays to be used
  std::vector<int> nn_indices (mean_k_);
  std::vector<float> nn_dists (mean_k_);
  //distances数组用于存储每个点到其最近邻点的平均距离
  std::vector<float> distances (indices_->size ());
  //indices和removed_indices_用于存储滤波后的点的索引。
  indices.resize (indices_->size ());
  removed_indices_->resize (indices_->size ());
  //oii和rii分别是输出点索引的迭代器和被移除点索引的迭代器。
  int oii = 0, rii = 0;  // oii = output indices iterator, rii = removed indices iterator

  // First pass: Compute the mean distances for all points with respect to their k nearest neighbors
  int valid_distances = 0;
  for (int iii = 0; iii < static_cast<int> (indices_->size ()); ++iii)  // iii = input indices iterator
  {
    if (!std::isfinite (input_->points[(*indices_)[iii]].x) ||
        !std::isfinite (input_->points[(*indices_)[iii]].y) ||
        !std::isfinite (input_->points[(*indices_)[iii]].z))
    {
      distances[iii] = 0.0;
      continue;
    }

    // Perform the nearest k search
    if (searcher_->nearestKSearch ((*indices_)[iii], mean_k_ + 1, nn_indices, nn_dists) == 0)
    {
      distances[iii] = 0.0;
      PCL_WARN ("[pcl::%s::applyFilter] Searching for the closest %d neighbors failed.\n", getClassName ().c_str (), mean_k_);
      continue;
    }

    // Calculate the mean distance to its neighbors
    double dist_sum = 0.0;
    for (int k = 1; k < mean_k_ + 1; ++k)  // k = 0 is the query point
      dist_sum += sqrt (nn_dists[k]);
    distances[iii] = static_cast<float> (dist_sum / mean_k_);
	//valid_distances用于记录有效的距离计算次数。
    valid_distances++;
  }

  // Estimate the mean and the standard deviation of the distance vector
  //sum和sq_sum分别用于累加距离和距离的平方
  double sum = 0, sq_sum = 0;
  for (const float &distance : distances)
  {
    sum += distance;
    sq_sum += distance * distance;
  }
  double mean = sum / static_cast<double>(valid_distances);
  double variance = (sq_sum - sum * sum / static_cast<double>(valid_distances)) / (static_cast<double>(valid_distances) - 1);
  double stddev = sqrt (variance);
  //getMeanStd (distances, mean, stddev);

  double distance_threshold = mean + std_mul_ * stddev;

  // Second pass: Classify the points on the computed distance threshold
  //如果点的平均距离超过阈值,即被认为是异常点,并将其索引存储到removed_indices_中。否则,将点的索引存储到indices中。
  for (int iii = 0; iii < static_cast<int> (indices_->size ()); ++iii)  // iii = input indices iterator
  {
    // Points having a too high average distance are outliers and are passed to removed indices
    // Unless negative was set, then it's the opposite condition
    if ((!negative_ && distances[iii] > distance_threshold) || (negative_ && distances[iii] <= distance_threshold))
    {
      if (extract_removed_indices_)
        (*removed_indices_)[rii++] = (*indices_)[iii];
      continue;
    }

    // Otherwise it was a normal point for output (inlier)
    indices[oii++] = (*indices_)[iii];
  }

  // Resize the output arrays
  indices.resize (oii);
  removed_indices_->resize (rii);
}

<think>嗯,用户问的是PCL中的StatisticalOutlierRemoval滤波器的原理。首先,我需要确认自己对这个滤波器的理解是否正确。记得StatisticalOutlierRemoval主要用于离群点,也就是那些不符合周围点云统计特性的点。 用户可能是在处理点云数据时遇到了声问题,比如激光雷达数据中的漂浮物或者测量误差。他们可能已经尝试过其他滤波方法,比如体素网格下采样或者半径滤波,但发现有些离群点仍然存在,所以想用更高级的方法来处理。 接下来,我需要回忆这个算法的具体步骤。StatisticalOutlierRemoval是基于统计学的,通常计算每个点到其邻居的平均距离,假设这些距离符合高斯分布,然后根据均值和标准差设定阈值,除超出阈值的点。这个过程需要设置两个主要参数:K近邻的数目和标准差倍数。比如,设置均值的标准差倍数,超过这个倍数的点被视为离群点。 可能用户在使用时遇到了参数设置的问题,比如如何选择合适的K值或者标准差倍数。这时候需要解释参数的影响,比如K值过小可能导致声误判,过大则可能忽略真实离群点。标准差倍数通常设为1或2,根据具体场景调整。 另外,用户可能想知道这个滤波器与其他滤波方法的区别,比如和RadiusOutlierRemoval相比,StatisticalOutlierRemoval更依赖统计特性,适合处理密度变化较大的点云,而RadiusOutlierRemoval则是基于固定半径内的点数,适合均匀分布的数据。 在实现细节方面,用户可能需要了解是否需要安装额外的模块,或者头文件。不过之前的问题已经问过头文件,所以这里可能更关注原理和参数调整。另外,可能需要注意这个滤波器处理后的点云索引变化,或者是否需要预处理步骤,比如先进行下采样提高计算效率。 用户还可能关心算法的计算复杂度,因为对于大规模点云,StatisticalOutlierRemoval可能会比较耗时,特别是当K值较大时。这时候可以建议先使用体素滤波减少点数,再应用StatisticalOutlierRemoval。 最后,需要确认是否有常见的错误或陷阱,比如未正确设置输入点云,或者参数不合理导致过滤过度或不足。这时候提供示例代码和参数设置建议会很有帮助,帮助用户快速上手。</think>在PCL中,`StatisticalOutlierRemoval`(统计离群点滤波)的原理是通过统计分析点云密度分布,剔除不符合统计规律的离群点。以下是其核心原理分步说明: --- ### 🌟 **核心原理** 1. **邻域密度统计** 对每个点计算其到 **$k$ 最近邻点** 的平均距离 公式: $$\bar{d}_i = \frac{1}{k} \sum_{j=1}^k ||p_i - p_j||$$ ($p_i$为当前点,$p_j$为邻居点) 2. **全局分布建模** 假设所有点的平均距离服从 **高斯分布** 计算全局均值$\mu$和标准差$\sigma$: $$\mu = \frac{1}{N} \sum_{i=1}^N \bar{d}_i, \quad \sigma = \sqrt{\frac{1}{N} \sum_{i=1}^N (\bar{d}_i - \mu)^2}$$ 3. **阈值过滤** 设定标准差倍数阈值$\alpha$(通常$\alpha=1$或$2$) 保留满足条件的点: $$\bar{d}_i \in [\mu - \alpha\sigma, \mu + \alpha\sigma]$$ --- ### 🛠️ **参数配置** ```cpp pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor; sor.setMeanK(50); // 设置邻域点数k(建议值:20-100) sor.setStddevMulThresh(1.0); // 设置标准差倍数α(典型值:1.0-2.0) sor.setInputCloud(cloud); sor.filter(*filtered_cloud); ``` --- ### 🎯 **适用场景** | 场景类型 | 效果说明 | |-----------------------|-------------------------| | 激光雷达声点 | 除漂浮物/鬼影 | | 深度相机异常值 | 消除传感器点 | | 复杂环境点云 | 保留主体结构,除离散点 | --- ### ⚖️ **优缺点对比** | 优点 | 缺点 | |-----------------------|-------------------------| | 自适应密度变化 | 计算量较大(需KNN搜索) | | 参数物理意义明确 | 对k值选择敏感 | | 保留锐利边缘 | 不适用于均匀分布声 | --- ### 🔍 **算法扩展** - **加速优化**:结合 **KD-Tree** 加速邻域搜索(PCL内部自动使用) - **改进变种**:`RadiusOutlierRemoval`(固定半径统计,适合均匀声) - **多阶段滤波**:建议先使用`VoxelGrid`下采样,再执行统计滤波 --- ### 📊 **实验效果可视化** ``` 原始点云 滤波后点云 ●●●●●● ●●●●● ●● ●● ●● ●● ●●●● ●●●● (含离散点) (结构更清晰) ``` --- ### ⚠️ **常见错误** ```cpp // 错误1:未设置足够大的k值导致过度滤波 sor.setMeanK(5); // k过小会导致正常点被误删 // 错误2:未正确处理NaN值(需先调用removeNaNFromPointCloud) sor.setInputCloud(cloud_with_nan); // 将引发计算异常 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值