【译】PCL官网教程翻译(13):PCL中3D特征体现 - How 3D Features work in PCL

英文原网页查看。

PCL中3D特征体现

本文介绍了PCL中的3D特性估计方法,并作为对PCL:: feature类的内部结构感兴趣的用户或开发人员的指南。

理论基础

来自Rusu论文:
在它们的原生表示中,三维映射系统概念中定义的点只是使用它们相对于给定原点的笛卡尔坐标x、y、z来表示。假设坐标系原点不随时间变化,可能有两个点p1和p2,分别在t1和t2时刻得到,坐标相同。然而,比较这些点是一个不适定的问题,因为即使它们相对于某些距离度量(例如欧几里得度量)是相等的,它们也可以在完全不同的表面上采样,因此,当将它们与附近的其他点放在一起时,它们所表示的信息是完全不同的。这是因为不能保证世界在t1和t2时刻间没有变化。一些采集设备可能会为采样点提供额外的信息,比如强度或表面缓解值(surface remission value),甚至是颜色,但这并不能完全解决问题,而且比较仍然模糊不清。
由于各种原因需要比较点的程序需要更好的特性和度量,以便能够区分几何表面。因此,三维点作为具有笛卡尔坐标的奇异实体的概念消失了,取而代之的是一个新的概念,即局部描述符(local descriptor)。文献中有大量描述相同概念的不同命名方案,例如形状描述符(shape descriptors)或几何特征(geometric features),但是对于本文的其余部分,它们将被称为点特征表示(point feature representations)。
……
通过包含周围的邻域,可以在特征表达式中推断和捕获底层采样的表面几何形状,有助于解决模糊度比较问题。理想情况下,对于在相同或相似表面上的点,其结果特征将非常相似(相对于某些度量),而对于位于不同表面上的点,结果将非常不同,如下图所示。一个好的点特征表示与一个坏的点特征表示不同,它能够在以下情况下捕获相同的局部表面特征:

  • 刚性变换(rigid transformations)——即数据中的三维旋转和三维平移不应影响最终的特征向量F估计;

  • 改变采样密度(varying sampling density)——原则上,采样密度或多或少的局部表面贴片应该具有相同的特征向量特征;

  • 噪声(noise)——点特征表示必须在其特征向量中保留相同或非常相似的值,即使在数据中存在轻微的噪声。
    在这里插入图片描述通常,PCL特性使用近似方法计算查询点的最近邻居,使用快速的kd-tree查询。我们感兴趣的查询有两种类型:

  • 确定查询点(也称为k-search)的k(用户给定参数)临近点;

  • 确定半径为r的球体(也称为radius-search)内查询点的所有临近点。

注意
关于选取合适的kr值,请参阅Rusu论文

术语

为了让大家更好的理解这篇文章,我们将使用一些缩写和符号来简化文本解释。请参阅下表,以了解所使用的每个术语。

术语解释
Foo一个类名
FooPtr一个指向类Foo的boost共享指针,如 boost::shared_ptr
FooConstPtr一个指向类Foo的const boost共享指针,如const boost::shared_ptr

传递输入

由于PCL中几乎所有继承自基本PCL::PCLBase类的类,PCL::Feature类以两种不同的方式接受输入数据:

  • 1.一个完整的点云数据集,通过setInputCloud (PointCloudConstPtr &)提供 - 强制性
    任何特征估计类,试图估计给定输入云中的每个点的特征。
  • 2.点云数据集的子集,通过setInputCloud (PointCloudConstPtr &)和setIndices (IndicesConstPtr &)提供——可选
    任何特征估计类都将尝试估计给定输入云中在给定索引列表中有索引的每个点的特征。默认情况下,如果没有给定一组索引,则将考虑云中的所有点。*

