pcl::Feature 类输入数据的传入方法和四种应用模式

pcl::Feature类 有两种输入数据的方法,共对应四种应用模式:

两种输入数据的方法

  1. 一个完整的点云数据集,通过setInputCloud (PointCloudConstPtr &)来传入。任何特征估计的子类都会尝试估计这个输入点云中每个点的特征。
  2. 一个点云数据集的子集,通过setInputCloud (PointCloudConstPtr &)和setIndices (IndicesConstPtr &)来传入。任何特征估计的子类都会尝试估计给定索引列表中的每个索引对应的点的特征,如果没有给定索引列表,则会默认估计点云数据集中所有点的特征。

此外,可以通过附加调用setSearchSurface(PointCloudConstPtr&)指定要使用的相邻点集。 此调用是可选的,当未给出搜索表面时,默认使用输入点云数据集。

要分清楚两种点,上面的概念才好理解:
第一种是将以这个点为中心,估计这个点的特征的那一个点子集,一般用setInputCloud (PointCloudConstPtr &)和setIndices (IndicesConstPtr &)来传入。
另一种是为第一类待估计的点提供邻接信息和特征约束的点集,一般用setSearchSurface(PointCloudConstPtr&)来传入。
第一种点的特征,用第二种点来描述,这就是特征估计。

对应的四种应用模式

因为总是需要setInputCloud(),所以我们可以使用<setInputCloud(),setIndices(),setSearchSurface()>创建最多四种组合。
假设我们有两个点云,P = {p_1,p_2,… p_n}和Q = {q_1,q_2,…,q_n}。 下图显示了所有四种情况:
在这里插入图片描述

  1. setIndices() = false, setSearchSurface() = false
    无疑这是PCL中最常用的一种形式,第一类点是输入的全体点云数据,第二类点也是输入的全体点云数据。这种应用模式,将对输入的所有点进行特征估计,用来特征表示的相邻点集也都来自输入点云。当给定一个待估计的点,便用输入点云自身的其他点表示待估计点的特征。
    如左1图所示, 首先,我们估计p_1的相邻点集,然后是p_2的相邻点集,依此类推,直到我们遍历完了P中的所有点。
  2. setIndices()= true,setSearchSurface()= false
    这种应用模式下,第一类点由输入点云数据与索引列表共同决定,即只估计输入点云中索引号在索引列表中的那些点,第二类的点则是完整的输入点云数据集。
    如左2图所示,我们假设p_2的索引不在给定的索引列表中,因此不会估计p_2处的相邻点集或特征。
  3. setIndices()= false,setSearchSurface()= true
    这种应用模式下,第一类点是输入的全体点云数据,第二类点是则由setSearchSurface()指定。
    如左3图所示,假设Q = {q_1,q_2}是第一类点,P是第二类点,即P是Q的搜索表面,则q_1和q_2的相邻点集或特征将用P中的点进行表示。
  4. setIndices()= true,setSearchSurface()= true
    这可能是最罕见的情况,其中给出了索引和搜索表面。在这种应用模式下,第一类点由输入点云数据与索引列表共同决定,第二类点由setSearchSurface()指定。
    如左4图所示,假设q_2的索引不是为Q给出的索引向量的一部分,因此在q_2处不会估计相邻点集或者特征信息。

使用setSearchSurface()最有用的例子是:
当我们有一个点云密度非常大的输入数据集时,基于计算花费的考虑,我们不想估计其中所有点的特征,而是想更精准地估计某些关键点(比如用pcl_keypoints中的方法获得的),或者估计点云的下采样版本中的点(比如用体素格滤波器获得)。 在这种情况下,我们可以通过setInputCloud()传入下采样/关键点作为数据输入,并将原始点云数据作为setSearchSurface()传入,从而灵活高效地实现估计指定点的特征。

在法向量估计中的应用举例

  1. 估计输入数据集中所有点的曲面法线
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>

{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

  ... read, pass in or create a point cloud ...

  // Create the normal estimation class, and pass the input dataset to it
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  // Create an empty kdtree representation, and pass it to the normal estimation object.
  // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // Output datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input cloud->points.size ()
}
  1. 估计输入数据集的子集中的所有点的曲面法线
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>

