特征描述与提取Features

       3D点云特征描述与提取是点云信息处理中的最基础也是最关键的部分,点云的识别、分割、重采样、配准、曲面重建等大部分算法,都十分依赖特征描述与提取的结果。

       从尺度上来划分,一般分为局部特征描述全局特征描述。例如局部的法线等几何形状特征的描述,全局的拓扑特征描述,都属于3D点云特征描述与提取范畴。在 PCL 中,目前已有很多基本的特征描述子与提取算法。

       广泛使用的几何点特征的示例是下图的表面在查询点p处的估计曲率和法线。被认为是局部特征,因为它们使用由其k个最近邻点邻提供的信息来表征点。为了有效地确定这些邻点,输入数据集通常使用空间分解技术(例如八叉树或kD树)分割成更小的块( 左:kD-tree,右:八叉树),然后执行在那个空间里最近点的搜索。

特征点 

        理想情况下,在使用同一种度量规则情况下,相同或相似的表面上的点的特征值应该非常相似,不同表面上的点的特征描述子有明显的差异。通过以下几个条件的变化仍能够获取相同或相似的局部表面特征,则说明该特征表示方式比较优秀:

  • 刚性变换(rigid transformations):即数据中的3D旋转和3D平移不应影响结果特征向量F的估计
  • 多种采样密度(varying sampling density):原则上,在一个局部表面或多或少采样密度的应具有相同的特征向量
  • 噪声(noise):在数据中存在轻微噪声的情况下,由特征点描述的特征向量必须相同或非常相近

        通常,PCL功能使用近似的方法通过快速的kd-tree查询来计算查询点的最近邻点,我们感兴趣的查询有两种:

  • k搜索:确定查询点的k个(用户给定参数)邻点
  • 半径搜索:确定半径为r的球面内查询点的所有邻点。

在PCL中实现的所有特征点算法

详细中文博客文章

3D 特征点概述(1)https://www.cnblogs.com/li-yao7758258/p/9350340.html

3D 特征点概述(2)https://www.cnblogs.com/li-yao7758258/p/9350746.html

PCL点云特征描述与提取(1)https://www.cnblogs.com/li-yao7758258/p/6479255.html

PCL点云特征描述与提取(2)https://www.cnblogs.com/li-yao7758258/p/6481668.html

PCL点云特征描述与提取(3)https://www.cnblogs.com/li-yao7758258/p/6481738.html

PCL点云特征描述与提取(4)https://www.cnblogs.com/li-yao7758258/p/6483721.html

        下表列出了关于各种特征点性质属性,以及应用的场景:

Feature NameSupports Texture / ColorLocal / Global / RegionalBest Use Case
PFHNoL
FPFHNoL2.5D Scans (Pseudo single position range images)
VFHNoGObject detection with basic pose estimation
CVFHNoRObject detection with basic pose estimation, detection of partial objects
RIFTYesLReal world 3D-Scans with no mirror effects. RIFT is vulnerable against flipping.
RSDNoL
NARFNoL2.5D (Range Images)

PFH (Point Feature Histogram)

        是一种局部特征点,PFH扩展了先前关于Surflet-Pair-Relation Histograms的工作(Wahl等人)。

  • Surflet-pair-relation histograms: a statistical 3D-shape representation for rapid classification

输入格式:

(1)由一组带有方向的点P组成的点云。有方向意味着所有点都具有正常的N(法向量)。
(2)此功能不使用颜色信息

工作原理:

(1)迭代点云P中的点。
(2)对于输入云中的每个点Pi(i是迭代索引),收集具有半径r的Pi周围的球体内的所有相邻点。 这个集合称为Pik(k为k个邻点)。
(3)循环关于Pik中的两对点,比如p1和p2。 法线与矢量p1-p2的角度较小的点是源点ps,另一个是目标点pt。
(4)计算四个特征,它们一起表示目标点pt处的平均曲率。 将它们组合并放入等效的直方图箱中。

