• 7.features特征


    3d特征点概述1

    3d特征点概述2

    各特征描述详见以上大佬博客。

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

    • 局部特征的描述
      • 局部的法线等几何形状特征的描述
    • 全局特征的描述
      • 全局的拓朴特征的描述

    原理:

            在原始表示形式下,点的定义是用笛卡尔坐标系坐标 x, y, z 相对于一个给定的原点来简单表示的三维映射系统的概念,假定坐标系的原点不随着时间而改变,这里有两个点p1和p2分别在时间t1和t2捕获,有着相同的坐标,对这两个点作比较其实是属于不适定问题(ill—posed problem),因为虽然相对于一些距离测度它们是相等的,但是它们取样于完全不同的表面,因此当把它们和临近的其他环境中点放在一起时,它们表达着完全不同的信息,这是因为在t1和t2之间局部环境有可能发生改变。一些获取设备也许能够提供取样点的额外数据,例如强度或表面反射率等,甚至颜色,然而那并不能完全解决问题,单从两个点之间来   对比仍然是不适定问题。

    由于各种不同需求需要进行对比以便能够区分曲面空间的分布情况,应用软件要求更好的特征度量方式,因此作为一个单一实体的三维点概念和笛卡尔坐标系被淘汰了,出现了一个新的概念取而代之:局部描述子(locl descriptor)。
    文献中对这一概念的描述有许多种不同的命名,如:形状描述子(shape descriptors)或几何特征(geometric features),文本中剩余部分都统称为点特征表示。通过包括周围的领域,特征描述子能够表征采样表面的几何性质,它有助于解决不适定的对比问题,理想情况下相同或相似表面上的点的特征值将非常相似(相对特定度量准则),而不同表面上的点的特征描述子将有明显差异。下面几个条件,通过能否获得相同的局部表面特征值,可以判定点特征表示方式的优劣:
    (1)刚体变换-----即三维旋转和三维平移变化 不会影响特征向量F估计,即特征向量具有平移旋转不变性。
    (2)改变采样密度-----原则上,一个局部表面小块的采样密度无论是大还是小,都应该有相同的特征向量值,即特征向量具有抗密度干扰性。
    (3)噪声---数据中有轻微噪声的情况下,点特征表示在它的特征向量中必须保持相同或者极其相似的值,即特征向量对点云噪声具有稳定性。
    通常,PCL中特征向量利用快速kd-tree查询 ,使用近似法来计算查询点的最近邻元素,通常有两种查询类型:K邻域查询,半径搜索两种方法

    1.法向量估计与快速点特征直方图(FPFH)描述子

    法向量估计:

    1. //打开点云代码
    2. pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
    3. pcl::io::loadPCDFile ("../table_scene_lms400.pcd", *cloud);
    4. //创建法线估计估计向量
    5. pcl::NormalEstimation ne;
    6. ne.setInputCloud(cloud);
    7. //创建一个空的KdTree对象,并把它传递给法线估计向量
    8. //基于给出的输入数据集,KdTree将被建立
    9. pcl::search::KdTree::Ptr tree (new pcl::search::KdTree ());
    10. ne.setSearchMethod(tree);
    11. //存储输出数据
    12. pcl::PointCloud::Ptr cloud_normals (new pcl::PointCloud);
    13. //使用半径在查询点周围3厘米范围内的所有临近元素
    14. ne.setRadiusSearch(0.03);
    15. //计算特征值
    16. ne.compute(*cloud_normals);

     法向量估计后计算fpfh:

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. using PointT = pcl::PointCloud;
    13. using PointN = pcl::PointCloud;
    14. using Normal = pcl::PointCloud;
    15. void test_demo()
    16. {
    17. pcl::PointCloud::Ptr cloud(new pcl::PointCloud);
    18. pcl::PointCloud::Ptr voxeled_cloud(new pcl::PointCloud);
    19. pcl::io::loadPCDFile("rabbit.pcd", *cloud);
    20. //下采样
    21. pcl::VoxelGrid voxel;
    22. voxel.setInputCloud(cloud);
    23. voxel.setLeafSize(0.1f, 0.1f,0.1f);
    24. voxel.filter(*voxeled_cloud);
    25. //法向量
    26. pcl::NormalEstimationOMPne;
    27. ne.setNumberOfThreads(4);
    28. ne.setInputCloud(voxeled_cloud);
    29. pcl::search::KdTree::Ptr tree;
    30. ne.setSearchMethod(tree);
    31. ne.setRadiusSearch(0.5f);
    32. PointN::Ptr normal_cloud(new PointN);
    33. ne.compute(*normal_cloud);
    34. //显示法向量
    35. for (int i = 0; i < normal_cloud->size(); ++i)
    36. {
    37. normal_cloud->points[i].x = voxeled_cloud->points[i].x;
    38. normal_cloud->points[i].y = voxeled_cloud->points[i].y;
    39. normal_cloud->points[i].z = voxeled_cloud->points[i].z;
    40. }
    41. pcl::visualization::PCLVisualizer viewer;
    42. viewer.setBackgroundColor(0, 0, 0);
    43. viewer.addPointCloud(voxeled_cloud,"voxel_cloud");
    44. int level = 1; // 多少条法向量集合显示成一条
    45. float scale = 0.05; // 法向量长度
    46. viewer.addPointCloudNormals(normal_cloud, level, scale, "normals");
    47. //显示原点云
    48. viewer.addPointCloud(cloud, "cloud");
    49. pcl::visualization::PointCloudColorHandlerCustom color_handler(normal_cloud, 255, 0, 0);
    50. //fpfh特征直方图
    51. pcl::FPFHEstimationfpfh;
    52. fpfh.setInputCloud(voxeled_cloud);
    53. //fpfh.setSearchSurface(cloud);
    54. fpfh.setInputNormals(normal_cloud);
    55. pcl::search::KdTree::Ptr tree1(new pcl::search::KdTree);
    56. fpfh.setSearchMethod(tree1);
    57. pcl::PointCloud::Ptr fpfh_cloud(new(pcl::PointCloud));
    58. fpfh.setRadiusSearch(0.05f);
    59. //检查法向量
    60. for (int i = 0; i < normal_cloud->size(); i++)
    61. {
    62. if (!pcl::isFinite((*normal_cloud)[i]))
    63. {
    64. PCL_WARN("normals[%d] is not finite\n", i);
    65. }
    66. }
    67. fpfh.compute(*fpfh_cloud);
    68. viewer.spin();
    69. system("pause");
    70. }



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

    正如点特征表示法所示,表面法线和曲率估计是某个点周围的几何特征基本表示法。虽然计算非常快速容易,但是无法获得太多信息,因为它们只使用很少的几个参数值来近似表示一个点的k邻域的几何特征。

    然而大部分场景中包含许多特征点,这些特征点有相同的或者非常相近的特征值,因此采用点特征表示法,其直接结果就减少了全局的特征信息

    那么三维特征描述子中一位成员:点特征直方图(Point Feature Histograms),我们简称为PFH,从PCL实现的角度讨论其实施细节。PFH特征不仅与坐标轴三维数据有关,同时还与表面法线有关。

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

    点特征直方图(PFH)表示法是基于点与其k邻域之间的关系以及它们的估计法线,简言之,它考虑估计法线方向之间所有的相互作用,试图捕获最好的样本表面变化情况,以描述样本的几何特征。因此,合成特征超空间取决于每个点的表面法线估计的质量。

    如图所示,表示的是一个查询点(Pq) 的PFH计算的影响区域,Pq 用红色标注并放在圆球的中间位置,半径为r, (Pq)的所有k邻元素(即与点Pq的距离小于半径r的所有点)全部互相连接在一个网络中。最终的PFH描述子通过计算邻域内所有两点之间关系而得到的直方图,因此存在一个O(k) 的计算复杂性。

          

    为了计算两点Pi和Pj及与它们对应的法线Ni和Nj之间的相对偏差,在其中的一个点上定义一个固定的局部坐标系,如图2所示。

                   

    使用上图中uvw坐标系,法线 和 之间的偏差可以用一组角度来表示,如下所示:

      估计PFH特征

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

    以下代码段将对输入数据集中的所有点估计其对应的PFH特征。

    1. computePointPFHSignature (const pcl::PointCloud &cloud,
    2. const pcl::PointCloud &normals,
    3. const std::vector<int> &indices,
    4. int nr_split,
    5. Eigen::VectorXf &pfh_histogram);

    3.SHOT

    主要代码

    1. typedef pcl::PointXYZ PointT;
    2. typedef pcl::Normal PointNT;
    3. typedef pcl::SHOT352 FeatureT;
    4. pcl::SHOTEstimation shot;
    5. shot.setRadiusSearch(18 * resolution);
    6. shot.setInputCloud(keys);
    7. shot.setSearchSurface(cloud);
    8. shot.setInputNormals(cloud_normal);
    9. //shot.setInputReferenceFrames(lrf); //也可以自己传入局部坐标系
    10. pcl::PointCloud::Ptr features(new pcl::PointCloud);
    11. shot.compute(*features);

    4.ROPS

    由于RoPS是基于网格数据,所以如果输入的是点云数据需要先进行网格化处理。

    主要代码

    1. pcl::PointCloud::Ptr cloud(new pcl::PointCloud);
    2. pcl::io::loadPCDFile(argv[1], *cloud);
    3. // 加载关键点
    4. pcl::PointCloud::Ptr key_points(new pcl::PointCloud);
    5. pcl::io::loadPCDFile(argv[2], *key_points);
    6. // 计算法向量
    7. pcl::NormalEstimation n;
    8. pcl::PointCloud::Ptr normals(new pcl::PointCloud);
    9. pcl::search::KdTree::Ptr tree(new pcl::search::KdTree);
    10. tree->setInputCloud(cloud);
    11. n.setInputCloud(cloud);
    12. n.setSearchMethod(tree);
    13. n.setKSearch(20);
    14. n.compute(*normals);
    15. // 连接数据
    16. pcl::PointCloud::Ptr cloud_with_normals(new pcl::PointCloud);
    17. pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
    18. //* cloud_with_normals = cloud + normals
    19. // ---- rops基于网格,所以要先将pcd点云数据重建网格 ---
    20. // Create search tree*
    21. pcl::search::KdTree::Ptr tree2(new pcl::search::KdTree);
    22. tree2->setInputCloud(cloud_with_normals);
    23. pcl::GreedyProjectionTriangulation gp3;// Initialize objects
    24. pcl::PolygonMesh triangles;
    25. // Set the maximum distance between connected points (maximum edge length)
    26. gp3.setSearchRadius(0.025);
    27. gp3.setMu(2.5); // Set typical values for the parameters
    28. gp3.setMaximumNearestNeighbors(100);
    29. gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
    30. gp3.setMinimumAngle(M_PI / 18); // 10 degrees
    31. gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
    32. gp3.setNormalConsistency(false);
    33. gp3.setInputCloud(cloud_with_normals);
    34. gp3.setSearchMethod(tree2);
    35. gp3.reconstruct(triangles); // Get result
    36. // ----- rops 描述-------
    37. // 由于pcl_1.8.0中rops还没有定义好的结构,所以采用pcl::Histogram存储描述子
    38. pcl::ROPSEstimation135>> rops;
    39. rops.setInputCloud(key_points);
    40. rops.setSearchSurface(cloud);
    41. rops.setNumberOfPartitionBins(5);
    42. rops.setNumberOfRotations(3);
    43. rops.setRadiusSearch(0.01);
    44. rops.setSupportRadius(0.01);
    45. rops.setTriangles(triangles.polygons);
    46. rops.setSearchMethod(pcl::search::KdTree::Ptr(new pcl::search::KdTree < pcl::PointXYZ>));
    47. //feature size = number_of_rotations * number_of_axis_to_rotate_around * number_of_projections * number_of_central_moments
    48. //unsigned int feature_size = number_of_rotations_ * 3 * 3 * 5; //计算出135
    49. pcl::PointCloud135>> description;
    50. rops.compute(description); // 结果计算的是描述子。。需传入inputcloud和surface
    51. std::cout << "size is " << description.points.size()<::Ptr cloud(new pcl::PointCloud);
    52. pcl::io::loadPCDFile(argv[1], *cloud);
    53. // 加载关键点
    54. pcl::PointCloud::Ptr key_points(new pcl::PointCloud);
    55. pcl::io::loadPCDFile(argv[2], *key_points);
    56. // 计算法向量
    57. pcl::NormalEstimation n;
    58. pcl::PointCloud::Ptr normals(new pcl::PointCloud);
    59. pcl::search::KdTree::Ptr tree(new pcl::search::KdTree);
    60. tree->setInputCloud(cloud);
    61. n.setInputCloud(cloud);
    62. n.setSearchMethod(tree);
    63. n.setKSearch(20);
    64. n.compute(*normals);
    65. // 连接数据
    66. pcl::PointCloud::Ptr cloud_with_normals(new pcl::PointCloud);
    67. pcl::concatenateFields(*cloud, *normals, *cloud_with_normals);
    68. //* cloud_with_normals = cloud + normals
    69. // ---- rops基于网格,所以要先将pcd点云数据重建网格 ---
    70. // Create search tree*
    71. pcl::search::KdTree::Ptr tree2(new pcl::search::KdTree);
    72. tree2->setInputCloud(cloud_with_normals);
    73. pcl::GreedyProjectionTriangulation gp3;// Initialize objects
    74. pcl::PolygonMesh triangles;
    75. // Set the maximum distance between connected points (maximum edge length)
    76. gp3.setSearchRadius(0.025);
    77. gp3.setMu(2.5); // Set typical values for the parameters
    78. gp3.setMaximumNearestNeighbors(100);
    79. gp3.setMaximumSurfaceAngle(M_PI / 4); // 45 degrees
    80. gp3.setMinimumAngle(M_PI / 18); // 10 degrees
    81. gp3.setMaximumAngle(2 * M_PI / 3); // 120 degrees
    82. gp3.setNormalConsistency(false);
    83. gp3.setInputCloud(cloud_with_normals);
    84. gp3.setSearchMethod(tree2);
    85. gp3.reconstruct(triangles); // Get result
    86. // ----- rops 描述-------
    87. // 由于pcl_1.8.0中rops还没有定义好的结构,所以采用pcl::Histogram存储描述子
    88. pcl::ROPSEstimation135>> rops;
    89. rops.setInputCloud(key_points);
    90. rops.setSearchSurface(cloud);
    91. rops.setNumberOfPartitionBins(5);
    92. rops.setNumberOfRotations(3);
    93. rops.setRadiusSearch(0.01);
    94. rops.setSupportRadius(0.01);
    95. rops.setTriangles(triangles.polygons);
    96. rops.setSearchMethod(pcl::search::KdTree::Ptr(new pcl::search::KdTree < pcl::PointXYZ>));
    97. //feature size = number_of_rotations * number_of_axis_to_rotate_around * number_of_projections * number_of_central_moments
    98. //unsigned int feature_size = number_of_rotations_ * 3 * 3 * 5; //计算出135
    99. pcl::PointCloud135>> description;
    100. rops.compute(description); // 结果计算的是描述子。。需传入inputcloud和surface
    101. std::cout << "size is " << description.points.size()<













     

  • 相关阅读:
    【六袆 - MySQL】SQL优化;Explain SQL执行计划分析;
    ChatGPT在数据分析学习阶段的应用
    计算机网络基础一
    12个微服务架构模式最佳实践
    菜狗杯Misc迅疾响应wp
    golang 解决invalid argument tmp (type interface {}) for len
    java.sql.SQLExceptio
    达梦DEM监控配置
    深入理解 C 语言的内存管理
    Modbus CRC
  • 原文地址:https://blog.csdn.net/m0_57747965/article/details/126292661