您当前的位置: 首页 > 

暂无认证

  • 0浏览

    0关注

    100388博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

PCL 点云特征描述与提取

发布时间:2020-04-28 10:00:00 ,浏览量:0

点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

作者:开着拖拉机唱山歌

链接:https://zhuanlan.zhihu.com/p/103219268

本文转载自知乎,作者已授权,未经许可请勿二次转载。

1. PCL 点云特征描述与提取

2. 常见的特征描述算法

3. PCL 描述三维特征相关基础

4. PCL 法线估计实例 ------ 估计某一点的表面法线

5. PCL 法线估计实例 ------ 估计一个点云的表面法线

6. PCL 使用积分图像进行法线估计

7. 点特征直方图(PFH)描述子

8. 快速点特征直方图(FPFH)描述子

9. 估视点特征直方图(VFH)

10. 从一个深度图像提取NARF特征

PCL 点云特征描述与提取

3D点云特征描述与提取是点云信息处理中最基础也是最关键的一部分。点云的识别、分割、重采样、配准曲面重建等处理,大部分算法都依赖特征描述符提取的结果。从尺度上来分,一般分为局部特征的描述和全局特征的描述,例如局部的法线等几何形状特征的描述,全局的拓朴特征的描述,都属于3D点云特征描述与提取的范畴。

常用的特征描述算法有:

1. 法线和曲率计算 normal_3d_feature 、
    2. 特征值分析、
    3. PFH  点特征直方图描述子 (统计点法线角度差值row pitch yaw)   n*k^2、
    4. FPFH 快速点特征直方图描述子 FPFH是PFH的简化形式  n*k
    5. 3D Shape Context(3D形状内容描述子)
        pcl::ShapeContext3DEstimation< PointInT, PointNT, PointOutT >
        实现3D形状内容描述子算法
    6. 纹理特征, 2d-3d点对  特征描述子(orb可以)
    7. Spin Image
    8. VFH  视点特征直方图(Viewpoint Feature Histogram 视角方向与点法线方向夹角)
    9. NARF 关键点特征  pcl::NarfKeypoint narf特征 pcl::NarfDescriptor(深度图边缘)
    10. RoPs 特征(Rotational Projection Statistics)
    11. (GASD)全局一致的空间分布描述子特征 Globally Aligned Spatial Distribution (GASD) descriptors
    12. 旋转图像(spin iamge)
        旋转图像最早是由johnson提出的特征描述子,主要用于3D场景中的曲面匹配和模型识别。

由于各种不同需求需要进行对比以便能够区分曲面空间的分布情况,应用软件要求更好的特征度量方式,因此作为一个单一实体的三维点概念和笛卡尔坐标系被淘汰了,出现了一个新的概念取而代之:局部描述子(locl descriptor)。文献中对这一概念的描述有许多种不同的命名,如:形状描述子(shape descriptors)或几何特征(geometric features)。

PCL 描述三维特征相关基础

在原始表示形式下,点的定义是用笛卡尔坐标系坐标 x, y, z 相对于一个给定的原点来简单表示的三维映射系统的概念。假定坐标系的原点不随着时间而改变,这里有两个点p1和p2分别在时间t1和t2捕获,有着相同的坐标,对这两个点作比较其实是属于不适定问题(ill-posed problem),因为虽然相对于一些距离测度它们是相等的,但是它们取样于完全不同的表面,因此当把它们和临近的其他环境中点放在一起时,它们表达着完全不同的信息,这是因为在t1和t2之间局部环境有可能发生改变。一些获取设备也许能够提供取样点的额外数据,例如强度或表面反射率等,甚至颜色,然而那并不能完全解决问题。单从两个点之间来,对比仍然是不适定问题。由于各种不同需求需要进行对比以便能够区分曲面空间的分布情况,应用软件要求有更好的特征度量方式,因此作为一个单一实体的三维点概念和笛卡尔坐标系被淘汰了,出现了一个新的概念取而代之:局部描述子(local descriptor)。文献中对这一概念的描述有许多种不同的命名,如:形状描述子(shape descriptors)或几何特征(geometric features),文本中剩余部分都统称为点特征表示。通过包括周围的领域,特征描述子能够表征采样表面的几何性质,它有助于解决不适定的对比问题,理想情况下相同或相似表面上的点的特征值将非常相似(相对特定度量准则),而不同表面上的点的特征描述子将有明显差异。下面几个条件,通过能否获得相同的局部表面特征值,可以判定点特征表示方式的优劣:

平移选转不变性(刚体变换):即三维旋转和三维平移变化 不会影响特征向量F估计,即特征向量具有
抗密度干扰性(改变采样密度):原则上,一个局部表面小块的采样密度无论是大还是小,都应该有相同的特征向量值
对噪声具有稳定性(噪声):数据中有轻微噪声的情况下,点特征表示在它的特征向量中必须保持相同或者极其相似的值

通常,PCL中特征向量利用快速 kd-tree 查询 ,使用近似法来计算查询点的最近邻元素,通常有两种查询类型:K邻域查询,半径搜索两中方法

PCL 法线估计实例 ------ 估计某一点的表面法线