简短概述
(1)为P中的所有的点云计算法线。
(2)估计P中的点Pi的特征:获取围绕点Pi(Pik)的半径r中的k个邻居的集合。在两点之间计算四个特征。相应的bin增加1。生成点特征直方图(PFH)。
(3)将得到的直方图组与其他点云的组进行比较,以便找到对应关系。 

FPFH (Fast Point Feature Histogram)

        FPFH也是一种局部特征点,且是PFH(Point Feature Histogram)的一种扩展描述。

输入格式:

(1)由一组定向点P组成的点云。定向意味着所有点都具有正常的n(法向量)。
(2)此功能不使用颜色信息。(这些都和PFH一样的要求)

工作原理:
由于FPFH来自PFH,因此它的工作方式非常相似。但是有一些优化步骤使FPFH更快:

(1)迭代点云集 P 中的所有点云。
(2)对于输入点云中的每个点Pi(i是迭代索引),收集具有半径r的Pi周围的球体内的所有相邻点。这个集合称为Pik(k为k个邻点)。
(3)该循环仅将点Pi与其每个邻点相关联。在这样的一对Pi对中,法线与矢量p1-p2的角度小的点是源点ps,另一个是目标点pt。
(4)计算三个特征(PFH中的四个,Ps和Pt之间的距离被遗漏),它们一起表示目标点pt处的平均曲率。将它们组合并放入等效的直方图箱中。
(5)与PFH中一样,仅考虑查询点Pi与其邻点之间的直接对(计算量少得多),得到的直方图称为SPFH(简单点特征直方图)。
(6)最后一步是新的:要重新补偿“丢失”连接,相邻的SPFH将根据其空间距离添加到Pi的SPFH中。

简短概述:
(1)为P中的所有点计算法线。
(2)估计P中的点Pi的特征:获取围绕点Pi(Pik)的半径r中的k个邻点的集合。在两点之间计算三个特征(仅在Pi与其邻点之间)。相应的bin增加1。生成简单的点特征直方图(SPFH)。
(3)为了达到更多的点和连接(最多2次r),邻点的SPFH根据它们的空间距离加以加权作为最后一步。
(4)可以将得到的直方图组与其他点云的组进行比较,以便找到对应关系。 

 VFH (Viewpoint Feature Histogram)

        VFH是一种全局特征点,扩展了FPFH的一种方法。

输入格式:

(1)由一组定向点P组成的点云。定向意味着所有点都具有正常的n法向量。
(2)此功能不使用颜色信息。

工作原理:

(1)计算点云的质心pc及其法向量nc。计算视点和质心之间的向量vc并对其进行标准化。
(2)VFH由两部分组成:视点部分和扩展的FPFH模块。
(3)要映射视点分量,迭代P中的所有点云,并计算它们的法线以及vc之间的角度。增加相应的直方图bin。
(4)对于扩展的FPFH组件,只需计算质心pc处的FPFH,将整个周围点云P设置为邻居。
(5)将两个直方图一起添加。

 简短概述:
(1)估算点云中的质心及其法线。计算视点和视点之间的归一化矢量vc。
(2)对于所有点,计算它们的法线和vc之间的角度。
(3)估算质心的FPFH,将所有剩余点设置为邻点。

 CVFH (Clustered Viewpoint Feature Histogram)

        CVFH是一种基于区域描述的特征点,也是基于VFH扩展的方法。

 输入格式:

(1)由一组定向点P组成的点云。定向意味着所有点都具有正常的法向量n。
(2)此功能不使用颜色信息。

工作原理:
(1)由于遮挡和传感器限制,我们使用传感器获取的是物体的2.5D点云数据,一旦缺失部分点云,计算整个点云的点和正常质心的结果可能完全不同。这就是为什么一旦点云中缺少必要点,VFH描述符也就会完全不同。
(2)CVFH创建稳定的集群区域。从点云P开始,新的簇Ci从尚未分配给任何簇的随机点Pr开始。如果Ci中存在点Pj,则P中的每个点Pi被分配给该簇,使得它们的法线类似并且它们处于直接邻域中(比较角度和距离阈值)。聚类簇群太少的集群被拒绝或忽略。
(3)计算每个群集上的VFH。
(4)为每个直方图添加形状分布,表示点如何围绕质心分布。

 简短概述:
