PCL学习七:Features-特征

参考引用

PCL点云库学习笔记(文章链接汇总)

1. 特征描述与提取

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

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

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

    • 刚性变换(rigid transformations):即数据中的 3D 旋转和 3D 平移不应影响结果特征向量 F 的估计
    • 多种采样密度(varying sampling density):原则上,在一个局部表面或多或少采样密度的应具有相同的特征向量
    • 噪声(noise):在数据中存在轻微噪声的情况下,由特征点描述的特征向量必须相同或非常相近
  • 通常,PCL 功能使用近似的方法通过快速的 kd-tree 查询来计算查询点的最近邻居,查询方法主要有两种

    • k 搜索:确定查询点的 k 个(用户给定参数)邻居
    • 半径搜索:确定半径为 r 的球面内查询点的所有邻居
  • 3D 形状内容描述子(3D shape contexts)
    利用描述子建立曲面间的对应点在 3D 物体识别领域有广发的应用,采用一个向量描述曲面上指定点及邻域的形状特征,通过匹配向量的值来建立不同曲面点的对应关系,此相邻则则称为指定点的俄描述子,经典描述子的 3D 形状内容描述子结构简单,辨别力强且对噪声不敏感
  • 旋转图像(spin iamge)
    旋转图像最早是由 johnson 提出的特征描述子,主要用于 3D 场景中的曲面匹配和模型识别

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 ...
        
        // 创建法向量估算类,传递输入数据集
        pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        ne.setInputCloud (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 ()
    }
    
    // 从一个点云的子集中进行表面法线估算
    #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 ()
    }
    
    // 为输入数据集中的所有点估算一组表面法线,但将使用另一个数据集(原始点云)估计其最近的邻居
    // 如前所述,一个很好的方式是降采样版本作为输入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()
    }
    

3. 点云表面法线估算

3.1 引言

  • 表面法线是几何表面的重要属性,在许多领域(例如计算机图形应用程序)中大量使用,以应用正确的光源以产生阴影和其他视觉效果

  • 给定一个几何表面,通常很难将表面某个点的法线方向推断为垂直于该点表面的向量。但是,由于获取的点云数据集是真实表面上的一组点样本,因此有两种可能性

    • 使用表面网格化技术从获取的点云数据集中获取基础表面,然后从网格中计算表面法线
    • 使用近似值直接从点云数据集中推断表面法线(下文将使用这个方法)

3.2 理论基础