对应点云P中每一个点p得到p点最近邻元素,计算p点的表面的法线N,检查N的方向是否指向视点如果不是则翻转。一旦确定查询点的邻域后,查询点的邻域点可以用来估计一个局部特征描述子,它用查询点周围领域点描述采样面的几何特征,描述几何表面图形的一个重要属性。首先是推断它在坐标系中的方位,也就是估计他的法线(表面法线是表面的一个重要的属性)。

点云法线有什么用
点云渲染:法线信息可以用于光照渲染,有些地方也称着色(立体感)。
如下图所示,左边的点云没有法线信息,右边的点云有法线信息。
比如Phone光照模型里,
漫反射光照符合Lambert余弦定律:漫反射光强与N * L成正比,N为法线方向,L为点到光源的向量。
所以,在模型边缘处,N与L近似垂直,着色会比较暗。

点云的几何属性:法线可用于计算几何相关的信息,
广泛应用于点云注册(配准),点云重建,特征点检测等。
另外法线信息还可以用于区分薄板正反面。

前面说的是二维降到一维时的情况,假如我们有一堆散乱的三维点云,则可以这样计算法线:
1)对每一个点,取临近点,比如取最临近的50个点,当然会用到K-D树
2)对临近点做PCA降维,把它降到二维平面上,
可以想象得到这个平面一定是它的切平面(在切平面上才可以尽可能分散)
3)切平面的法线就是该点的法线了,而这样的法线有两个,
取哪个还需要考虑临近点的凸包方向

代码

#include //法线特征
--------------------------------------------------------------
// 创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);
// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  ne.setRadiusSearch (0.03);//半径内搜索临近点
  //ne.setKSearch(8);       //其二 指定临近点数量
  // 计算表面法线特征
  ne.compute (cloud_normals);

完整代码

#include 
#include 
#include   //法线估计类头文件
#include 
#include 
#include 

int main ()
{
//打开点云代码
pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
pcl::io::loadPCDFile ("table_scene_lms400.pcd", *cloud);
//创建法线估计估计向量
pcl::NormalEstimation ne;
ne.setInputCloud (cloud);
//创建一个空的KdTree对象,并把它传递给法线估计向量
//基于给出的输入数据集,KdTree将被建立
pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
ne.setSearchMethod (tree);
//存储输出数据
pcl::PointCloud::Ptr cloud_normals (new pcl::PointCloud);
//使用半径在查询点周围3厘米范围内的所有临近元素
ne.setRadiusSearch (0.03);
//计算特征值
ne.compute (*cloud_normals);
// cloud_normals->points.size ()应该与input cloud_downsampled->points.size ()有相同的尺寸
//可视化
pcl::visualization::PCLVisualizer viewer("PCL Viewer");
viewer.setBackgroundColor (0.0, 0.0, 0.0);
viewer.addPointCloudNormals(cloud, cloud_normals);
//视点默认坐标是(0,0,0)可使用 setViewPoint(float vpx,float vpy,float vpz)来更换
while (!viewer.wasStopped ())
{
     viewer.spinOnce ();
}

return 0;
}
PCL 法线估计实例 ------ 估计一个点云的表面法线

表面法线是几何体表面一个十分重要的属性,例如:在进行光照渲染时产生符合可视习惯的效果时需要表面法线的信息才能正常进行。对于一个已经已经知道的几何体表面,根据垂直于点表面的的矢量,得出表面某一点的法线方向比较容易,然而由于我们获取的点云的数据集在真实的物体的表面表现为一组点的样本,这样就会有两种方法解决:

1. 使用曲面重建技术,从获取的点云数据中得到采样点对应的曲面,然后从曲面模型中计算出表面法线

2. 直接从点云数据中近似推断表面法线

在确定表面一点法线的问题近似于估计表面的一个相切面法线的问题,因此转换过来就是求一个最小二乘法平面拟合的问题

  • PCL 使用积分图像进行法线估计。

注意此方法只适用于有序点云

  • 为什么使用积分图像?

在PCL估计点云的表面法向量一文中,可以看到,要估计无序点云的法线,需要先确定待估计点的最近邻集合,然后再用PCA拟合切平面,从而获得表面法线。

而要确定待估计点的最近邻集合,不管是用k近邻还是半径约束的方法,都将是计算量巨大的。如果输入的点云是有序点云,利用这种有序性可以明显加快表面法线估计的速度,积分图像正是为了利用点云有序性提高法线估计的速度而引入的。

  • 什么是积分图像?

在二维图像中,将每一个像素点都用原图像中该像素点与原点构成的矩形中所有像素之和代替,那么生成的新的图像就是积分图像。

设原图像为I(x,y),积分图像为SAT(x,y),则:

#include 
#include 
#include 
#include 