(1)将点云细分为具有相似法线的相邻点的聚类(稳定区域)。
(2)计算每个群集的VFH。
(3)将形状分布组件(SDC)添加到每个直方图。

RIFT (Rotation-Invariant Feature Transform)

        RIFT是一种局部特征描述法,且该方法扩展于SIFT。

 输入格式:

(1)由一组纹理点组成的点云P。没有纹理,此功能不会产生任何可用的结果。
(2)强度梯度计算。

工作原理:
(1)迭代点云P中的所有点。
(2)对于输入点云中的每个点Pi(i是迭代索引),收集具有半径r的Pi周围的球体内的所有相邻点。这个集合称为Pik(k为k个邻点)。
(3)具有n个片段的假想圆(球体的垂直于Pi的法线的投影)适合于表面。这里n对应于实现中的距离 bin 的数量。
(4)Pi的所有邻点根据它们的距离d <n和梯度角位置θ<g(g表示实现中的梯度区的数量)被分配给直方图区间。 θ是梯度方向和从中心向外指向圆的矢量之间的角度。

简短概述:
(1)对于P样本中的每个点Pi,Pi周围的所有k个邻点。
(2)根据距离d和它们的梯度角θ将所有邻点分配给直方图。
(3)可以将得到的直方图组与其他点云的组进行比较,以便找到对应关系。

 RSD (Radius-based Surface Descriptor)

        是一种局部特征点。

输入格式:
(1)由一组带有方向信息点P组成的点云。带有方向意味着所有点都具有正常的n法线。
(2)此功能不使用颜色信息。

工作原理:
(1)迭代点云P中的点。
(2)对于输入云中的每个点Pi(i是迭代索引),收集具有半径r的Pi周围的球体内的所有相邻点。这个集合称为Pik(k为k个邻居)
(3)对于Pik中的每个邻居Pikj,计算Pi和Pikj之间的距离以及它们的法线之间的角度。这些值被分配给表征点Pi处的曲率的直方图。
(4)使用这些值,可以通过两个点拟合具有近似半径rc的假想圆(见图)。请注意,当两个点位于平面上时,半径将变为无穷大。
(5)由于查询点Pi可以是多个圆的一部分,其邻点仅保持最小和最大半径并将其分配给Pi作为输出。该算法接受最大半径参数,在该参数之上,点将被视为平面。

简短概述:
(1)对于P样本中的每个点Pi,Pi周围的所有k个邻居。
(2)根据距离d和无向法线的角度将所有邻居分配到直方图。
(3)假设与每个邻居的Pi对描述一个圆(见图)。找到Pi描述的所有球体的最小和最大半径及其邻域。
(4)得到的直方图和半径组可以与其他点云的组合进行比较,以便找到对应关系。

NARF (Normal Aligned Radial Feature)

        这是一个局部特征点,NARF功能扩展了SIFT(Lowe)的一些概念。

输入格式:
(1)场景的深度图像RI。
(2)NARF不仅是描述符,还是检测器。可以首先在数据集上运行兴趣点检测器再进行描述子描述。

工作原理:
(1)迭代深度图像RI中的所有兴趣点。
(2)对于每个点,Pi通过沿着法线看它来创建一个小图像补丁。法线是图像块的局部坐标系的Z轴,其中Pi位于(0,0)。Y轴是世界坐标系Y轴。X轴相应对齐。围绕Pi的半径r内的所有邻居都被转移到该局部坐标系中。
(3)具有n个光束的星形图案投射在图像块上。对于每个波束,计算[-0.5,0.5]中的分数。如果在梁下方的细胞中存在大量强度变化,则束具有高分。这是通过将每个单元与下一个单元进行比较来计算的。另外,靠近中心的细胞有助于得分具有更高的重量(中间2个,边缘1个)。
(4)最后,计算补片的主导方向,使其对法线周围的旋转不变。

简短概述:
(1)对于深度图像RI中的每个关键点Pi,对Pi周围的所有邻点进行采样,并将它们转换为局部坐标系,其中Pi为O。
(2)在图像块上投射星形图案并计算每个光束下的强度变化以获得光束的分数。 在计算中,更靠近中心的光束具有更大的权重。 分数归一化为[-0.5,0.5]。
(3)迭代所有光束并找到图像块的主要方向。