尽管有许多不同的法线估计方法,先了解其中最简单也是最常见的一个:确定表面一点法线近似于估计表面的一个相切面法线的问题,因此转换过来以后就变成一个最小二乘法平面拟合估计问题

  • 法线计算

    • 将平面表示为一个点 c c c 和一个法向量 n ⃗ \vec{n} n ,则从一个点 p i ∈ P k p_{i}\in P^{k} piPk 到平面的距离可以定义为: x x x n n n 的值是通过最小二乘法以使 d i = 0 d_i=0 di=0 得到的,则中心点
      c = p ˉ = 1 k ⋅ ∑ i = 1 k p i c=\bar{p}=\frac{1}{k}\cdot\sum_{i=1}^{k}p_i c=pˉ=k1i=1kpi

    • 假如有一堆点 p i p_i pi ,查找一个超平面,使通过点 c c c 的一个法向量 n ⃗ \vec{n} n 满足
      min ⁡ c , n ⃗ , ∥ n ⃗ ∥ = 1 ∑ i = 1 k ( ( p i − c ) T n ⃗ ) 2 \min\limits_{\mathbf{c},\vec{\mathbf{n}},\|\vec{\mathbf{n}}\|=1}\sum\limits_{i=1}^k\left(\left(\mathbf{p}_i-\mathbf{c}\right)^T\vec{\mathbf{n}}\right)^2 c,n ,n =1mini=1k((pic)Tn )2

    • 周围点的向量 ( p i − c ) T (p_i-c)^T (pic)T 在法向量 n ⃗ \vec{n} n 上的投影之和最小(两个向量的乘积即是一个向量在另一个向量上的投影)
      在这里插入图片描述

    • c c c 作为 P k P^k Pk 的中心,法向 n ⃗ \vec{n} n 的值是通过分析点集合 P k P^k Pk 协方差矩阵 C ∈ R 3 × 3 \mathcal{C}\in R^{3\times3} CR3×3 的特征值和特征向量得到的(PCA—主成分分析),对于指定区域的点集合 P k P^k Pk,其协方差矩阵定义如下
      C = 1 k ∑ i = 1 k ( p i − p ˉ ) ⋅ ( p i − p ˉ ) T , C ⋅ v ⃗ j = λ j ⋅ v ⃗ j , j ∈ { 0 , 1 , 2 } \mathcal{C}=\frac{1}{k}\sum_{i=1}^k\left(p_i-\bar{p}\right)\cdot\left(p_i-\bar{p}\right)^T,\mathcal{C}\cdot\vec{v}_j=\lambda_j\cdot\vec{v}_j,j\in\{0,1,2\} C=k1i=1k(pipˉ)(pipˉ)T,Cv j=λjv j,j{ 0,1,2}

    • C \mathcal{C} C 是一个对称的半正定矩阵, k k k 是点数目, p ˉ \bar{p} pˉ 是周围点的质心, λ j \lambda_j λj 是协方差矩阵的第 j j j 个特征值。 v ⃗ j \vec{v}_j v j是协方差矩阵的第 j j j 个特征向量。通过以下代码可以计算质心和协方差矩阵

    // 定义一小块表面区域的协方差矩阵
    Eigen::Matrix3f covariance_matrix;
    // 定义一小块表面区域的质心坐标
    Eigen::Vector4f xyz_centroid;
    // 计算质心坐标
    pcl::compute3DCentroid(*cloud, xyz_centroid);
    // 计算 3x3 的协方差矩阵
    pcl::computeCovarianceMatrix(*cloud, xyz_centroid, covariance_matrix);
    
    • 表面曲率 Surface Curvature 通过协方差矩阵的特征值进行估算得到
      σ p = λ 0 λ 0 + λ 1 + λ 2 \sigma_p=\frac{\lambda_0}{\lambda_0+\lambda_1+\lambda_2} σp=λ0+λ1+λ2λ0
      • 这里 λ 0 = m i n ( λ j ) \lambda_0=min(\lambda_j) λ0=min(λj),即三个特征值中的最小值
  • 法线方向问题

    • 没有直接的数学方法可以解决法线的朝向问题,如下边的左图和中图,该数据集来自厨房环境的一部分。很明显图中显示出的法线方向并非朝着一个方向。可以观察右图,对所有法线合并成的扩展高斯图(EGI),也称为法线球体(Normal Sphere),它描述了点云中所有法线的方向。由于数据是 2.5 维的,即数据只从单一视角获取,其法线也应该仅是一个半球体的扩展高斯图,由于没有定下法线的方向,所以这些法线遍布球体
      在这里插入图片描述

    • 但是,如果有了视点 V p V_p Vp,则此问题就很好解决了,让所有法 n ⃗ \vec{n} n 选取其朝向视点的那个方向即可,则可以观察到将所有法线重新定向之后的效果和其对应的扩展高斯图(EGI)
      n ⃗ i ⋅ ( V p − p i ) > 0 \vec{n}_i\cdot\left(V_p-p_i\right)>0 n i(Vppi)>0
      在这里插入图片描述

  • 选择合适的比例

    • 如前所述,需要根据该点的周围点邻域支持,也称为 k-neighborhood(k邻域)来估算该点的表面法线。最近邻估计问题的细节提出了正确的比例因子的问题:给定采样点云数据集,如何选择正确的 k(通过 pcl::Feature::setKSearch 给出)或 r(通过 pcl::Feature::setRadiusSearch 给出)值来确定点的最近邻居?
      在这里插入图片描述

    • 这个问题非常重要,并且构成了点特征表示的自动估计(即在没有用户给定阈值的情况下)的限制因素。为了更好地说明此问题,下图显示了选择较小的比例(即较小的 r 或 k)与较大的比例(即较大的 r 或 k)的效果。图的左侧部分描绘了一个合理选择的比例因子,其中估计的表面法线大致垂直于两个平面,并且在整个桌子上可见小的边缘。但是,如果比例因子太大(右侧部分),则相邻对象的集合会覆盖来自相邻表面的较大点,则估计的点要素表示会失真,在两个平面边缘处旋转的曲面法线会被涂抹边缘和压制的精细细节(如果杯子的手柄和圆柱部分之间的边缘处的曲率很重要,则比例因子必须足够小以捕获这些细节,否则要大)
      在这里插入图片描述

    • 默认视点坐标为 (0,0,0),可以使用以下代码修改

    setViewPoint(float vpx, float vpy, float vpx);
    