int main()
{
    //打开点云
    pcl::PointCloud::Ptr cloud(new pcl::PointCloud);
    pcl::io::loadPCDFile("table.pcd", *cloud);
    //创建法线估计向量
    pcl::PointCloud::Ptr normals(new pcl::PointCloud);
    pcl::IntegralImageNormalEstimation ne;
    /****************************************************************************************
    三种法线估计方法
    COVARIANCE_MATRIX 模式从具体某个点的局部邻域的协方差矩阵创建9个积分,来计算这个点的法线
    AVERAGE_3D_GRADIENT   模式创建6个积分图来计算水平方向和垂直方向的平滑后的三维梯度并使用两个梯度间的向量
						   积计算法线
    AVERAGE_DEPTH——CHANGE  模式只创建了一个单一的积分图,从而平局深度变化计算法线
    ********************************************************************************************/
    ne.setNormalEstimationMethod(ne.AVERAGE_3D_GRADIENT);  //设置法线估计的方式AVERAGE_3D_GRADIENT
    ne.setMaxDepthChangeFactor(0.02f);   //设置深度变化系数
    ne.setNormalSmoothingSize(10.0f);   //设置法线优化时考虑的邻域的大小
    ne.setInputCloud(cloud);               //输入的点云
    ne.compute(*normals);                    //计算法线
    //可视化
    pcl::visualization::PCLVisualizer viewer("PCL Viewer");   //视口的名称
    viewer.setBackgroundColor(0.0, 0.0, 0.5);    //背景颜色的设置
    viewer.addPointCloudNormals(cloud, normals);  //将法线加入到点云中
    while (!viewer.wasStopped())
	{
		viewer.spinOnce();
	}
    return 0;
}
点特征直方图(PFH)描述子

正如点特征表示法所示,表面法线和曲率估计是某个点周围的几何特征基本表示法。虽然计算非常快速容易,但是无法获得太多信息,因为它们只使用很少的几个参数值来近似表示一个点的k邻域的几何特征。然而大部分场景中包含许多特征点,这些特征点有相同的或者非常相近的特征值,因此采用点特征表示法,其直接结果就减少了全局的特征信息。那么三维特征描述子中一位成员:点特征直方图(Point Feature Histograms),我们简称为PFH,从PCL实现的角度讨论其实施细节。PFH特征不仅与坐标轴三维数据有关,同时还与表面法线有关。

PFH计算方式通过参数化查询点与邻域点之间的空间差异,并形成一个多维直方图对点的k邻域几何属性进行描述。直方图所在的高维超空间为特征表示提供了一个可度量的信息空间,对点云对应曲面的6维姿态来说它具有不变性,并且在不同的采样密度或邻域的噪音等级下具有鲁棒性。点特征直方图(PFH)表示法是基于点与其k邻域之间的关系以及它们的估计法线,简言之,它考虑估计法线方向之间所有的相互作用,试图捕获最好的样本表面变化情况,以描述样本的几何特征。因此,合成特征超空间取决于每个点的表面法线估计的质量。

blog.csdn.net/zfjBIT/ar参考

点特征直方图(PFH)在PCL中的实现是pcl_features模块的一部分。默认PFH的实现使用5个区间分类(例如:四个特征值中的每个都使用5个区间来统计)

计算法线---计算临近点对角度差值-----直方图--
点特征直方图(PFH)描述子
点特征直方图(Point Feature Histograms)
正如点特征表示法所示,表面法线和曲率估计是某个点周围的几何特征基本表示法。
虽然计算非常快速容易,但是无法获得太多信息,因为它们只使用很少的
几个参数值来近似表示一个点的k邻域的几何特征。然而大部分场景中包含许多特征点,
这些特征点有相同的或者非常相近的特征值,因此采用点特征表示法,
其直接结果就减少了全局的特征信息。

代码

#include 
#include //法线特征
---------------------------------------------------
// =====计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);

// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
  // 计算表面法线特征
  ne.compute (cloud_normals);

//=======创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它=================
  pcl::PFHEstimation pfh;// phf特征估计其器
  pfh.setInputCloud (cloud_ptr);
  pfh.setInputNormals (cloud_normals_ptr);
 //如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
 //创建一个空的kd树表示法,并把它传递给PFH估计对象。
 //基于已给的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
  //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  pfh.setSearchMethod (tree2);//设置近邻搜索算法
  //输出数据集
  pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
 //使用半径在5厘米范围内的所有邻元素。
  //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  pfh.setRadiusSearch (0.05);
  //计算pfh特征值
  pfh.compute (*pfh_fe_ptr);

完整代码