ESF (Ensemble of Shape Functions)(拓展一个特征点)

        是一种局部特征点。

输入格式:
(1)由一组点P组成的点云。
(2)此功能不使用颜色信息。

工作原理:
(1) 启动一个循环,从点云P中采样20000点。
(2) 每次迭代都会对三个随机点Pri,Prj,Prk进行采样。
(3) D2:对于D2函数,计算Pri和Prj之间的距离。然后检查连接两点的线是否完全位于表面(IN),表面外(OUT)或两者(MIXED)。在先前计算的距离箱处增加D2的子组合图之一(IN,OUT或MIXED)。当采样三个点时,可以在该迭代中计算另外两个距离。
(4) D2比率:还有另一个直方图,可以捕获位于表面和自由空间中的每条线的各部分之间的比率。
(5) D3:对于D3函数,计算Pri,Prj和Prk之间三角形区域的平方根。这相当于D2,因为该区域也分为IN,OUT和MIXED。增加D3直方图的相应直方图区间。
(6) A3:对于A3函数计算三点之间的角度。此功能再次分为IN,OUT和MIXED。这次使用与角度相反的线。增加相应的A3直方图bin。
(7)在循环结束时,我们得到一个包含10个子图形(每个64个子区)的全局描述符:D2(IN,OUT,MIXED,比率),D3(IN,OUT,MIXED),A3(IN,OUT,MIXED) 。

 简短概述:
(1)开始一个从点云P中随机抽样20000点的循环。每轮样本三点Pri,Prj,Prk。
(2)对于两个点对,计算彼此之间的距离,并检查两者之间的线是否位于表面上,外部或与物体相交(IN,OUT或MIXED)。在D2的三个子图表中的一个中增加与计算的距离对应的bin。
(3)对于前一行,找到位于表面或外部的那条线的部分之间的比率。结果应该是0表示完全在外面,1表示完全在表面上,并且来自MIXED线的所有值都在它们之间分布。增加D2比率直方图的对应bin。
(4)对于三元组,建立一个三角形并计算两侧之间的角度,并将角度的一侧分为三角形(IN,OUT,MIXED)。增加A3的IN,OUT或MIXED子组合图中的相应角度箱。
(5)对于前一个三角形,计算区域的平方根,并将区域分为IN,OUT或MIXED。增加D3的IN,OUT或MIXED子组合图中的相应区域bin。

