https://docs.opencv.org/3.3.1/d9/d25/group__surface__matching.html
表面匹配简介
具有3D结构感觉能力的相机和类似设备正变得越来越普遍。 因此,使用深度和强度信息来匹配3D对象(或部件)对于计算机视觉至关重要。 应用范围从工业控制到指导视障人士的日常行为。 范围图像中的识别和姿势估计的任务旨在通过将所查询的3D自由形式对象与所获取的数据库匹配来识别和定位所述3D自由形式对象。
从工业角度来看,使机器人能够从垃圾箱中自动定位和拾取随机放置和定向的物体是工厂自动化中的一项重要挑战,取代了繁琐而繁重的手工劳动。 系统应该能够识别和定位具有预定义形状的物体,并以夹持机器人拾取它所需的精度估计位置。 这是视觉引导机器人占据舞台的地方。 类似的工具还能够通过非结构化环境引导机器人(甚至是人),从而实现自动导航。 这些属性使得点云的3D匹配无处不在。 在此背景下,我现在将描述使用3D特征的3D对象识别和姿势估计算法的OpenCV实现。
基于三维特征的曲面匹配算法
为了实现任务3D匹配,算法的状态在很大程度上基于[41] ,这是该领域中提出的第一个和主要的实用方法之一。 该方法包括从深度图像或通用点云中随机提取3D特征点,索引它们以及稍后在运行时有效地查询它们。 仅考虑3D结构,并且使用普通哈希表进行特征查询。
在充分意识到利用漂亮的CAD模型结构以实现智能点采样的同时,我现在将其放在一边,以便尊重方法的普遍性(通常,对于这样的算法,不需要在CAD模型上进行培训,点云就足够了)。 以下是整个算法的概要:
算法概要
如上所述,该算法依赖于点对特征的提取和索引,其定义如下:
\ [\ bf {{F}}(\ bf {{m1}},\ bf {{m2}})=(|| \ bf {{d}} || _2,<(\ bf {{n1}} ,\ bf {{d}}),<(\ bf {{n2}},\ bf {{d}}),<(\ bf {{n1}},\ bf {{n2}}))\]
其中\(\ bf {{m1}} \)和\(\ bf {{m2}} \)是模型(或场景)上两个选定点的特征,\(\ bf {{d}} \)是差异向量,\(\ bf {{n1}} \)和\(\ bf {{n2}} \)是\(\ bf {{m1}} \)和\(\ bf {m2} \的法线)。 在训练阶段,该矢量被量化,索引。 在测试阶段,从场景中提取相同的特征并与数据库进行比较。 通过诸如旋转分量的分离之类的一些技巧,姿势估计部分也可以变得有效(检查参考以获得更多细节)。 使用霍夫式投票和聚类来估计对象姿势。 为了对姿势进行聚类,原始姿势假设按投票数的递减顺序排序。 从最高投票开始,创建一个新集群。 如果下一个姿势假设接近现有聚类之一,则将该假设添加到聚类中,并将聚类中心更新为聚类内姿势假设的平均值。 如果下一个假设不接近任何群集,则会创建新群集。 接近测试是在平移和旋转中使用固定阈值完成的。 用于平移的距离计算和平均在3D欧几里德空间中执行,而用于旋转的距离计算和平均是使用四元数表示来执行的。 在聚类之后,聚类按照总投票数的降序排序,这决定了估计姿势的置信度。
使用\(ICP \)进一步细化该姿势以获得最终姿势。
类似霍夫的投票计划
如概要所示,在训练阶段期间,从模型中提取PPF(点对特征),量化,存储在散列表中并索引。 然而,在运行时期间,在输入场景上执行类似的操作,除了这次执行对哈希表的相似性查找而不是插入。 此查找还允许我们计算场景对的地平面变换。 在此之后,计算姿势的旋转分量减少到差值的计算\(\ alpha = \ alpha_m- \ alpha_s \)。 该组件带有关于对象姿势的提示。 在局部模型坐标向量和\(\ alpha \)上执行类似霍夫的投票方案。 为每个场景点实现的最高姿势让我们恢复对象姿势。
PPF匹配的源代码
// pc是模型的加载点云
//(Nx6)和pcTest是一个加载点云
//场景(Mx6)
ppf_match_3d :: PPF3D检测器检测器(0.03,0.05);
detector.trainModel(PC);
vector <Pose3DPtr>结果;
detector.match(pcTest,results,1.0 / 10.0,0.05);
cout << “Poses:” << endl;
//打印姿势
for ( size_t i = 0; i <results.size(); i ++)
{
Pose3DPtr pose = results [i];
cout << “姿势结果” << i << endl;
pose-> printPose();
}
通过ICP进行注册
匹配过程终止于达到姿势。 然而,由于多个匹配点,错误假设,姿势平均等等,这种姿势对噪声非常开放,并且很多时候远非完美。 尽管在该阶段获得的视觉结果令人满意,但定量评估显示出((10)度变化(误差),这是可接受的匹配水平。 很多时候,要求可能远远超出此边界,并且希望改进计算出的姿势。
此外,在典型的RGBD场景和点云中,由于场景中的可见性,3D结构仅能捕获不到一半的模型。 因此,可以快速且正确地记录遮挡和部分可见形状的稳健姿态细化算法不是不切实际的愿望。
在这一点上,一个简单的选择是使用众所周知的迭代最近点算法。 然而,基本ICP的使用导致收敛慢,配准不良,异常值敏感以及未能记录部分形状。 因此,它绝对不适合这个问题。 出于这个原因,已经提出了许多变体。 不同的变体有助于姿势估计过程的不同阶段。
ICP由\(6 \)阶段组成,我为每个阶段提出的改进总结如下。
采样
为了提高收敛速度和计算时间,通常使用比模型实际具有的更少的点。 然而,将正确的点采样到寄存器本身就是一个问题。 天真的方式是统一采样并希望获得合理的子集。 更聪明的方法试图找出关键点,这些关键点被认为对注册过程有很大贡献。 Gelfand等。 人。 利用协方差矩阵来约束本征空间,从而使用影响平移和旋转的一组点。 这是一种巧妙的子采样方式,我将在实现中选择使用它。
通讯搜索
顾名思义,这一步实际上是以最接近的方式分配数据和模型中的点。 正确的分配将导致正确的姿势,错误的分配会严重降低结果。 通常,KD树用于搜索最近邻居,以提高速度。 然而,这不是最优保证,并且很多时候会导致错误的点匹配。 幸运的是,分配在迭代中得到纠正。
为了克服一些限制,Picky ICP [209]和BC-ICP(使用双独特对应的ICP )是两种众所周知的方法。 Picky ICP首先以老式方式找到对应关系,然后在得到的对应对中找到对应关系,如果将多个场景点\(p_i \)分配给同一个模型点\(m_j \),则选择\(p_i \ )对应于最小距离。 另一方面,BC-ICP首先允许多个对应,然后通过建立双唯一对应来解决分配。 它还定义了一种新颖的无对应异常值,它本质上简化了识别异常值的过程。
作为参考,使用两种方法。 由于P-ICP速度稍快,性能缺点不显着,因此它将成为对应物再加工的首选方法。
对称加权
在我的实现中,我目前不使用加权方案。 但常见的方法包括正常的兼容性*(\(w_i = n ^ 1_i \ cdot n ^ 2_j \))或为较远距离的点对分配较低的权重(\(w = 1- \ frac {|| dist(m_i, S_I)|| _2} {{dist_最大}} \))。
拒绝对
基于标准偏差的稳健估计,使用动态阈值来完成拒绝。 换句话说,在每次迭代中,我找到了Std的MAD估计值。 开发。 我将其表示为\(mad_i \)。 我拒绝距离为(d_i> \ tau mad_i \)的对。 这里\(\ tau \)是拒绝的阈值,默认设置为\(3 \)。 在Picky细化之前应用加权,在前一阶段进行了解释。
错误度量标准
如上所述,使用[108]误差度量中的点到平面的线性化。 这既加快了注册过程又提高了收敛速度。
最小化
尽管提出了许多非线性优化器(例如Levenberg Mardquardt),但由于前一步骤中的线性化,姿态估计减少到求解线性方程组。 这就是我用DECOMP_SVD选项完全使用cv :: solve 。
ICP 算法
已经描述了上述步骤,这里我总结了ICP算法的布局。
通过点云金字塔的高效ICP
虽然现在提出的变体很好地处理了一些异常值和错误的初始化,但它们需要大量的迭代。 然而,多分辨率方案可以通过允许注册从粗略级别开始并传播到更低和更精细的级别来帮助减少迭代次数。 这种方法既改善了性能又提高了运行时间。
搜索以分层方式通过多个级别完成。 注册从一组非常粗略的模型样本开始。 迭代地说,这些要点是经过密集和追求的。 在每次迭代之后,先前估计的姿势被用作初始姿势并且用ICP进行细化。
使用ICP进行姿态细化的源代码
ICP icp(200,0.001f,2.5f,8);
//使用先前声明的pc和pcTest
//这将为每个姿势执行注册
//包含在结果中
icp.registerModelToScene(pc,pcTest,results);
//结果现在包含精致的姿势
结果
本节专门介绍曲面匹配的结果(点对特征匹配和以下ICP细化):
使用ppf + icp的单个青蛙模型的几个匹配
Mian数据集的不同模型匹配如下:
Mian数据集的不同模型匹配
您可以在此处查看 youTube上的视频。
一个完整的样本
参数调整
只要可能,表面匹配模块就会相对于模型直径(轴平行边界框的直径)处理其参数。 这使得参数独立于模型大小。 这就是为什么模型和场景云都被二次采样,使得所有点的最小距离为\(RelativeSamplingStep * DimensionRange \),其中\(DimensionRange \)是沿给定维度的距离。 所有三个维度都以类似的方式进行采样。 例如,如果\(RelativeSamplingStep \)设置为0.05且模型直径为1m(1000mm),则从对象表面采样的点将相距约50 mm。 从另一个角度来看,如果采样RelativeSamplingStep设置为0.05,则最多生成\(20x20x20 = 8000 \)模型点(取决于模型填充卷的方式)。 因此,这导致最多8000x8000对。 实际上,由于模型不是均匀分布在矩形棱镜上,因此可以预期的点数要少得多。 减小此值可以产生更多的模型点,从而获得更准确的表示。 但是,请注意,要计算的点对特征的数量现在是二次增加的,因为复杂度为O(N ^ 2)。 对于32位系统而言,这尤其令人担忧,因为大型模型很容易超出可用内存。 通常,对于大多数应用,0.025 - 0.05范围内的值似乎是足够的,其中默认值为0.03。 (注意,这个参数与[41]中给出的不同。在[41]中,使用一个统一的长方体进行量化,模型直径用于采样参考。在我的实现中,长方体是一个矩形棱柱,每个维度都是独立量化的。我不参考直径,而是沿着各个维度。
从模型中删除异常值并最初准备理想模型是非常明智的。 这是因为,异常值直接影响相对计算并降低匹配精度。
在运行时阶段,场景再次由\(RelativeSamplingStep \)采样,如上所述。 但是这次,只有一部分场景点被用作参考。 此部分由参数\(RelativeSceneSampleStep \)控制,其中\(SceneSampleStep =(int)(1.0 / RelativeSceneSampleStep)\)。 换句话说,如果\(RelativeSceneSampleStep = 1.0 / 5.0 \),则子采样场景将再次被均匀地采样到点数的1/5。 此参数的最大值为1,增加此参数也会增加稳定性,但会降低速度。 同样,由于初始场景无关的相对采样,微调此参数并不是一个大问题。 当模型形状均匀地占据体积时,或者当模型形状在量化体积内的微小位置被压缩时(例如,八叉树表示将具有太多的空单元),这将仅是问题。
\(RelativeDistanceStep \)充当散列表的离散化步骤。 点对特征被量化以映射到散列表的桶。 这种离散化涉及乘法和整数的转换。 理论上调整RelativeDistanceStep可控制碰撞率。 请注意,散列表上的更多冲突导致估计不太准确。 减小此参数会增加量化的影响,但会开始将不相似的点对分配给相同的分档。 然而,增加它会减少对类似对进行分组的能力。 通常,因为在采样阶段,训练模型点均匀地选择,其距离由RelativeSamplingStep控制,RelativeDistanceStep预期等于该值。 同样,0.025-0.05范围内的值是明智的。 但是,此时,当模型密集时,建议不要降低此值。 对于嘈杂的场景,可以增加该值以提高匹配噪声点的鲁棒性。
________________________补充------------------------______________________________________________
PPF
机器人视觉中有一项重要人物就是从场景中提取物体的位置,姿态。图像处理算法借助Deep Learning 的东风已经在图像的物体标记领域耍的飞起了。而从三维场景中提取物体还有待研究。目前已有的思路是先提取关键点,再使用各种局部特征描述子对关键点进行描述,最后与待检测物体进行比对,得到点-点的匹配。个别文章在之后还采取了ICP对匹配结果进行优化。
对于缺乏表面纹理信息,或局部曲率变化很小,或点云本身就非常稀疏的物体,采用局部特征描述子很难有效的提取到匹配对。所以就有了所谓基于Point Pair 的特征,该特征使用了一些全局的信息来进行匹配,更神奇的是,最终的位姿估计结果并不会陷入局部最小值。详细可参见论文:Model globally, match locally: Efficient and robust 3D object recognition. 与 Going further with point pair features。SLAM的重要研究方向object based Slam 也声称使用了Point Pair Feature进行匹配。
为了更好的理解这种方法,而在pcl中也没有找到现成的算法,所以我自己用matlab实现了一遍。
算法的思想很简单:
0、ppf 特征为[d,<d,n1>,<d,n2>,<n1,n2>].
1、针对目标模型,在两两点之间构造点对特征F,如果有N个点,那么就有N*N个特征(说明此算法是O(N2)的),N*N个特征形成特征集F_Set
2、在场景中任意取1定点a,再任意取1动点b,构造ppf特征,并从F_set中寻找对应的,那么理想情况下,如果找到了完全匹配的特征,则可获得点云匹配的结果。
3、此算法是一种投票算法,每次匹配都能得到一个旋转角度,如果m个b都投票给了某一旋转角度则可认为匹配成功
这个算法最大的问题就是不停的采样会导致极大的计算量。不过算法本身确实可以匹配物体和场景。
ppf 特征的构建
1 function obj = ppf(point1,point2) 2 d = point1.Location - point2.Location; 3 d_unit = d/norm(d); 4 apha1 = acos(point1.Normal*d_unit'); 5 apha2 = acos(point2.Normal*d_unit'); 6 apha3 = acos(point1.Normal*point2.Normal'); 7 obj = [norm(d),apha1,apha2,apha3]; 8 end
ppf 特征集的构建
1 classdef modelFeatureSet < handle 2 %MODELFEATURESET 此处显示有关此类的摘要 3 % 此处显示详细说明 4 5 properties 6 FeatureTree 7 ModelPointCloud 8 Pairs 9 end 10 11 methods 12 function obj = modelFeatureSet(pt) 13 obj.ModelPointCloud = copy(pt.removeInvalidPoints()); 14 end 15 function growTree(self) 16 self.ModelPointCloud = pcdownsample(self.ModelPointCloud,'GridAverage',.1); 17 pt_size = self.ModelPointCloud.Count; 18 idx = repmat(1:pt_size,pt_size,1); 19 tmp1 = reshape(idx,pt_size*pt_size,1); 20 tmp2 = reshape(idx',pt_size*pt_size,1); 21 pairs = [tmp1,tmp2]; 22 rnd = randseed(1,1000,1,1,pt_size*pt_size); 23 pairs = pairs(rnd,:); 24 Features = zeros(size(pairs,1),4); 25 for i = 1:size(pairs,1) 26 Features(i,:) = ppf(self.ModelPointCloud.select(pairs(i,1)),... 27 self.ModelPointCloud.select(pairs(i,2))); 28 end 29 self.FeatureTree = createns(Features); 30 self.Pairs = pairs; 31 end 32 end 33 end
PFH
正如点特征表示法所示,表面法线和曲率估计是某个点周围的几何特征基本表示法。虽然计算非常快速容易,但是无法获得太多信息,因为它们只使用很少的几个参数值来近似表示一个点的k邻域的几何特征。然而大部分场景中包含许多特征点,这些特征点有相同的或者非常相近的特征值,因此采用点特征表示法,其直接结果就减少了全局的特征信息。本小节介绍三维特征描述子中的一位成员:点特征直方图(Point Feature Histograms),我们简称为PFH,本小节将介绍它的理论优势,从PCL实现的角度讨论其实施细节。PFH特征不仅与坐标轴三维数据有关,同时还与表面法线有关。
理论基础
PFH计算方式通过参数化查询点与邻域点之间的空间差异,并形成一个多维直方图对点的k邻域几何属性进行描述。直方图所在的高维超空间为特征表示提供了一个可度量的信息空间,对点云对应曲面的6维姿态来说它具有不变性,并且在不同的采样密度或邻域的噪音等级下具有鲁棒性。点特征直方图(PFH)表示法是基于点与其k邻域之间的关系以及它们的估计法线,简言之,它考虑估计法线方向之间所有的相互作用,试图捕获最好的样本表面变化情况,以描述样本的几何特征。因此,合成特征超空间取决于每个点的表面法线估计的质量。如图1所示,表示的是一个查询点(Pq)的PFH计算的影响区域,Pq 用红色标注并放在圆球的中间位置,半径为r,(Pq)的所有k邻元素(即与点Pq的距离小于半径r的所有点)全部互相连接在一个网络中。最终的PFH描述子通过计算邻域内所有两点之间关系而得到的直方图,因此存在一个O(k)的计算复杂性。
图1 查询点 的PFH计算的影响区域
为了计算两点Pi和Pj及与它们对应的法线Ni和Nj之间的相对偏差,在其中的一个点上定义一个固定的局部坐标系,如图2所示。
图2 定义一个固定的局部坐标系
使用上图中uvw坐标系,法线和之间的偏差可以用一组角度来表示,如下所示:
d是两点Ps和Pt之间的欧氏距离,。计算k邻域内的每一对点的四组值,这样就把两点和它们法线相关的12个参数(xyz坐标值和法线信息)减少到4个。
为每一对点估计PFH四元组,可以使用:
computePairFeatures (const Eigen::Vector4f&p1,const Eigen::Vector4f&n1,
const Eigen::Vector4f&p2,const Eigen::Vector4f&n2,
float&f1,float&f2,float&f3,float&f4);
有关其他详细信息,请见API文件。为查询点创建最终的PFH表示,所有的四元组将会以某种统计的方式放进直方图中,这个过程首先把每个特征值范围划分为b个子区间,并统计落在每个子区间的点数目,因为四分之三的特征在上述中为法线之间的角度计量,在三角化圆上可以将它们的参数值非常容易地归一到相同的区间内。一个统计的例子是:把每个特征区间划分成等分的相同数目,为此在一个完全关联的空间内创建有个区间的直方图。在这个空间中,一个直方图中某一区间统计个数的增一对应一个点的四个特征值。如图3所示,就是点云中不同点的点特征直方图表示法的一个例子,在某些情况下,第四个特征量d在通常由机器人捕获的2.5维数据集中的并不重要,因为临近点间的距离从视点开始是递增的,而并非不变的,在扫描中局部点密度影响特征时,实践证明省略d是有益的。
图3 点云中不同点的点特征直方图表示法
注意:更多相关信息和数学推导,包括不同几何体表面点云的PFH特征分析,请见[RusuDissertation]。
估计PFH特征
点特征直方图(PFH)在PCL中的实现是pcl_features模块的一部分。默认PFH的实现使用5个区间分类(例如:四个特征值中的每个都使用5个区间来统计),其中不包括距离(在上文中已经解释过了——但是如果有需要的话,也可以通过用户调用computePairFeatures方法来获得距离值),这样就组成了一个125浮点数元素的特征向量(35),其保存在一个pcl::PFHSignature125的点类型中。以下代码段将对输入数据集中的所有点估计其对应的PFH特征。
#include <pcl/point_types.h> //点类型头文件
#include <pcl/features/pfh.h> //pfh特征估计类头文件
...//其他相关操作
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud(newpcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptrnormals(newpcl::PointCloud<pcl::Normal>());
...//打开点云文件估计法线等
//创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它
pcl::PFHEstimation<pcl::PointXYZ,pcl::Normal,pcl::PFHSignature125>pfh;
pfh.setInputCloud(cloud);
pfh.setInputNormals(normals);
//如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
//创建一个空的kd树表示法,并把它传递给PFH估计对象。
//基于已给的输入数据集,建立kdtree
pcl::KdTreeFLANN<pcl::PointXYZ>::Ptrtree(newpcl::KdTreeFLANN<pcl::PointXYZ>());
pfh.setSearchMethod(tree);
//输出数据集
pcl::PointCloud<pcl::PFHSignature125>::Ptrpfhs(newpcl::PointCloud<pcl::PFHSignature125>());
//使用半径在5厘米范围内的所有邻元素。
//注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
pfh.setRadiusSearch(0.05);
//计算pfh特征值
pfh.compute(*pfhs);
// pfhs->points.size ()应该与input cloud->points.size ()有相同的大小,即每个点都有一个pfh特征向量
PFHEstimation类的实际计算程序内部只执行以下:
对点云P中的每个点p
1.得到p点的最近邻元素
2.对于邻域内的每对点,计算其三个角度特征参数值
3.将所有结果统计到一个输出直方图中
使用下列代码,从一个k-邻域计算单一的PFH描述子:
computePointPFHSignature (const pcl::PointCloud<PointInT> &cloud,
const pcl::PointCloud<PointNT> &normals,
const std::vector<int> &indices,
int nr_split,
Eigen::VectorXf&pfh_histogram);
此处,cloud变量是包含点的输入点云,normals变量是包含对应cloud的法线的输入点云,indices代表输入点云(点与法线对应)中查询点的k-近邻元素集,nr_split是所分区间的数目,用于每个特征区间的统计过程,pfh_histogram是浮点数向量来存储输出的合成直方图。
FPFH
已知点云P中有n个点,那么它的点特征直方图(PFH)的理论计算复杂度是,其中k是点云P中每个点p计算特征向量时考虑的邻域数量。对于实时应用或接近实时应用中,密集点云的点特征直方图(PFH)的计算,是一个主要的性能瓶颈。本小节讲述PFH计算方式的简化形式,我们称为快速点特征直方图FPFH(Fast Point Feature Histograms)(更多详情,请见[RusuDissertation]),FPFH把算法的计算复杂度降低到了,但是任然保留了PFH大部分的识别特性。
理论基础
为了简化直方图的特征计算,我们执行以下过程:
第一步,对于每一个查询点,计算这个点和它的邻域点之间的一个元组(参考上一节PFH的介绍),第一步结果我们称之为简化的点特征直方图SPFH(Simple Point Feature Histograms);
第二步,重新确定每个点的k邻域,使用邻近的SPFH值来计算的最终直方图(称为FPFH),如下所示:
上式中,权重 在一些给定的度量空间中,表示查询点 和其邻近点 之间的距离,因此可用来评定一对点( , ),但是如果需要的话,也可以把用 另一种度量来表示。如图1所示可以帮助理解这个权重方式的重要性,它表示的是以点为中心的k邻域影响范围。
图1 以点 为中心的k邻域影响范围图
因此,对于一个已知查询点 ,这个算法首先只利用 和它邻域点之间对应对(上图中以红色线来说明),来估计它的SPFH值,很明显这样比PFH的标准计算少了邻域点之间的互联。点云数据集中的所有点都要执行这一计算获取SPFH,接下来使用它的邻近点的SPFH值和点的SPFH值重新权重计算,从而得到点的最终FPFH值。FPFH计算添加的计算连接对,在上图中以黑色线表示。如上图所示,一些重要对点(与直接相连的点)被重复计数两次(图中以粗线来表示),而其他间接相连的用细黑线表示。
PFH和FPFH的区别
PFH和FPFH计算方式之间的主要区别总结如下:
1.FPFH没有对全互连 点的所有邻近点的计算参数进行统计,从图12-18中可以看到,因此可能漏掉了一些重要的点对,而这些漏掉的对点可能对捕获查询点周围的几何特征有贡献。
2.PFH特征模型是对查询点周围的一个精确的邻域半径内,而FPFH还包括半径r范围以外的额外点对(不过在2r内);
3.因为重新权重计算的方式,所以FPFH结合SPFH值,重新捕获邻近重要点对的几何信息;
4.由于大大地降低了FPFH的整体复杂性,因此FPFH有可能使用在实时应用中;
5.通过分解三元组,简化了合成的直方图。也就是简单生成d分离特征直方图,对每个特征维度来单独绘制,并把它们连接在一起(见下2图)。
图2 PFH与FPFH示意图
估计FPFH特征
快速点特征直方图FPFH在点云库中的实现可作为pcl_features库的一部分。默认的FPFH实现使用11个统计子区间(例如:四个特征值中的每个都将它的参数区间分割为11个),特征直方图被分别计算然后合并得出了浮点值的一个33元素的特征向量,这些保存在一个pcl::FPFHSignature33点类型中。以下代码段将对输入数据集中的所有点估计一组FPFH特征值。
#include
#include //fpfh特征估计类头文件声明
...//其他相关操作
pcl::PointCloud<pcl::PointXYZ>::Ptrcloud(newpcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::Normal>::Ptrnormals(newpcl::PointCloud<pcl::Normal>());
...//打开点云文件估计法线等
//创建FPFH估计对象fpfh,并把输入数据集cloud和法线normals传递给它。
pcl::FPFHEstimation<pcl::PointXYZ,pcl::Normal,pcl::FPFHSignature33>fpfh;
fpfh.setInputCloud(cloud);
fpfh.setInputNormals(normals);
//如果点云是类型为PointNormal,则执行fpfh.setInputNormals (cloud);
//创建一个空的kd树对象tree,并把它传递给FPFH估计对象。
//基于已知的输入数据集,建立kdtree
pcl::search::KdTree<PointXYZ>::Ptrtree(newpcl::search::KdTree<PointXYZ>);
fpfh.setSearchMethod(tree);
//输出数据集
pcl::PointCloud<pcl::FPFHSignature33>::Ptrfpfhs(newpcl::PointCloud<pcl::FPFHSignature33>());
//使用所有半径在5厘米范围内的邻元素
//注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
fpfh.setRadiusSearch(0.05);
//计算获取特征向量
fpfh.compute(*fpfhs);
//fpfhs->points.size ()应该和input cloud->points.size ()有相同的大小,即每个点有一个特征向量
FPFHEstimation类的实际计算内部只执行以下操作:
对点云P中的每个点p
第一步:
1.得到:math:`p`的邻域元素
2. 计算每一对:math:`p, p_k`的三个角度参数值(其中:math:`p_k`是:math:`p`的邻元素)
3.把所有结果统计输出到一个SPFH直方图
第二步:
1.得到:math:`p`的最近邻元素
2.使用:math:`p`的每一个SPFH和一个权重计算式,来计算最终:math:`p`的FPFH
利用OpenMP提高FPFH速度
对于计算速度要求苛刻的用户,PCL提供了一个FPFH估计的另一实现,它使用多核/多线程规范,利用OpenMP开发模式来提高计算速度。这个类的名称是pcl::FPFHEstimationOMP,并且它的应用程序接口(API)100%兼容单线程pcl::FPFHEstimation,这使它适合作为一个替换元件。在8核系统中,OpenMP的实现可以在6-8倍更快的计算时间内完全同样单核系统上的计算。
RIFT描述子
论文:A sparse texture representation using local affine regions
RIFT描述子,本质上来自于SIFT描述子,但是由于SIFT需要找到主方向,而RIFT考虑的是点云数据中三维区域,因此。作者改进为如下结构:
也就是说,点P出一定范围内,以点P为圆心,分割为等宽范围内的同心圆。然后计算每个环内的梯度方向直方图,为了保持旋转不变性,以每个采样点到圆心的距离和点梯度方向与圆心指向外部的梯度方向夹角作为统计特征图。作者使用了4个环和8个直方图方向,形成一个32维描述子。