/*
计算法线---计算临近点对角度差值-----直方图--
点特征直方图(PFH)描述子
点特征直方图(Point Feature Histograms)
正如点特征表示法所示,表面法线和曲率估计是某个点周围的几何特征基本表示法。
虽然计算非常快速容易,但是无法获得太多信息,因为它们只使用很少的
几个参数值来近似表示一个点的k邻域的几何特征。然而大部分场景中包含许多特征点,
这些特征点有相同的或者非常相近的特征值,因此采用点特征表示法,
其直接结果就减少了全局的特征信息。
http://www.pclcn.org/study/shownews.php?lang=cn&id=101
通过参数化查询点与邻域点之间的空间差异,并形成一个多维直方图对点的k邻域几何属性进行描述。
直方图所在的高维超空间为特征表示提供了一个可度量的信息空间,
对点云对应曲面的6维姿态来说它具有不变性,
并且在不同的采样密度或邻域的噪音等级下具有鲁棒性。
是基于点与其k邻域之间的关系以及它们的估计法线,
简言之,它考虑估计法线方向之间所有的相互作用,
试图捕获最好的样本表面变化情况,以描述样本的几何特征。
Pq 用红色标注并放在圆球的中间位置,半径为r,
(Pq)的所有k邻元素(即与点Pq的距离小于半径r的所有点)
全部互相连接在一个网络中。最终的PFH描述子通过计
算邻域内所有两点之间关系而得到的直方图,
因此存在一个O(nk^2) 的计算复杂性。
每一点对,原有12个参数,6个坐标值,6个坐标姿态(基于法线)
PHF计算没一点对的 相对坐标角度差值三个值和 坐标点之间的欧氏距离 d
从12个参数减少到4个参数
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);
为查询点创建最终的PFH表示,所有的四元组将会以某种统计的方式放进直方图中,
这个过程首先把每个特征值范围划分为b个子区间,并统计落在每个子区间的点数目,
因为四分之三的特征在上述中为法线之间的角度计量,
在三角化圆上可以将它们的参数值非常容易地归一到相同的区间内。
默认PFH的实现使用5个区间分类(例如:四个特征值中的每个都使用5个区间来统计),
其中不包括距离(在上文中已经解释过了——但是如果有需要的话,
也可以通过用户调用computePairFeatures方法来获得距离值),
这样就组成了一个125浮点数元素的特征向量(35),
其保存在一个pcl::PFHSignature125的点类型中。
*/

#include 
#include 
#include //点云文件pcd 读写
#include //法线特征

#include  //直方图的可视化
#include // 直方图的可视化 方法2

//using namespace std;
using std::cout;
using std::endl;
int main(int argc, char** argv)
{

  pcl::PointCloud::Ptr cloud_ptr (new pcl::PointCloud);
//======【1】 读取点云文件 填充点云对象======
  pcl::PCDReader reader;
  reader.read( "../../Filtering/table_scene_lms400.pcd", *cloud_ptr);
  if(cloud_ptr==NULL) { cout << "pcd file read err" << endl; return -1;}
  cout << "PointCLoud size() " << cloud_ptr->width * cloud_ptr->height
       << " data points ( " << pcl::getFieldsList (*cloud_ptr) << "." << endl;

// =====【2】计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);
/*
 法线估计类NormalEstimation的实际计算调用程序内部执行以下操作:
对点云P中的每个点p
  1.得到p点的最近邻元素
  2.计算p点的表面法线n
  3.检查n的方向是否一致指向视点,如果不是则翻转
 在PCL内估计一点集对应的协方差矩阵,可以使用以下函数调用实现:
//定义每个表面小块的3x3协方差矩阵的存储对象
Eigen::Matrix3fcovariance_matrix;
//定义一个表面小块的质心坐标16-字节对齐存储对象
Eigen::Vector4fxyz_centroid;
//估计质心坐标
compute3DCentroid(cloud,xyz_centroid);
//计算3x3协方差矩阵
computeCovarianceMatrix(cloud,xyz_centroid,covariance_matrix);
*/
// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
  // 计算表面法线特征
  ne.compute (cloud_normals);


//=======【3】创建PFH估计对象pfh,并将输入点云数据集cloud和法线normals传递给它=================
  pcl::PFHEstimation pfh;// phf特征估计其器
  pfh.setInputCloud (cloud_ptr);
  pfh.setInputNormals (cloud_normals_ptr);
 //如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
 //创建一个空的kd树表示法,并把它传递给PFH估计对象。
 //基于已给的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
  //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  pfh.setSearchMethod (tree2);//设置近邻搜索算法
  //输出数据集
  pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
 //使用半径在5厘米范围内的所有邻元素。
  //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  pfh.setRadiusSearch (0.05);
  //计算pfh特征值
  pfh.compute (*pfh_fe_ptr);


  cout << "phf feature size : " << pfh_fe_ptr->points.size() << endl;
  // 应该与input cloud->points.size ()有相同的大小,即每个点都有一个pfh特征向量


  // ========直方图可视化=============================
  pcl::visualization::PCLHistogramVisualizer view;//直方图可视化
  view.setBackgroundColor(255,0,0);//背景红色
  view.addFeatureHistogram(*pfh_fe_ptr,"pfh",100);
  view.spinOnce();  //循环的次数
  //view.spin();  //无限循环
  // pcl::visualization::PCLPlotter plotter;
   //plotter.addFeatureHistogram(*pfh_fe_ptr, 300); //设置的很坐标长度,该值越大,则显示的越细致
   //plotter.plot();

  return 0;
}
快速点特征直方图(FPFH)描述子 pcl::FPFHEstimation
phf点特征直方图 计算复杂度还是太高
计算法线---计算临近点对角度差值-----直方图--
因此存在一个O(nk^2) 的计算复杂性。
k个点之间相互的点对 k×k条连接线

每一点对,原有12个参数,6个坐标值,6个坐标姿态(基于法线)
PHF计算没一点对的 相对坐标角度差值三个值和 坐标点之间的欧氏距离 d
从12个参数减少到4个参数

快速点特征直方图FPFH(Fast Point Feature Histograms)把算法的计算复杂度降低到了O(nk) ,
但是任然保留了PFH大部分的识别特性。
查询点和周围k个点的连线 的4参数特征
也就是1×k=k个线