如何传递输入参数

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

  1. 通过setInputCloud(PointCloudConstPtr&)给出的整个点云数据集, 任何试图在给定输入云中的每个点上进行特征估计。
  2. 通过setInputCloud(PointCloudConstPtr&)setIndices(IndicesConstPtr&)(可选)给出的点云数据集的子集, 所有特征估计方式都将用来尝试估计输入云中每个在indices索引列表中点的特征。默认情况下,如果未给出索引集,则将考虑云中的所有点。

        另外,可以通过额外的调用setSearchSurface(PointCloudConstPtr&)来指定要搜索使用的点邻域集。此调用是可选的,如果未提供搜索范围的情况下,默认将使用输入点云数据集。由于始终需要setInputCloud(),因此可以使用<setInputCloud(),setIndices(),setSearchSurface()>创建多达四个组合。假设我们有两个点云,P={p1,p2,…pn}P={p1,p2,…pn}和Q={q1,q2,…,qn}Q={q1,q2,…,qn}。下图显示了所有四种情况:

  • setIndices() = false, setSearchSurface() = false

    毫无疑问,这是PCL中最常用的情况,用户仅输入单个PointCloud数据集,并期望在云中的所有点处估计某个特征。由于我们不希望根据是否给出一组indices(索引)和/或search surface(搜索表面)来维护不同的实现副本,因此每当indices = false时,PCL就会创建一组内部索引(作为std::vector <int>)基本上指向整个数据集(索引= 1..N,其中N是云中的点数)。

    在上图中,这对应于最左边的情况。首先,我们估计p1的最近邻点,然后估计p2的最近邻点,依此类推,直到用尽P中的所有点。

  • setIndices() = true, setSearchSurface() = false

    如前所述,特征估计方法将仅计算在给定indices(索引) 的vecter中具有索引的那些点的特征;

    在上图中,这对应于第二种情况。在此,我们假设p2的索引不是给定索引vector的一部分,因此不会在p2处估计任何邻点或特征。

  • setIndices() = false, setSearchSurface() = true

    与第一种情况一样,将对输入的所有点进行特征估计,但是setSearchSurface()中给定的基础相邻表面将用于获取输入的最近邻居点,而不是输入云本身;

    在上图中,这对应于第三种情况。如果Q={q1,q2}Q={q1,q2}是作为输入云,把P设置为Q的search surface搜索面,则将从P计算q1和q2的邻点。

  • setIndices() = true, setSearchSurface() = true

    这是最罕见的情况,同时给出了indices索引和search surface搜索表面。在这种情况下,将使用setSearchSurface中提供的搜索表面信息来计算<input, indices>中的子集的特征。

    在上图中,这对应于最右边的情况。在此,我们假设q2的索引不属于为Q指定的索引向量,因此在q2处不会估计出任何邻点或特征。

        使用setSearchSurface的最有用的情况是:当我们有一个非常密集的输入数据集,但我们不想估计其中所有点的特征,而是要估计使用pcl_keypoints中的方法发现的某些关键点,或者在点云的降采样版本上(例如,使用pcl::VoxelGrid <T>过滤器获得的降采样点云)。在这种情况下,我们通过setInputCloud传递降采样/关键点输入,并将原始数据作为setSearchSurface传递,这样可以大幅提高效率。

法向量估算示例

法向量估算实现(1)

        查询点的相邻点可以用于估计局部特征表示,该局部特征表示捕获查询点周围的基础采样表面的几何形状。描述表面几何形状的一个重要问题是首先推断其在坐标系中的方向,即估算其法线。表面法线是表面的重要属性,在许多领域(例如计算机图形应用程序)中大量使用,以应用正确的光源来生成阴影和其他视觉效果(有关更多信息,参见[RusuDissertation])。 以下代码片段将为输入数据集中的所有点估计一组表面法线。

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

  // 准备一个indices索引集合,为了简单起见,我们直接使用点云的前10%的点
  std::vector<int> indices (std::floor (cloud->points.size () / 10));
  for (std::size_t i = 0; i < indices.size (); ++i) indices[i] = i;

  // 创建法向量估算类,设置输入点云
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

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

  // 创建一个空的kdtree,将值传递给法向量估算对象
  // 这个tree对象将会在ne内部根据输入的数据集进行填充(这里设置没有其他的search surface)
  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 ()
}

法向量估算实现(2)

        以下代码片段将会从一个点云的子集中进行表面法线估算:

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

  // 准备一个indices索引集合,为了简单起见,我们直接使用点云的前10%的点
  std::vector<int> indices (std::floor (cloud->points.size () / 10));
  for (std::size_t i = 0; i < indices.size (); ++i) indices[i] = i;

  // 创建法向量估算类,设置输入点云
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud);

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

  // 创建一个空的kdtree,将值传递给法向量估算对象
  // 这个tree对象将会在ne内部根据输入的数据集进行填充(这里设置没有其他的search surface)
  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 ()
}

法向量估算实现(3)

        以下代码片段将为输入数据集中的所有点估算一组表面法线,但将使用另一个数据集(原始点云)估计其最近的邻点。如前所述,使用降采样版本作为输入input cloud是一个很好的方式。

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

  // 创建法向量估算类,将降采样后的数据作为输入点云
  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  ne.setInputCloud (cloud_downsampled);

  // 传入降采样之前的原始数据作为search surface
  ne.setSearchSurface (cloud);

  // 创建一个空的kdtree,将值传递给法向量估算对象
  // 这个tree对象将会在ne内部根据输入的数据集进行填充(这里设置没有其他的search surface)
  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()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小镇种田家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值