3.3 代码实现

#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
#include <pcl/io/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/features/normal_3d.h>

int main() {
    
    
    // 导入点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile("../data/table_scene_mug_stereo_textured.pcd", *cloud);

    // 法线
    pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);

    // 创建一个用于法线估计的对象并计算法线
    pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normalEstimation;
    // normalEstimation.setIndices()
    normalEstimation.setInputCloud(cloud);
    normalEstimation.setRadiusSearch(0.03); // 对每个点,使用半径为 3cm 的所有邻居
    // 使用 kd-tree 方法来寻找最近邻
    pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
    normalEstimation.setSearchMethod(kdtree);
    normalEstimation.compute(*normals);    // 计算法线

    // 法线可视化操作
    pcl::visualization::PCLVisualizer viewer("PCL Viewer");
    viewer.setBackgroundColor(0.0, 0.0, 0.5);
    viewer.addPointCloud<pcl::PointXYZ>(cloud, "cloud");
    // 参数 int level = 2 表示每 2 个点绘制一个法向量
    // 参数 float scale = 0.01 表示法向量长度缩放为 0.01 倍
    viewer.addPointCloudNormals<pcl::PointXYZ, pcl::Normal>(cloud, normals, 2, 0.01, "normals");

    while (!viewer.wasStopped()) {
    
    
        viewer.spinOnce();
    }
    return 0;
}

4. 积分图估算点云法线

此方法进行法线估计只适用于有序点云,对于无序点云就只能采用其他方法

  • integral_image_normal.cpp

    #include <pcl/io/io.h>
    #include <pcl/io/pcd_io.h>
    #include <pcl/features/integral_image_normal.h>
    #include <pcl/visualization/cloud_viewer.h>
    
    int main() {
          
          
        // 导入点云
        pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
        pcl::io::loadPCDFile("../data/table_scene_mug_stereo_textured.pcd", *cloud);
    
        // 法线
        pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>);
    
        // 创建一个用于法线估计的对象并计算法线
        pcl::IntegralImageNormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
        // 有以下可选的估算方式:
            /*
                enum NormalEstimationMethod {
                  COVARIANCE_MATRIX,
                  AVERAGE_3D_GRADIENT,
                  AVERAGE_DEPTH_CHANGE
                };
                COVARIANCE_MATRIX 模式创建 9 个积分图像,以根据其局部邻域的协方差矩阵为特定点计算法线
                AVERAGE_3D_GRADIENT 模式创建 6 个积分图像以计算水平和垂直 3D 渐变的平滑版本,并使用这两个渐变之间的叉积计算法线
                AVERAGE_DEPTH_CHANGE 模式仅创建单个积分图像,并根据平均深度变化计算法线
            */
        ne.setNormalEstimationMethod(ne.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<pcl::PointXYZ, pcl::Normal>(cloud, normals);
    
        while (!viewer.wasStopped()) {
          
          
            viewer.spinOnce();
        }
        return 0;
    }
    
  • 配置文件 CMakeLists.txt

    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
    
    project(integral_image_normal)
    
    find_package(PCL 1.2 REQUIRED)
    
    include_directories(${
          
          PCL_INCLUDE_DIRS})
    link_directories(${
          
          PCL_LIBRARY_DIRS})
    add_definitions(${
          
          PCL_DEFINITIONS})
    
    add_executable(integral_image_normal integral_image_normal.cpp)
    target_link_libraries(integral_image_normal ${
          
          PCL_LIBRARIES})
    
  • 编译并执行

    $ mkdir build
    $ cd build
    $ cmake ..
    $ make
    
    $ ./integral_image_normal
    