默认的FPFH实现使用11个统计子区间(例如:四个特征值中的每个都将它的参数区间分割为11个),
特征直方图被分别计算然后合并得出了浮点值的一个33元素的特征向量,
这些保存在一个pcl::FPFHSignature33点类型中。
PFH和FPFH计算方式之间的主要区别总结如下:
1.FPFH没有对全互连 点的所有邻近点的计算参数进行统计,从图12-18中可以看到,
  因此可能漏掉了一些重要的点对,而这些漏掉的对点可能对捕获查询点周围的几何特征有贡献。
  
2.PFH特征模型是对查询点周围的一个精确的邻域半径内,而FPFH还包括半径r范围以外的额外点对(不过在2r内);

3.因为重新权重计算的方式,所以FPFH结合SPFH值,重新捕获邻近重要点对的几何信息;

4.由于大大地降低了PFH的整体复杂性,因此FPFH有可能使用在实时应用中;

5.通过分解三元组,简化了合成的直方图。
  也就是简单生成d分离特征直方图,对每个特征维度来单独绘制,并把它们连接在一起(见下2图)。
FPFHEstimation类的实际计算内部只执行以下操作,对点云P中的每个点p:
1. 得到 p 的邻居k个点 p_k(0~k-1)
2. 计算每一对 p 、p_k(i) 法线的三个角度差值(row pitch yaw) + 距离
3. 把所有结果统计输出到一个SPFH直方图
4. 得到 p 的邻域元素
5. 使用 p 的每一个SPFH和一个权重计算式,来计算最终 p 的FPFH

头文件
#include 
#include //法线特征
----------------------------------------------------
// =====计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);
/*
 法线估计类NormalEstimation的实际计算调用程序内部执行以下操作:
对点云P中的每个点p
  1.得到p点的最近邻元素
  2.计算p点的表面法线n
  3.检查n的方向是否一致指向视点,如果不是则翻转

 在PCL内估计一点集对应的协方差矩阵,可以使用以下函数调用实现:
//定义每个表面小块的3x3协方差矩阵的存储对象
Eigen::Matrix3fcovariance_matrix;
//定义一个表面小块的质心坐标16-字节对齐存储对象
Eigen::Vector4fxyz_centroid;
//估计质心坐标
compute3DCentroid(cloud,xyz_centroid);
//计算3x3协方差矩阵
computeCovarianceMatrix(cloud,xyz_centroid,covariance_matrix);
*/
// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
    // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
    // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
    // 计算表面法线特征
  ne.compute (cloud_normals);

//=======创建FPFH估计对象fpfh, 并将输入点云数据集cloud和法线normals传递给它=================
    //pcl::PFHEstimation pfh;// phf特征估计其器
  pcl::FPFHEstimation fpfh;
    // pcl::FPFHEstimationOMP fpfh;//多核加速
  fpfh.setInputCloud (cloud_ptr);
  fpfh.setInputNormals (cloud_normals_ptr);
   //如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
   //创建一个空的kd树表示法,并把它传递给PFH估计对象。
   //基于已给的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
    //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  fpfh.setSearchMethod (tree2);//设置近邻搜索算法
    //输出数据集
    //pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
  pcl::PointCloud::Ptr fpfh_fe_ptr (new pcl::PointCloud());//fphf特征
    //使用半径在5厘米范围内的所有邻元素。
    //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  fpfh.setRadiusSearch (0.05);
    //计算pfh特征值
  fpfh.compute (*fpfh_fe_ptr);

完整代码

/*
phf点特征直方图 计算复杂度还是太高
计算法线---计算临近点对角度差值-----直方图--
因此存在一个O(nk^2) 的计算复杂性。
k个点之间相互的点对 k×k条连接线
每一点对,原有12个参数,6个坐标值,6个坐标姿态(基于法线)
PHF计算没一点对的 相对坐标角度差值三个值和 坐标点之间的欧氏距离 d
从12个参数减少到4个参数
快速点特征直方图FPFH(Fast Point Feature Histograms)把算法的计算复杂度降低到了O(nk) ,
但是任然保留了PFH大部分的识别特性。
查询点和周围k个点的连线 的4参数特征
也就是1×k=k个线
默认的FPFH实现使用11个统计子区间(例如:四个特征值中的每个都将它的参数区间分割为11个),
特征直方图被分别计算然后合并得出了浮点值的一个33元素的特征向量,
这些保存在一个pcl::FPFHSignature33点类型中。
*/

#include 
//#include 
#include 
#include //点云文件pcd 读写
#include //法线特征
#include  //直方图的可视化
#include // 直方图的可视化 方法2