{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);

  ... read, pass in or create a point cloud ...

  // Create a set of indices to be used. For simplicity, we're going to be using the first 10% of the points in cloud
  std::vector<int> indices (floor (cloud->points.size () / 10));
  for (size_t i = 0; i < indices.size (); ++i) indices[i] = i;

  // Create the normal estimation class, and pass the input dataset to it
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  // Pass the indices
  boost::shared_ptr<std::vector<int> > indicesptr (new std::vector<int> (indices));
  ne.setIndices (indicesptr);

  // Create an empty kdtree representation, and pass it to the normal estimation object.
  // Its content will be filled inside the object, based on the given input dataset (as no other search surface is given).
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // Output datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input indicesptr->size ()
}
  1. 估计输入数据集中的所有点的曲面法线,但使用另一个数据集估计其相邻点集。 即估计下采样版本点云中所有点的法向量的实现方法。
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>

{
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_downsampled (new pcl::PointCloud<pcl::PointXYZ>);

  ... read, pass in or create a point cloud ...

  ... create a downsampled version of it ...

  // Create the normal estimation class, and pass the input dataset to it
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud_downsampled);

  // Pass the original data (before downsampling) as the search surface
  ne.setSearchSurface (cloud);

  // Create an empty kdtree representation, and pass it to the normal estimation object.
  // Its content will be filled inside the object, based on the given surface dataset.
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // Output datasets
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);

  // Compute the features
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () should have the same size as the input cloud_downsampled->points.size ()
}

参考资料

How 3D Features work in PCL

//粗配准 void PointProcessing::coarseRegistration( const std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr>& clusters, // 多簇源点云 const pcl::PointCloud<pcl::PointXYZ>::Ptr& target, // 目标点云 std::vector<Eigen::Matrix4f>& transformation_matrices, // 输出变换矩阵集合 float sac_ia_max_distance, // SAC-IA最大对应距离 int feature_radius // FPFH特征半径 ) { transformation_matrices.clear(); // 遍历每个簇 for (const auto& cluster : clusters) { // --- 1. 源簇关键点提取 --- pcl::PointCloud<pcl::PointXYZ>::Ptr src_keypoints(new pcl::PointCloud<pcl::PointXYZ>); pcl::UniformSampling<pcl::PointXYZ> uniform_sampling; uniform_sampling.setInputCloud(cluster); uniform_sampling.setRadiusSearch(2.0); // 关键点采样半径(可参数化) uniform_sampling.filter(*src_keypoints); // --- 2. 目标关键点提取(复用同一采样器)--- pcl::PointCloud<pcl::PointXYZ>::Ptr tgt_keypoints(new pcl::PointCloud<pcl::PointXYZ>); uniform_sampling.setInputCloud(target); uniform_sampling.filter(*tgt_keypoints); // --- 3. 使用自定义函数计算法线 --- pcl::PointCloud<pcl::Normal>::Ptr src_normals(new pcl::PointCloud<pcl::Normal>); computernormals(src_keypoints, src_normals, 5.0); // 法线半径5.0 pcl::PointCloud<pcl::Normal>::Ptr tgt_normals(new pcl::PointCloud<pcl::Normal>); computernormals(tgt_keypoints, tgt_normals, 5.0); // --- 4. 使用自定义函数计算FPFH特征 --- pcl::PointCloud<pcl::FPFHSignature33>::Ptr src_features(new pcl::FPFHSignature33); computerFPFH(src_keypoints, src_normals, src_features, feature_radius); pcl::PointCloud<pcl::FPFHSignature33>::Ptr tgt_features(new pcl::FPFHSignature33); computerFPFH(tgt_keypoints, tgt_normals, tgt_features, feature_radius); // --- 5. SAC-IA配准 --- pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia; sac_ia.setInputSource(src_keypoints); sac_ia.setSourceFeatures(src_features); sac_ia.setInputTarget(tgt_keypoints); sac_ia.setTargetFeatures(tgt_features); sac_ia.setMaxCorrespondenceDistance(sac_ia_max_distance); pcl::PointCloud<pcl::PointXYZ>::Ptr aligned(new pcl::PointCloud<pcl::PointXYZ>); pcl::PointCloud<pcl::PointXYZ>::Ptr aligned(new pcl::PointCloud<pcl::PointXYZ>); sac_ia.align(*aligned); // 无返回值,直接执行 // 检查变换矩阵有效性 Eigen::Matrix4f matrix = sac_ia.getFinalTransformation(); if (!matrix.isZero() && !matrix.hasNaN()) { transformation_matrices.push_back(matrix); } else { transformation_matrices.push_back(Eigen::Matrix4f::Identity()); } } }其中clusters是一个包含多个聚的容器,修改这个函数,循环遍历clusters,计算法线与特征,随后与target进行粗配准
最新发布
03-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值