此外,可以通过另一个调用setSearchSurface (PointCloudConstPtr &)指定要使用的点邻居集。这个调用是可选的,当搜索面没有给定时,默认情况下使用的是输入点云数据集。
因为setInputCloud()总是必需的,所以我们可以使用创建最多四种组合。说我们有两个点云,P = {p_1、p_2…p_n}和Q = {q_1, q_2,…, q_n}。下图展示了所有四种情况:
在这里插入图片描述

  • setIndices() = false, setSearchSurface() = false——毫无疑问,这是PCL中最常用的情况,在PCL中,用户只是在一个点云数据集中提供数据,并期望在云中所有点上估计某个特性。
    因为我们不期望根据给定的索引集和/或搜索面来维护不同的实现副本,所以当索引= false时,PCL创建一组内部索引(例如std::vector),这些索引基本上指向整个数据集(索引=1……N,其中N为云中点的个数)。
    在上图中,这对应于最左边的情况。首先,我们估计p_1的最近邻域,然后是p_2的最近邻域,以此类推,直到我们耗尽P中的所有点。
  • setIndices() = true, setSearchSurface() = false—如前所述,特征估计方法只计算给定索引向量中具有索引的点的特征;
    在上图中,这对应于第二种情况。这里,我们假设p_2的下标不是给定下标向量的一部分,所以在p2处没有邻居或特征。
  • setIndices () = false, setSearchSurface () = true,在第一种情况下,将为所有点估计作为输入,但底层邻近表面在setSearchSurface()将被用来获取最近的邻居的输入点,而不是输入云本身;
    在上图中,这对应于第三种情况。如果Q={q_1, q_2}是另一个给定的云作为输入,与P不同,而P是Q的搜索面,则从P计算q_1和q_2的邻域。
  • setIndices() = true, setSearchSurface() = true——这可能是最罕见的情况,其中索引和搜索表面都给出了。在本例中,使用setSearchSurface()中给出的搜索表面信息,仅对对中的一个子集的特征进行估计。
    最后,在上图中,这对应于最后一种情况(最右边)。这里,我们假设q_2的指标不属于给定的Q的指标向量的一部分,所以在q2处没有邻居或特征值被估计。

当我们有一个非常密集的输入数据集,但我们不想估计所有特征点,而是在pcl_keypoints一些要点发现使用方法,或者downsampled版本的云(例如,使用pcl: VoxelGrid < T >过滤获得),使用setSearchSurface()效果最为明显。在本例中,我们通过setInputCloud()传递向下采样的/关键点输入,并将原始数据作为setSearchSurface()传递。

一个法向估计示例

一旦确定了查询点的邻点,就可以使用该邻点的邻点来估计局部特征表示,该特征表示捕获查询点周围的底层采样曲面的几何形状。描述曲面几何的一个重要问题是首先在坐标系中推断曲面的方向,即估计曲面的法向。表面法向是一个表面的重要属性,在许多领域被广泛应用,如计算机图形应用程序,应用正确的光源产生阴影和其他视觉效果(见Rusu论文获得更多信息)。
下面的代码片段将为输入数据集中的所有点估计一组表面法线。

#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 ...

  // 创建normal estimate类,并将输入数据集传递给它
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  //创建一个空的kdtree表示,并将其传递给普通的评估对象。
  // 它的内容将根据给定的输入数据集填充到对象中(因为没有其他搜索表面)。
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // 输出数据集
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // 使用半径为3cm的球体中的所有相邻点
  ne.setRadiusSearch (0.03);

  // 计算特征
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () 应该 cloud->points.size ()的大小一样
}

下面的代码片段将为输入数据集中的一个子集估计一组表面法线。

#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 ...

  //创建一组要使用的索引。为了简单起见,我们将使用云中的前10%的点
  std::vector<int> indices (std::floor (cloud->points.size () / 10));
  for (size_t i = 0; i < indices.size (); ++i) indices[i] = i;

  // 创建normal estimate类,并将输入数据集传递给它
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

  // 传递索引
  boost::shared_ptr<std::vector<int> > indicesptr (new std::vector<int> (indices));
  ne.setIndices (indicesptr);

  //创建一个空的kdtree表示,并将其传递给普通的评估对象。
  // 它的内容将根据给定的输入数据集填充到对象中(因为没有其他搜索表面)。
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // 输出数据集
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // 使用半径为3cm的球体中的所有相邻点
  ne.setRadiusSearch (0.03);

  // 计算特征
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () 应该 cloud->points.size ()的大小一样
}

最后,下面的代码片段将为输入数据集中的所有点估计一组表面法线,但将使用另一个数据集估计它们的最近邻居。正如前面提到的,当输入是曲面的下采样版本时,这是一个很好的用途。

#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 ...

  // 创建normal estimate类,并将输入数据集传递给它
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud_downsampled);

将原始数据(下采样前)作为搜索面传递
  ne.setSearchSurface (cloud);

  // 创建一个空的kdtree表示,并将其传递给普通的评估对象。
  //它的内容将根据给定的表面数据集填充到对象中。
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
  ne.setSearchMethod (tree);

  // 输出数据集
  pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);

  // 使用半径为3cm的球体中的所有相邻点
  ne.setRadiusSearch (0.03);

  // 计算特征
  ne.compute (*cloud_normals);

  // cloud_normals->points.size () 应该 cloud->points.size ()的大小一样
}

注意
@博士论文{RusuDoctoralDissertation, 作者 = {Radu Bogdan Rusu}, 题目 = {Semantic 3D Object Maps for Everyday Manipulation in Human Living Environments}, 学校 = {Computer Science department, Technische Universitaet Muenchen, Germany}, year = {2009}, month = {October} }

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值