//using namespace std;
using std::cout;
using std::endl;
int main(int argc, char** argv)
{

  pcl::PointCloud::Ptr cloud_ptr (new pcl::PointCloud);
//======【1】 读取点云文件 填充点云对象======
  pcl::PCDReader reader;
  reader.read( "../../Filtering/table_scene_lms400.pcd", *cloud_ptr);
  if(cloud_ptr==NULL) { cout << "pcd file read err" << endl; return -1;}
  cout << "PointCLoud size() " << cloud_ptr->width * cloud_ptr->height
       << " data points ( " << pcl::getFieldsList (*cloud_ptr) << "." << endl;

// =====【2】计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);
/*
 法线估计类NormalEstimation的实际计算调用程序内部执行以下操作:
对点云P中的每个点p
  1.得到p点的最近邻元素
  2.计算p点的表面法线n
  3.检查n的方向是否一致指向视点,如果不是则翻转
 在PCL内估计一点集对应的协方差矩阵,可以使用以下函数调用实现:
//定义每个表面小块的3x3协方差矩阵的存储对象
Eigen::Matrix3fcovariance_matrix;
//定义一个表面小块的质心坐标16-字节对齐存储对象
Eigen::Vector4fxyz_centroid;
//估计质心坐标
compute3DCentroid(cloud,xyz_centroid);
//计算3x3协方差矩阵
computeCovarianceMatrix(cloud,xyz_centroid,covariance_matrix);
*/
// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
  // 计算表面法线特征
  ne.compute (cloud_normals);


//=======【3】创建FPFH估计对象fpfh, 并将输入点云数据集cloud和法线normals传递给它=================
  //pcl::PFHEstimation pfh;// phf特征估计其器
  pcl::FPFHEstimation fpfh;
// pcl::FPFHEstimationOMP fpfh;//多核加速
  fpfh.setInputCloud (cloud_ptr);
  fpfh.setInputNormals (cloud_normals_ptr);
 //如果点云是类型为PointNormal,则执行pfh.setInputNormals (cloud);
 //创建一个空的kd树表示法,并把它传递给PFH估计对象。
 //基于已给的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
  //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  fpfh.setSearchMethod (tree2);//设置近邻搜索算法
  //输出数据集
  //pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
  pcl::PointCloud::Ptr fpfh_fe_ptr (new pcl::PointCloud());//fphf特征
 //使用半径在5厘米范围内的所有邻元素。
  //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  fpfh.setRadiusSearch (0.05);
  //计算pfh特征值
  fpfh.compute (*fpfh_fe_ptr);


  cout << "phf feature size : " << fpfh_fe_ptr->points.size() << endl;
  // 应该与input cloud->points.size ()有相同的大小,即每个点都有一个pfh特征向量


// ========直方图可视化=============================
  pcl::visualization::PCLHistogramVisualizer view;//直方图可视化
  view.setBackgroundColor(255,0,0);//背景红色
  view.addFeatureHistogram(*fpfh_fe_ptr,"fpfh",100); //对下标为100的点的直方图特征可视化
  view.spinOnce();  //循环的次数
  //view.spin();  //无限循环
  // pcl::visualization::PCLPlotter plotter;
   //plotter.addFeatureHistogram(*fpfh_fe_ptr, 300); //设置的很坐标长度,该值越大,则显示的越细致
   //plotter.plot();


  return 0;
}
视点特征直方图 pcl::VFHEstimation VFH(Viewpoint Feature Histogram)描述子 视角向量和点法线夹角
它是一种新的特征表示形式,应用在点云聚类识别和六自由度位姿估计问题。

视点特征直方图(或VFH)是源于FPFH描述子.
由于它的获取速度和识别力,我们决定利用FPFH强大的识别力,
但是为了使构造的特征保持缩放不变性的性质同时,
还要区分不同的位姿,计算时需要考虑加入视点变量。
我们做了以下两种计算来构造特征,以应用于目标识别问题和位姿估计:
1.扩展FPFH,使其利用整个点云对象来进行计算估计(如2图所示),
        在计算FPFH时以物体中心点与物体表面其他所有点之间的点对作为计算单元。

    2.添加视点方向与每个点估计法线之间额外的统计信息,为了达到这个目的,
        我们的关键想法是在FPFH计算中将视点方向变量直接融入到相对法线角计算当中。

因此新组合的特征被称为视点特征直方图(VFH)。
下图表体现的就是新特征的想法,包含了以下两部分:
1.一个视点方向相关的分量
    2.一个包含扩展FPFH的描述表面形状的分量

对扩展的FPFH分量来说,默认的VFH的实现使用45个子区间进行统计,
而对于视点分量要使用128个子区间进行统计,这样VFH就由一共308个浮点数组成阵列。
在PCL中利用pcl::VFHSignature308的点类型来存储表示。P
FH/FPFH描述子和VFH之间的主要区别是:

对于一个已知的点云数据集,只一个单一的VFH描述子,
而合成的PFH/FPFH特征的数目和点云中的点数目相同。
头文件
#include 
#include //法线特征

------------------------------------------------------
// =====计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);

// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
  // 计算表面法线特征
  ne.compute (cloud_normals);