在这里插入图片描述

5. 3D 特征描述子

5.1 定义与分类

  • 特征描述子(Feature Descriptor)

    • 是每个特征点独特的身份认证
    • 同一空间点在不同视角的特征点具有高度相似的描述子
    • 不同特征点的描述子差异性比较大
    • 通常描述子是一个具有固定长度的向量
  • 描述子可以分为以下几种类型

    • 基于不变性的描述子、基于直方图的描述子、二进制描述子
  • PCL 主要实现了

    • NARF 特征点描述子、PFH(FPFH)点特征直方图描述子、RoPs 特征、VFH 视点特征直方图描述子、GASD 全局对齐的空间分布描述子、基于惯性矩和偏心率的描述子

5.2 PFH(Point Feature Histograms)点特征直方图描述子

  • 表面法线和曲率估计是某个点周围的几何特征的基本表示方法。虽然计算起来比较容易,但是能够提供的信息并不多。因为他们只是用很少的几个参数值近似地表示一个点的 k 邻域几何特征。大部分场景下都会有很多相同或相似的特征值,故而只采用点特征就会少了很多全局的特征信息

    • 可以通过点特征直方图(Point Feature Histograms, PFH)来采集全局的特征信息
  • 理论基础

    • PFH 通过参数化查询点与邻域点之间的空间差异信息,形成一个多维直方图对点的 k 邻域的几何特征进行描述。直方图所在的高维超空间为特征的表示提供了一个可度量的信息空间,使点云对应的 6DoF 姿态来说具有不变性,并且在不同的采样密度或邻域噪音等级下具有鲁棒性
      在这里插入图片描述

    • 在 VR 的应用中也分为 3DoF 和 6DoF
      在这里插入图片描述

    • PFH 表示法是基于点与其 k 邻域之间的关系以及他们所构成的法线而建立的。他考虑了法线之间的关系,并较好的获取了样品表面的变化情况,进而描述了样本的几何特征。下图表示的是查询点 P q P_q Pq 的 PFH 计算影响区域。 P q P_q Pq 为中间的红色点,虚线圆内是半径为 r 的所有邻域点,他们相互连接在一个网络中,PFH 描述子是通过计算邻域内所有两点之间的关系而得到的直方图
      在这里插入图片描述

    • 为了计算两个点 P s P_s Ps P t P_t Pt 以及他们对应法 n ⃗ s \vec{n}_s n s n ⃗ t \vec{n}_t n t 之间的关系,在其中一个点上定义一个固定的局部坐标系
      在这里插入图片描述

    • 坐标系 u v w uvw uvw 的定义算法
      { u = n s v = u × ( P t − P s ) ∥ P t − P s ∥ 2 w = u × v \left\{\begin{array}{l}u=n_s\\ v=u\times\frac{(P_t-P_s)}{\|P_t-P_s\|_2}\\ w=u\times v\end{array}\right. u=nsv=u×PtPs2(PtPs)w=u×v

    • 使用上图中的 u v w uvw uvw 坐标系,我们可以用一组角度来表示法线 n ⃗ s \vec{n}_s n s n ⃗ t \vec{n}_t n t 之间的关系(偏差)
      θ = arctan ⁡ ( v ⋅ n t , u ⋅ n t ) α = v ⋅ n t ϕ = u ⋅ ( P t − P s ) d \begin{aligned}\theta&=\arctan\left(\text{v}\cdot\boldsymbol{n}_t,\text{u}\cdot\boldsymbol{n}_t\right)\\ \alpha&=\text{v}\cdot\boldsymbol{n}_t\\ \phi&=\text{u}\cdot\frac{\left(P_t-P_s\right)}{d}\end{aligned} θαϕ=arctan(vnt,unt)=vnt=ud(PtPs)

      • 这里 d d d 表示 P t P_t Pt P s P_s Ps 之间的欧氏距离,即 d = ∥ P t − P s ∥ 2 d=\|P_t-P_s\|_2 d=PtPs2,通过计算 k 邻域内的每一对点的 < α , ϕ , θ , d > <\alpha,\phi,\theta,d> <α,ϕ,θ,d> 四个值,就可以把之前两个点和其法向量相关的 12 个参数(两个点的 x y z xyz xyz 坐标和法向量)减少到 4 个。为每个点估算 PFH 四元组,可以使用以下代码
      #include <pcl/features/pfh_tools.h>
      /** \brief 计算点对的PFH特征的4个特征元素值,包含3个角度值和一个两点间的距离值
          * \param[in] p1 the first XYZ point 第一个点的xyz坐标
          * \param[in] n1 the first surface normal 第一个点所在区域的表面法向量
          * \param[in] p2 the second XYZ point 第二个点的xyz坐标
          * \param[in] n2 the second surface normal 第二个点所在区域的表面法向量
        * \param[out] f1 第一个角度特征值 Θ (angle between the projection of nq_idx and u)
        * \param[out] f2 第二个角度特征值 α (angle between nq_idx and v)
        * \param[out] f3 第三个角度特征值 Φ (angle between np_idx and |p_idx - q_idx|)
        * \param[out] f4 两点间的欧式距离 d (p_idx - q_idx)
        */
      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);
      
  • 直方图的统计方式

    • 1、将 < α , ϕ , θ , d > <\alpha,\phi,\theta,d> <α,ϕ,θ,d> 中的每个特征值范围划分为 b 个子区间(默认为 5,这个数值可以自行计算并设置),则 4 个特征共有 b 4 b^4 b4 个区间
    • 2、统计对应特征值落在每个子区间的点的数目,由于 < α , ϕ , θ > <\alpha,\phi,\theta> <α,ϕ,θ> 三个特征都是法线之间的角度信息,则它们的值很可能会归为同一个空间
    • 3、计算每一个点对的特征值,直方图中对应于该点对四个特征值区间的统计个数 +1

    注意,在一些情况下,第四个特征 d 通常跟设备捕获 2.5D 深度数据是相关的,邻近点的距离 d 是从视点开始递增的,在不同视角下,这些值的变化意义不大,所以在扫描局部点密度影响特征时,省略距离 d 效果会更好

    • 以下是两个点的PFH直方图演示
      在这里插入图片描述

    • 默认 PFH 的实现,对每个特征都使用 5 个子区间进行分类,这里不包括距离 d。这样就组成了一个 125 个浮点数元素的特征向量( 5 3 5^3 53),保存在数据类型 pcl::PFHSignature125 中。以下是根据 3D 点与法线的空间邻域估计单个点的三个角特征的 PFH(点特征直方图)

    /*
        * features for a given point based on its spatial neighborhood of 3D points with normals
        * \param[in] cloud 包含xyz坐标点信息的数据集
        * \param[in] normals 数据集中点的法向量信息
        * \param[in] indices 指定查询点,数据集中 k 邻域点的索引
        * \param[in] nr_split 每个角特征的区间数
        * \param[out] pfh_histogram 结果 PFH 直方图,表示查询点处的特征
    */
    void 
    computePointPFHSignature (const pcl::PointCloud<PointInT> &cloud,
                              const pcl::PointCloud<PointNT> &normals, 
                              const std::vector<int> &indices, int nr_split,
                              Eigen::VectorXf &pfh_histogram);
    

5.3 FPFH(Fast Point Feature Histograms)快速点特征直方图描述子

  • 如果点云 P 中有 n 个点,则其点特征直方图 PFH 的理论计算复杂度 O ( n k 2 ) O(nk^2) O(nk2),其中 k 是点云 P 中每个点 p 计算特征向量时要考虑的邻域数量。对于实时应用或近实时应用,密集点云的点特征直方图 PFH 的计算,是一个主要的性能瓶颈,故而可以将之进行简化,称为快速点特征直方图 FPFH(Fast Point Feature Histograms)。这个 FPFH 将计算复杂度降到了 O ( n k ) O(nk) O(nk),但是仍然保留了 PFH 大部分的识别特性

  • FPFH 计算过程

    • 1、为查询点求得它和其 k 邻域内每个点之间的三个特征元素值,然后统计成一个 SimplePFH
    • 2、分别对 k 邻域中的每个点确定 k 邻域,按第一步分别形成自己的 SPFH
    • 3、对邻域中的各个SPFH进行加权统计,得到最终的 FPFH 公式如下
      F P F H ( p q ) = S P F H ( p q ) + 1 k ∑ i = 1 k 1 ω k ⋅ S P F H ( p k ) \mathrm{FPFH}(\boldsymbol{p}_q)=\mathrm{SPFH}(\boldsymbol{p}_q)+\frac{1}{k}\sum_{i=1}^k\frac{1}{\omega_k}\cdot\mathrm{SPFH}(\boldsymbol{p}_k) FPFH(pq)=SPFH(pq)+k1i=1kωk1SPFH(pk)
      • 权重 ω k ω_k ωk 表示查询点 P q P_q Pq 和其临近点 P k P_k Pk 之间的距离
  • 下图为 FPFH 快速点特征直方图的影响区域图

    • 每个查询点(红色)仅连接到其直接的 k 邻居(由灰色圆圈包围)
    • 每个直接邻居都连接到其自己的邻居,并将所得直方图与查询点的直方图一起加权以形成 FPFH
    • 较粗的连接两次会被重复计数 2 次(比较重要的点对)
      在这里插入图片描述
  • FPFH 与 PFH 的主要区别

    • 1、FPFH 没有对全互连点的所有邻近点的计算参数进行统计,因此可能漏掉了一些重要的点对,而这些漏掉的对点可能对捕获查询点周围的几何特征有贡献
    • 2、PFH 特征模型是对查询点周围的一个精确的邻域半径内,而 FPFH 还包括半径 r 范围以外的额外点对(但不超过 2r 的范围)
    • 3、因为采用权重计算的方式,所以 FPFH 结合 SPFH 值,重新捕获邻近重要点对的几何信息
    • 4、由于 FPFH 大大地降低了 PFH 的整体复杂性,因此 FPFH 经常使用在实时应用中
    • 5、通过分解三元组,简化了合成的直方图。也就是简单生成 d 分离特征直方图,对每个特征维度来单独绘制,并把它们连接在一起
      在这里插入图片描述
  • 默认 PCL 实现的的 FPFH 使用 11 个统计区间(对每个特征值都将其参数区间分割为 11 个),分别计算特征直方图,然后合并得到了一个 33 个元素的特征向量,保存在数据类型 pcl::FPFHSignature33 中

5.4 VFH(Viewpoint Feature Histogram)视点特征直方图描述子

  • 视点特征直方图VFH概念及使用
  • 源于 FPFH 描述子,为了使构造的特征保持缩放不变性的同时,还要区分不同的位姿,因而计算时,需要加入视点信息。VFH 视点特征直方图包含两个部分
    • 视点方向的相关分量
    • 包含扩展 FPFH 的描述表面形状的分量

5.5 NARF(Normal Aligned Radial Feature)法线对齐径向特征描述子

猜你喜欢

转载自blog.csdn.net/qq_42994487/article/details/130546048
今日推荐