//=======创建VFH估计对象vfh,并把输入数据集cloud和法线normal传递给它================
  pcl::VFHEstimation vfh;
  //pcl::PFHEstimation pfh;// phf特征估计其器
  //pcl::FPFHEstimation fpfh;// fphf特征估计其器
  // pcl::FPFHEstimationOMP fpfh;//多核加速
  vfh.setInputCloud (cloud_ptr);
  vfh.setInputNormals (cloud_normals_ptr);
  //如果点云是PointNormal类型,则执行vfh.setInputNormals (cloud);
  //创建一个空的kd树对象,并把它传递给FPFH估计对象。
  //基于已知的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
  //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  vfh.setSearchMethod (tree2);//设置近邻搜索算法
  //输出数据集
  //pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
  //pcl::PointCloud::Ptr fpfh_fe_ptr (new pcl::PointCloud());//fphf特征
  pcl::PointCloud::Ptr vfh_fe_ptr (new pcl::PointCloud());//vhf特征
  //使用半径在5厘米范围内的所有邻元素。
  //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  //fpfh.setRadiusSearch (0.05);
  //计算pfh特征值
  vfh.compute (*vfh_fe_ptr);

完整代码

/*
视点特征直方图VFH(Viewpoint Feature Histogram)描述子,
它是一种新的特征表示形式,应用在点云聚类识别和六自由度位姿估计问题。
视点特征直方图(或VFH)是源于FPFH描述子.
由于它的获取速度和识别力,我们决定利用FPFH强大的识别力,
但是为了使构造的特征保持缩放不变性的性质同时,
还要区分不同的位姿,计算时需要考虑加入视点变量。
我们做了以下两种计算来构造特征,以应用于目标识别问题和位姿估计:
1.扩展FPFH,使其利用整个点云对象来进行计算估计(如2图所示),
在计算FPFH时以物体中心点与物体表面其他所有点之间的点对作为计算单元。
2.添加视点方向与每个点估计法线之间额外的统计信息,为了达到这个目的,
我们的关键想法是在FPFH计算中将视点方向变量直接融入到相对法线角计算当中。
通过统计视点方向与每个法线之间角度的直方图来计算视点相关的特征分量。
注意:并不是每条法线的视角,因为法线的视角在尺度变换下具有可变性,
我们指的是平移视点到查询点后的视点方向和每条法线间的角度。
第二组特征分量就是前面PFH中讲述的三个角度,如PFH小节所述,
只是现在测量的是在中心点的视点方向和每条表面法线之间的角度。
因此新组合的特征被称为视点特征直方图(VFH)。
下图表体现的就是新特征的想法,包含了以下两部分:
1.一个视点方向相关的分量
2.一个包含扩展FPFH的描述表面形状的分量
对扩展的FPFH分量来说,默认的VFH的实现使用45个子区间进行统计,
而对于视点分量要使用128个子区间进行统计,这样VFH就由一共308个浮点数组成阵列。
在PCL中利用pcl::VFHSignature308的点类型来存储表示。P
FH/FPFH描述子和VFH之间的主要区别是:
对于一个已知的点云数据集,只一个单一的VFH描述子,
而合成的PFH/FPFH特征的数目和点云中的点数目相同。
*/

#include 
//#include 
//#include 
#include 
#include //点云文件pcd 读写
#include //法线特征

#include  //直方图的可视化
#include // 直方图的可视化 方法2
// 可视化 https://segmentfault.com/a/1190000006685118

//using namespace std;
using std::cout;
using std::endl;
int main(int argc, char** argv)
{

  pcl::PointCloud::Ptr cloud_ptr (new pcl::PointCloud);
//======【1】 读取点云文件 填充点云对象======
  pcl::PCDReader reader;
  reader.read( "../../Filtering/table_scene_lms400.pcd", *cloud_ptr);
  if(cloud_ptr==NULL) { cout << "pcd file read err" << endl; return -1;}
  cout << "PointCLoud size() " << cloud_ptr->width * cloud_ptr->height
       << " data points ( " << pcl::getFieldsList (*cloud_ptr) << "." << endl;

// =====【2】计算法线========创建法线估计类====================================
  pcl::NormalEstimation ne;
  ne.setInputCloud (cloud_ptr);
/*
 法线估计类NormalEstimation的实际计算调用程序内部执行以下操作:
对点云P中的每个点p
  1.得到p点的最近邻元素
  2.计算p点的表面法线n
  3.检查n的方向是否一致指向视点,如果不是则翻转
 在PCL内估计一点集对应的协方差矩阵,可以使用以下函数调用实现:
//定义每个表面小块的3x3协方差矩阵的存储对象
Eigen::Matrix3fcovariance_matrix;
//定义一个表面小块的质心坐标16-字节对齐存储对象
Eigen::Vector4fxyz_centroid;
//估计质心坐标
compute3DCentroid(cloud,xyz_centroid);
//计算3x3协方差矩阵
computeCovarianceMatrix(cloud,xyz_centroid,covariance_matrix);
*/
// 添加搜索算法 kdtree search  最近的几个点 估计平面 协方差矩阵PCA分解 求解法线
  pcl::search::KdTree::Ptr tree (new pcl::search::KdTree());
  ne.setSearchMethod (tree);//设置近邻搜索算法
  // 输出点云 带有法线描述
  pcl::PointCloud::Ptr cloud_normals_ptr (new pcl::PointCloud);
  pcl::PointCloud& cloud_normals = *cloud_normals_ptr;
  // Use all neighbors in a sphere of radius 3cm
  ne.setRadiusSearch (0.03);//半价内搜索临近点 3cm
  // 计算表面法线特征
  ne.compute (cloud_normals);


//=======【3】创建VFH估计对象vfh,并把输入数据集cloud和法线normal传递给它================
  pcl::VFHEstimation vfh;
  //pcl::PFHEstimation pfh;// phf特征估计其器
  //pcl::FPFHEstimation fpfh;// fphf特征估计其器
  // pcl::FPFHEstimationOMP fpfh;//多核加速
  vfh.setInputCloud (cloud_ptr);
  vfh.setInputNormals (cloud_normals_ptr);
  //如果点云是PointNormal类型,则执行vfh.setInputNormals (cloud);
  //创建一个空的kd树对象,并把它传递给FPFH估计对象。
  //基于已知的输入数据集,建立kdtree
  pcl::search::KdTree::Ptr tree2 (new pcl::search::KdTree());
  //pcl::KdTreeFLANN::Ptr tree2 (new pcl::KdTreeFLANN()); //-- older call for PCL 1.5-
  vfh.setSearchMethod (tree2);//设置近邻搜索算法
  //输出数据集
  //pcl::PointCloud::Ptr pfh_fe_ptr (new pcl::PointCloud());//phf特征
  //pcl::PointCloud::Ptr fpfh_fe_ptr (new pcl::PointCloud());//fphf特征
  pcl::PointCloud::Ptr vfh_fe_ptr (new pcl::PointCloud());//vhf特征
  //使用半径在5厘米范围内的所有邻元素。
  //注意:此处使用的半径必须要大于估计表面法线时使用的半径!!!
  //fpfh.setRadiusSearch (0.05);
  //计算pfh特征值
  vfh.compute (*vfh_fe_ptr);


  cout << "phf feature size : " << vfh_fe_ptr->points.size() << endl;
  // 应该 等于 1
// ========直方图可视化=============================
  //pcl::visualization::PCLHistogramVisualizer view;//直方图可视化
  //view.setBackgroundColor(255,0,0);//背景红色
  //view.addFeatureHistogram(*vfh_fe_ptr,"vfh",0);
  //view.spinOnce();  //循环的次数
  //view.spin();  //无限循环
  pcl::visualization::PCLPlotter plotter;
  plotter.addFeatureHistogram(*vfh_fe_ptr, 300); //设置的很坐标长度,该值越大,则显示的越细致
  plotter.plot();


  return 0;
}
NARF 深度图像 边缘特征 pcl::NarfKeypoint pcl::NarfDescriptor
从深度图像(RangeImage)中提取NARF关键点  pcl::NarfKeypoint
 然后计算narf特征 pcl::NarfDescriptor
边缘提取
直接把三维的点云投射成 二维的图像不就好了。
这种投射方法叫做range_image.

#include // RangeImage 深度图像
#include // narf关键点检测
#include // narf特征

------------------------------------------------------

// ======从点云数据,创建深度图像=====================
  // 直接把三维的点云投射成二维的图像
  float noise_level = 0.0;
//noise level表示的是容差率,因为1°X1°的空间内很可能不止一个点,
//noise level = 0则表示去最近点的距离作为像素值,如果=0.05则表示在最近点及其后5cm范围内求个平均距离
//minRange表示深度最小值,如果=0则表示取1°X1°的空间内最远点,近的都忽略
  float min_range = 0.0f;
//bordersieze表示图像周边点
  int border_size = 1;
  boost::shared_ptrrange_image_ptr (new pcl::RangeImage);//创建RangeImage对象(智能指针)
  pcl::RangeImage& range_image = *range_image_ptr; //RangeImage的引用
//半圆扫一圈就是整个图像了
 range_image.createFromPointCloud (point_cloud, angular_resolution, pcl::deg2rad (360.0f), pcl::deg2rad (180.0f),
                                   scene_sensor_pose, coordinate_frame, noise_level, min_range, border_size);
  range_image.integrateFarRanges (far_ranges);//整合远距离点云
  if (setUnseenToMaxRange)
    range_image.setUnseenToMaxRange ();

 // ====================提取NARF关键点=======================
  pcl::RangeImageBorderExtractor range_image_border_extractor;//创建深度图像的边界提取器,用于提取NARF关键点
  pcl::NarfKeypoint narf_keypoint_detector (&range_image_border_extractor);//创建NARF对象
  narf_keypoint_detector.setRangeImage (&range_image);//设置点云对应的深度图
  narf_keypoint_detector.getParameters ().support_size = support_size;// 感兴趣点的尺寸(球面的直径)
  //narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;
  //narf_keypoint_detector.getParameters ().distance_for_additional_points = 0.5;

  pcl::PointCloudkeypoint_indices;//用于存储关键点的索引 PointCloudnarf_keypoint_detector.compute (keypoint_indices);//计算NARF关键

//========================提取 NARF 特征 ====================
  // ------------------------------------------------------
  std::vectorkeypoint_indices2;//用于存储关键点的索引 vectorkeypoint_indices2.resize (keypoint_indices.points.size ());
  for (unsigned int i=0; i            
关注
打赏
1655516835
查看更多评论
立即登录/注册

微信扫码登录

0.0507s