当前位置:   article > 正文

PCL笔记八:关键点_pcl::pointcloud

pcl::pointcloud

关键点提取是2D和3D信息处理中不可或缺的关键技术。

NARF(Normal Aligned Radial Feature)关键点

NARF(Normal Aligned Radial Feature)关键点是为了从深度图像识别物体而提出的,关键点探测的重要一步是减少特征提取时的搜索空间,把重点放在重要的结构上,对NARF关键点提取过程中有以下要求

  • 提取的过程必须将边缘以及物体表面变化信息考虑在内;
  • 关键点的位置必须稳定,可以被重复探测,及时换了不同的视角
  • 关键点所在的位置必须有稳定的支持区域,可以计算描述子并进行唯一的法向量估计。

关键点探测步骤如下:

  1. 遍历每个深度图像点,通过寻找在近邻区域深度突变的位置进行边缘检测
  2. 遍历每个深度图像点,根据近邻区域的表面变化决定一种测度表面变化的系数,以及变化的主方向
  3. 根据第二步找到的主方向计算兴趣值,表征该方向与其他方向的不同,以及该处表面的变化情况,即改点有多稳定。
  4. 对兴趣值进行平滑过滤。
  5. 进行无最大值压缩找到最终的关键点,纪委NARF关键点。

Harris关键点:

Harris关键点检测通过计算图像点的Harris矩阵矩阵对应的特征值来判断是否为关键点。如果Harris矩阵特征的两个特征值都很大,则该点为关键点

Harris关键点检测算子对于图像旋转变换保持较好的检测重复率,但不适合尺寸变化的关键点检测。

点云中的3D Harris关键点借鉴了2D Harris关键点检测的思想,不过3D Harris关键点检测使用的是点云表面法向量的信息,而不是2D Harris关键点检测使用的图像梯度



距离图像中提取NARF关键点

  1. /* \author Bastian Steder */
  2. #include <iostream>
  3. #include <boost/thread/thread.hpp>
  4. #include <pcl/range_image/range_image.h>
  5. #include <pcl/io/pcd_io.h>
  6. #include <pcl/visualization/range_image_visualizer.h>
  7. #include <pcl/visualization/pcl_visualizer.h>
  8. #include <pcl/features/range_image_border_extractor.h>
  9. #include <pcl/keypoints/narf_keypoint.h>
  10. #include <pcl/console/parse.h>
  11. #include <pcl/common/file_io.h> // for getFilenameWithoutExtension
  12. #ifdef WIN32
  13. #define pcl_sleep(x) Sleep(1000*(x))
  14. #else
  15. #define pcl_sleep(x) sleep(x)
  16. #endif
  17. typedef pcl::PointXYZ PointType;
  18. //参数
  19. float angular_resolution = 0.5f;
  20. float support_size = 0.2f;
  21. pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;
  22. bool setUnseenToMaxRange = false;
  23. //打印帮助
  24. void
  25. printUsage(const char* progName)
  26. {
  27. std::cout << "\n\nUsage: " << progName << " [options] <scene.pcd>\n\n"
  28. << "Options:\n"
  29. << "-------------------------------------------\n"
  30. << "-r <float> angular resolution in degrees (default " << angular_resolution << ")\n"
  31. << "-c <int> coordinate frame (default " << (int)coordinate_frame << ")\n"
  32. << "-m Treat all unseen points as maximum range readings\n"
  33. << "-s <float> support size for the interest points (diameter of the used sphere - "
  34. << "default " << support_size << ")\n"
  35. << "-h this help\n"
  36. << "\n\n";
  37. }
  38. void
  39. setViewerPose(pcl::visualization::PCLVisualizer& viewer, const Eigen::Affine3f& viewer_pose)
  40. {
  41. Eigen::Vector3f pos_vector = viewer_pose * Eigen::Vector3f(0, 0, 0);
  42. Eigen::Vector3f look_at_vector = viewer_pose.rotation() * Eigen::Vector3f(0, 0, 1) + pos_vector;
  43. Eigen::Vector3f up_vector = viewer_pose.rotation() * Eigen::Vector3f(0, -1, 0);
  44. //viewer.camera_.pos[0] = pos_vector[0];
  45. //viewer.camera_.pos[1] = pos_vector[1];
  46. //viewer.camera_.pos[2] = pos_vector[2];
  47. //viewer.camera_.focal[0] = look_at_vector[0];
  48. //viewer.camera_.focal[1] = look_at_vector[1];
  49. //viewer.camera_.focal[2] = look_at_vector[2];
  50. //viewer.camera_.view[0] = up_vector[0];
  51. //viewer.camera_.view[1] = up_vector[1];
  52. //viewer.camera_.view[2] = up_vector[2];
  53. viewer.setCameraPosition(pos_vector[0], pos_vector[1], pos_vector[2], look_at_vector[0], look_at_vector[1], look_at_vector[2], up_vector[0], up_vector[1], up_vector[2]);
  54. //viewer.updateCamera();
  55. }
  56. // -----Main-----
  57. int
  58. main(int argc, char** argv)
  59. {
  60. //解析命令行参数
  61. if (pcl::console::find_argument(argc, argv, "-h") >= 0)
  62. {
  63. printUsage(argv[0]);
  64. return 0;
  65. }
  66. if (pcl::console::find_argument(argc, argv, "-m") >= 0)
  67. {
  68. setUnseenToMaxRange = true;
  69. cout << "Setting unseen values in range image to maximum range readings.\n";
  70. }
  71. int tmp_coordinate_frame;
  72. if (pcl::console::parse(argc, argv, "-c", tmp_coordinate_frame) >= 0)
  73. {
  74. coordinate_frame = pcl::RangeImage::CoordinateFrame(tmp_coordinate_frame);
  75. cout << "Using coordinate frame " << (int)coordinate_frame << ".\n";
  76. }
  77. if (pcl::console::parse(argc, argv, "-s", support_size) >= 0)
  78. cout << "Setting support size to " << support_size << ".\n";
  79. if (pcl::console::parse(argc, argv, "-r", angular_resolution) >= 0)
  80. cout << "Setting angular resolution to " << angular_resolution << "deg.\n";
  81. angular_resolution = pcl::deg2rad(angular_resolution);
  82. //读取给定的pcd文件或者自行创建随机点云
  83. pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);
  84. pcl::PointCloud<PointType>& point_cloud = *point_cloud_ptr;
  85. pcl::PointCloud<pcl::PointWithViewpoint>far_ranges;
  86. Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());
  87. std::vector<int>pcd_filename_indices = pcl::console::parse_file_extension_argument(argc, argv, "pcd");
  88. if (!pcd_filename_indices.empty())
  89. {
  90. std::string filename = argv[pcd_filename_indices[0]];
  91. if (pcl::io::loadPCDFile(filename, point_cloud) == -1)
  92. {
  93. cerr << "Was not able to open file \"" << filename << "\".\n";
  94. printUsage(argv[0]);
  95. return 0;
  96. }
  97. scene_sensor_pose = Eigen::Affine3f(Eigen::Translation3f(point_cloud.sensor_origin_[0],
  98. point_cloud.sensor_origin_[1],
  99. point_cloud.sensor_origin_[2])) *
  100. Eigen::Affine3f(point_cloud.sensor_orientation_);
  101. std::string far_ranges_filename = pcl::getFilenameWithoutExtension(filename) + "_far_ranges.pcd";
  102. if (pcl::io::loadPCDFile(far_ranges_filename.c_str(), far_ranges) == -1)
  103. std::cout << "Far ranges file \"" << far_ranges_filename << "\" does not exists.\n";
  104. }
  105. else
  106. {
  107. setUnseenToMaxRange = true;
  108. cout << "\nNo *.pcd file given =>Genarating example point cloud.\n\n";
  109. for (float x = -0.5f; x <= 0.5f; x += 0.01f)
  110. {
  111. for (float y = -0.5f; y <= 0.5f; y += 0.01f)
  112. {
  113. PointType point; point.x = x; point.y = y; point.z = 2.0f - y;
  114. point_cloud.points.push_back(point);
  115. }
  116. }
  117. point_cloud.width = (int)point_cloud.points.size(); point_cloud.height = 1;
  118. }
  119. //从点云创建距离图像
  120. float noise_level = 0.0;
  121. float min_range = 0.0f;
  122. int border_size = 1;
  123. pcl::RangeImage::Ptr range_image_ptr(new pcl::RangeImage);
  124. pcl::RangeImage& range_image = *range_image_ptr;
  125. range_image.createFromPointCloud(point_cloud, angular_resolution, pcl::deg2rad(360.0f), pcl::deg2rad(180.0f),
  126. scene_sensor_pose, coordinate_frame, noise_level, min_range, border_size);
  127. range_image.integrateFarRanges(far_ranges);
  128. if (setUnseenToMaxRange)
  129. range_image.setUnseenToMaxRange();
  130. // 创建3D点云可视化窗口,并显示点云
  131. pcl::visualization::PCLVisualizer viewer("3D Viewer");
  132. viewer.setBackgroundColor(1, 1, 1);
  133. pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange>range_image_color_handler(range_image_ptr, 0, 0, 0);
  134. viewer.addPointCloud(range_image_ptr, range_image_color_handler, "range image");
  135. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image");
  136. //viewer.addCoordinateSystem (1.0f);
  137. //PointCloudColorHandlerCustom<PointType>point_cloud_color_handler (point_cloud_ptr, 150, 150, 150);
  138. //viewer.addPointCloud (point_cloud_ptr, point_cloud_color_handler, "original point cloud");
  139. viewer.initCameraParameters();
  140. setViewerPose(viewer, range_image.getTransformationToWorldSystem());
  141. // 显示距离图像
  142. pcl::visualization::RangeImageVisualizer range_image_widget("Range image");
  143. range_image_widget.showRangeImage(range_image);
  144. //提取NARF关键点
  145. pcl::RangeImageBorderExtractor range_image_border_extractor; // 用来边缘提取。NARF第一步就是需要探测出深度图像的边缘。
  146. pcl::NarfKeypoint narf_keypoint_detector(&range_image_border_extractor);
  147. narf_keypoint_detector.setRangeImage(&range_image);
  148. narf_keypoint_detector.getParameters().support_size = support_size;
  149. //narf_keypoint_detector.getParameters ().add_points_on_straight_edges = true;
  150. //narf_keypoint_detector.getParameters ().distance_for_additional_points = 0.5;
  151. pcl::PointCloud<int>keypoint_indices;
  152. narf_keypoint_detector.compute(keypoint_indices); // 关键点探测
  153. std::cout << "Found " << keypoint_indices.points.size() << " key points.\n";
  154. //在距离图像显示组件内显示关键点
  155. //for (size_ti=0; i<keypoint_indices.points.size (); ++i)
  156. //range_image_widget.markPoint (keypoint_indices.points[i]%range_image.width,
  157. //keypoint_indices.points[i]/range_image.width);
  158. //3D窗口中显示关键点
  159. pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_ptr(new pcl::PointCloud<pcl::PointXYZ>);
  160. pcl::PointCloud<pcl::PointXYZ>& keypoints = *keypoints_ptr;
  161. keypoints.points.resize(keypoint_indices.points.size());
  162. for (size_t i = 0; i < keypoint_indices.points.size(); ++i)
  163. keypoints.points[i].getVector3fMap() = range_image.points[keypoint_indices.points[i]].getVector3fMap();
  164. pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>keypoints_color_handler(keypoints_ptr, 0, 255, 0);
  165. viewer.addPointCloud<pcl::PointXYZ>(keypoints_ptr, keypoints_color_handler, "keypoints");
  166. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "keypoints");
  167. // 主循环
  168. while (!viewer.wasStopped())
  169. {
  170. range_image_widget.spinOnce();// process GUI events
  171. viewer.spinOnce();
  172. pcl_sleep(0.01);
  173. }
  174. }


SIFT关键点提取

SIFT:尺度不变特征变换(Scale-invariant feature transform, SIFT)。局部特征描述子。

  1. #include <iostream>
  2. #include <pcl/io/pcd_io.h>
  3. #include <pcl/point_types.h>
  4. #include <pcl/common/io.h>
  5. #include <pcl/keypoints/sift_keypoint.h>
  6. #include <pcl/features/normal_3d.h>
  7. #include <pcl/visualization/pcl_visualizer.h>
  8. #include <pcl/console/time.h>
  9. using namespace std;
  10. namespace pcl
  11. {
  12. template<>
  13. struct SIFTKeypointFieldSelector<PointXYZ>
  14. {
  15. inline float
  16. operator () (const PointXYZ& p) const
  17. {
  18. return p.z;
  19. }
  20. };
  21. }
  22. int
  23. main(int argc, char* argv[])
  24. {
  25. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz(new pcl::PointCloud<pcl::PointXYZ>);
  26. pcl::io::loadPCDFile(argv[1], *cloud_xyz);
  27. const float min_scale = stof(argv[2]);
  28. const int n_octaves = stof(argv[3]);
  29. const int n_scales_per_octave = stof(argv[4]);
  30. const float min_contrast = stof(argv[5]);
  31. pcl::SIFTKeypoint<pcl::PointXYZ, pcl::PointWithScale> sift;//创建sift关键点检测对象
  32. pcl::PointCloud<pcl::PointWithScale> result;
  33. sift.setInputCloud(cloud_xyz);//设置输入点云
  34. pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
  35. sift.setSearchMethod(tree);//创建一个空的kd树对象tree,并把它传递给sift检测对象
  36. sift.setScales(min_scale, n_octaves, n_scales_per_octave);//指定搜索关键点的尺度范围:
  37. sift.setMinimumContrast(min_contrast);//设置限制关键点检测的阈值
  38. sift.compute(result);//执行sift关键点检测,保存结果在result:类型为pcl::PointWithScale。
  39. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_temp(new pcl::PointCloud<pcl::PointXYZ>);
  40. // 为了后期处理与显示的需要。需要将SIFT关键点检测结果转换为点类型为pcl::PointXYZ的数据。
  41. copyPointCloud(result, *cloud_temp);//将点类型pcl::PointWithScale的数据转换为点类型pcl::PointXYZ的数据
  42. //可视化输入点云和关键点
  43. pcl::visualization::PCLVisualizer viewer("Sift keypoint");
  44. viewer.setBackgroundColor(255, 255, 255);
  45. viewer.addPointCloud(cloud_xyz, "cloud");
  46. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 0, "cloud");
  47. viewer.addPointCloud(cloud_temp, "keypoints");
  48. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 9, "keypoints");
  49. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 255, "keypoints");
  50. while (!viewer.wasStopped())
  51. {
  52. viewer.spinOnce();
  53. }
  54. return 0;
  55. }

SIFT关键点检测队形相关的参数:

setScales函数用于指定搜索关键点的尺度范围。

sift.setScales(min_scale, n_octaves, n_scales_per_octave);//指定搜索关键点的尺度范围:
  • min_scale:用于设置尺度空间中最小尺度的标准偏差,
  • 参数octaves是高斯金字塔中组(Octave)的数目,
  • 参数nr_scales_per_octave是每组(Octave)计算的尺度(scale)数目。


Harris关键点提取

既可以提取角点也可以提取边缘点。3D Harris角点检测利用的是点云法向量的信息。

  1. #include <iostream>
  2. #include <pcl\io\pcd_io.h>
  3. #include <pcl/point_cloud.h>
  4. #include <pcl/visualization/pcl_visualizer.h>
  5. #include <pcl/io/io.h>
  6. #include <pcl/keypoints/harris_3D.h> //harris特征点估计类头文件声明
  7. #include <cstdlib>
  8. #include <vector>
  9. #include <pcl/console/parse.h>
  10. using namespace std;
  11. int main(int argc, char* argv[])
  12. {
  13. pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud(new pcl::PointCloud<pcl::PointXYZ>);
  14. pcl::io::loadPCDFile(argv[1], *input_cloud);
  15. pcl::PCDWriter writer;
  16. float r_normal;
  17. float r_keypoint;
  18. r_normal = stof(argv[2]);
  19. r_keypoint = stof(argv[3]);
  20. typedef pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZI> ColorHandlerT3;
  21. // 创建关键点估计对象,用于保存Harris关键点。
  22. pcl::PointCloud<pcl::PointXYZI>::Ptr Harris_keypoints(new pcl::PointCloud<pcl::PointXYZI>());
  23. // Harris特征检测对象
  24. pcl::HarrisKeypoint3D<pcl::PointXYZ, pcl::PointXYZI, pcl::Normal>* harris_detector = new pcl::HarrisKeypoint3D<pcl::PointXYZ, pcl::PointXYZI, pcl::Normal>;
  25. //harris_detector->setNonMaxSupression(true);
  26. harris_detector->setRadius(r_normal); // 法向量估计的半径。
  27. harris_detector->setRadiusSearch(r_keypoint); // 关键点估计的近邻搜索半径。
  28. harris_detector->setInputCloud(input_cloud);
  29. //harris_detector->setNormals(normal_source);
  30. //harris_detector->setMethod(pcl::HarrisKeypoint3D<pcl::PointXYZRGB,pcl::PointXYZI>::LOWE);
  31. harris_detector->compute(*Harris_keypoints);
  32. cout << "Harris_keypoints的大小是" << Harris_keypoints->size() << endl;
  33. writer.write<pcl::PointXYZI>("Harris_keypoints.pcd", *Harris_keypoints, false);
  34. pcl::visualization::PCLVisualizer visu3("clouds");
  35. visu3.setBackgroundColor(255, 255, 255);
  36. visu3.addPointCloud(Harris_keypoints, ColorHandlerT3(Harris_keypoints, 0.0, 0.0, 255.0), "Harris_keypoints");
  37. visu3.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 8, "Harris_keypoints");
  38. visu3.addPointCloud(input_cloud, "input_cloud");
  39. visu3.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 0, "input_cloud");
  40. visu3.spin();
  41. }

设置【项目】【属性】【配置属性】【调试】【命令参数】:room.pcd 0.1 0.1



基于对应点分类的对象识别

对应点聚类算法:利用特征匹配得到场景中的对应点,基于对应点聚类为待识别模型实例

算法输出的每个聚类,代表场景中的一个模型实例假设,同时,对应一个变换矩阵,是该模型实例假设对应的位姿估计

  1. #include <tchar.h>
  2. #include <pcl/io/pcd_io.h>
  3. #include <pcl/point_cloud.h>
  4. #include <pcl/correspondence.h>
  5. #include <pcl/features/normal_3d_omp.h>
  6. #include <pcl/features/shot_omp.h>
  7. #include <pcl/features/board.h>
  8. #include <pcl/keypoints/uniform_sampling.h>
  9. #include <pcl/recognition/cg/hough_3d.h>
  10. #include <pcl/recognition/cg/geometric_consistency.h>
  11. #include <pcl/visualization/pcl_visualizer.h>
  12. #include <pcl/kdtree/kdtree_flann.h>
  13. #include <pcl/kdtree/impl/kdtree_flann.hpp>
  14. #include <pcl/common/transforms.h>
  15. #include <pcl/console/parse.h>
  16. #include <pcl/filters/uniform_sampling.h> // class "pcl::UniformSampling<PointType>" 没有成员 "compute"
  17. typedef pcl::PointXYZRGBA PointType;
  18. typedef pcl::Normal NormalType;
  19. typedef pcl::ReferenceFrame RFType;
  20. typedef pcl::SHOT352 DescriptorType;
  21. std::string model_filename_;
  22. std::string scene_filename_;
  23. //Algorithm params
  24. bool show_keypoints_(false);
  25. bool show_correspondences_(false);
  26. bool use_cloud_resolution_(false);
  27. bool use_hough_(true);
  28. float model_ss_(0.01f);
  29. float scene_ss_(0.03f);
  30. float rf_rad_(0.015f);
  31. float descr_rad_(0.02f);
  32. float cg_size_(0.01f);
  33. float cg_thresh_(5.0f);
  34. # define pcl_isfinite(x) std::isfinite(x)
  35. void
  36. showHelp(char* filename)
  37. {
  38. std::cout << std::endl;
  39. std::cout << "***************************************************************************" << std::endl;
  40. std::cout << "* *" << std::endl;
  41. std::cout << "* Correspondence Grouping Tutorial - Usage Guide *" << std::endl;
  42. std::cout << "* *" << std::endl;
  43. std::cout << "***************************************************************************" << std::endl << std::endl;
  44. std::cout << "Usage: " << filename << " model_filename.pcd scene_filename.pcd [Options]" << std::endl << std::endl;
  45. std::cout << "Options:" << std::endl;
  46. std::cout << " -h: Show this help." << std::endl;
  47. std::cout << " -k: Show used keypoints." << std::endl;
  48. std::cout << " -c: Show used correspondences." << std::endl;
  49. std::cout << " -r: Compute the model cloud resolution and multiply" << std::endl;
  50. std::cout << " each radius given by that value." << std::endl;
  51. std::cout << " --algorithm (Hough|GC): Clustering algorithm used (default Hough)." << std::endl;
  52. std::cout << " --model_ss val: Model uniform sampling radius (default 0.01)" << std::endl;
  53. std::cout << " --scene_ss val: Scene uniform sampling radius (default 0.03)" << std::endl;
  54. std::cout << " --rf_rad val: Reference frame radius (default 0.015)" << std::endl;
  55. std::cout << " --descr_rad val: Descriptor radius (default 0.02)" << std::endl;
  56. std::cout << " --cg_size val: Cluster size (default 0.01)" << std::endl;
  57. std::cout << " --cg_thresh val: Clustering threshold (default 5)" << std::endl << std::endl;
  58. }
  59. void
  60. parseCommandLine(int argc, char* argv[])
  61. {
  62. //Show help
  63. if (pcl::console::find_switch(argc, argv, "-h"))
  64. {
  65. showHelp(argv[0]);
  66. exit(0);
  67. }
  68. //Model & scene filenames
  69. std::vector<int> filenames;
  70. filenames = pcl::console::parse_file_extension_argument(argc, argv, ".pcd");
  71. if (filenames.size() != 2)
  72. {
  73. std::cout << "Filenames missing.\n";
  74. showHelp(argv[0]);
  75. exit(-1);
  76. }
  77. model_filename_ = argv[filenames[0]];
  78. scene_filename_ = argv[filenames[1]];
  79. //Program behavior
  80. if (pcl::console::find_switch(argc, argv, "-k"))
  81. {
  82. show_keypoints_ = true;
  83. }
  84. if (pcl::console::find_switch(argc, argv, "-c"))
  85. {
  86. show_correspondences_ = true;
  87. }
  88. if (pcl::console::find_switch(argc, argv, "-r"))
  89. {
  90. // 所有参数将于点云分辨率相乘,得到最终使用的参数。
  91. use_cloud_resolution_ = true;
  92. }
  93. std::string used_algorithm;
  94. if (pcl::console::parse_argument(argc, argv, "--algorithm", used_algorithm) != -1)
  95. {
  96. if (used_algorithm.compare("Hough") == 0)
  97. {
  98. use_hough_ = true;
  99. }
  100. else if (used_algorithm.compare("GC") == 0)
  101. {
  102. use_hough_ = false;
  103. }
  104. else
  105. {
  106. std::cout << "Wrong algorithm name.\n";
  107. showHelp(argv[0]);
  108. exit(-1);
  109. }
  110. }
  111. //General parameters
  112. pcl::console::parse_argument(argc, argv, "--model_ss", model_ss_);
  113. pcl::console::parse_argument(argc, argv, "--scene_ss", scene_ss_);
  114. pcl::console::parse_argument(argc, argv, "--rf_rad", rf_rad_);
  115. pcl::console::parse_argument(argc, argv, "--descr_rad", descr_rad_);
  116. pcl::console::parse_argument(argc, argv, "--cg_size", cg_size_);
  117. pcl::console::parse_argument(argc, argv, "--cg_thresh", cg_thresh_);
  118. }
  119. // 计算点云的空间分辨率:算出输入点云的每个点与其临近点距离的平均值。
  120. double
  121. computeCloudResolution(const pcl::PointCloud<PointType>::ConstPtr& cloud)
  122. {
  123. double res = 0.0;
  124. int n_points = 0;
  125. int nres;
  126. std::vector<int> indices(2);
  127. std::vector<float> sqr_distances(2);
  128. pcl::search::KdTree<PointType> tree;
  129. tree.setInputCloud(cloud);
  130. for (size_t i = 0; i < cloud->size(); ++i)
  131. {
  132. if (!pcl_isfinite((*cloud)[i].x))
  133. {
  134. continue;
  135. }
  136. //Considering the second neighbor since the first is the point itself.
  137. nres = tree.nearestKSearch(i, 2, indices, sqr_distances);
  138. if (nres == 2)
  139. {
  140. res += sqrt(sqr_distances[1]);
  141. ++n_points;
  142. }
  143. }
  144. if (n_points != 0)
  145. {
  146. res /= n_points;
  147. }
  148. return res;
  149. }
  150. int
  151. main(int argc, char* argv[])
  152. {
  153. parseCommandLine(argc, argv);
  154. pcl::PointCloud<PointType>::Ptr model(new pcl::PointCloud<PointType>());
  155. pcl::PointCloud<PointType>::Ptr model_keypoints(new pcl::PointCloud<PointType>());
  156. pcl::PointCloud<PointType>::Ptr scene(new pcl::PointCloud<PointType>());
  157. pcl::PointCloud<PointType>::Ptr scene_keypoints(new pcl::PointCloud<PointType>());
  158. pcl::PointCloud<NormalType>::Ptr model_normals(new pcl::PointCloud<NormalType>());
  159. pcl::PointCloud<NormalType>::Ptr scene_normals(new pcl::PointCloud<NormalType>());
  160. pcl::PointCloud<DescriptorType>::Ptr model_descriptors(new pcl::PointCloud<DescriptorType>());
  161. pcl::PointCloud<DescriptorType>::Ptr scene_descriptors(new pcl::PointCloud<DescriptorType>());
  162. //
  163. // Load clouds
  164. //
  165. if (pcl::io::loadPCDFile(model_filename_, *model) < 0)
  166. {
  167. std::cout << "Error loading model cloud." << std::endl;
  168. showHelp(argv[0]);
  169. return (-1);
  170. }
  171. if (pcl::io::loadPCDFile(scene_filename_, *scene) < 0)
  172. {
  173. std::cout << "Error loading scene cloud." << std::endl;
  174. showHelp(argv[0]);
  175. return (-1);
  176. }
  177. //
  178. // Set up resolution invariance
  179. //
  180. if (use_cloud_resolution_)
  181. {
  182. float resolution = static_cast<float> (computeCloudResolution(model));
  183. if (resolution != 0.0f)
  184. {
  185. model_ss_ *= resolution;
  186. scene_ss_ *= resolution;
  187. rf_rad_ *= resolution;
  188. descr_rad_ *= resolution;
  189. cg_size_ *= resolution;
  190. }
  191. std::cout << "Model resolution: " << resolution << std::endl;
  192. std::cout << "Model sampling size: " << model_ss_ << std::endl;
  193. std::cout << "Scene sampling size: " << scene_ss_ << std::endl;
  194. std::cout << "LRF support radius: " << rf_rad_ << std::endl;
  195. std::cout << "SHOT descriptor radius: " << descr_rad_ << std::endl;
  196. std::cout << "Clustering bin size: " << cg_size_ << std::endl << std::endl;
  197. }
  198. //
  199. // Compute Normals
  200. // 计算场景和模型的每一个点的法向量,
  201. pcl::NormalEstimationOMP<PointType, NormalType> norm_est;
  202. norm_est.setKSearch(10); // 计算法向量时,对于每一个点利用10个临近点,
  203. // 该临近点设置的数量是一个经验值,已被证实可以较好的应用于Kinect等获取的数据分辨率;但对于非常稠密的点云数据来说,可以用近似半径来控制 或设置其他参数,很大程度上取决于数据。
  204. norm_est.setInputCloud(model);
  205. norm_est.compute(*model_normals);
  206. norm_est.setInputCloud(scene);
  207. norm_est.compute(*scene_normals);
  208. //
  209. // Downsample Clouds to Extract keypoints
  210. // 为了获取较为稀疏的关键点,下采样,
  211. pcl::PointCloud<int> sampled_indices;
  212. pcl::UniformSampling<PointType> uniform_sampling;
  213. uniform_sampling.setInputCloud(model);
  214. uniform_sampling.setRadiusSearch(model_ss_); // 均匀采样的半径:默认值:0.03
  215. //uniform_sampling.compute(sampled_indices);
  216. //pcl::copyPointCloud(*model, sampled_indices.points, *model_keypoints);
  217. uniform_sampling.filter(*model_keypoints);
  218. std::cout << "Model total points: " << model->size() << "; Selected Keypoints: " << model_keypoints->size() << std::endl;
  219. uniform_sampling.setInputCloud(scene);
  220. uniform_sampling.setRadiusSearch(scene_ss_);
  221. //uniform_sampling.compute(sampled_indices);
  222. //pcl::copyPointCloud(*scene, sampled_indices.points, *scene_keypoints);
  223. uniform_sampling.filter(*scene_keypoints);
  224. std::cout << "Scene total points: " << scene->size() << "; Selected Keypoints: " << scene_keypoints->size() << std::endl;
  225. //
  226. // Compute Descriptor for keypoints
  227. // 为场景和模型的每个关键点建立特征描述子,计算每个模型和场景的关键点的3D描述子。SHOT描述子。
  228. pcl::SHOTEstimationOMP<PointType, NormalType, DescriptorType> descr_est;
  229. descr_est.setRadiusSearch(descr_rad_); // 描述子描述区域范围大小。
  230. descr_est.setInputCloud(model_keypoints);
  231. descr_est.setInputNormals(model_normals);
  232. descr_est.setSearchSurface(model);
  233. descr_est.compute(*model_descriptors);
  234. descr_est.setInputCloud(scene_keypoints);
  235. descr_est.setInputNormals(scene_normals);
  236. descr_est.setSearchSurface(scene);
  237. descr_est.compute(*scene_descriptors);
  238. // 利用KD树结构找到模型和场景的对应点。
  239. // Find Model-Scene Correspondences with KdTree
  240. //模型和场景描述子点云之间对应点对集合。
  241. pcl::CorrespondencesPtr model_scene_corrs(new pcl::Correspondences());
  242. // 构造模型描述子点云的KdTreeFLANN,
  243. pcl::KdTreeFLANN<DescriptorType> match_search;
  244. match_search.setInputCloud(model_descriptors);
  245. // For each scene keypoint descriptor, find nearest neighbor into the model keypoints descriptor cloud and add it to the correspondences vector.// 最近邻模型描述子 添加到 对应场景描述子 的 向量中。
  246. for (size_t i = 0; i < scene_descriptors->size(); ++i)
  247. {
  248. std::vector<int> neigh_indices(1);
  249. std::vector<float> neigh_sqr_dists(1);
  250. if (!pcl_isfinite(scene_descriptors->at(i).descriptor[0])) //skipping NaNs
  251. {
  252. continue;
  253. }
  254. int found_neighs = match_search.nearestKSearch(scene_descriptors->at(i), 1, neigh_indices, neigh_sqr_dists);
  255. if (found_neighs == 1 && neigh_sqr_dists[0] < 0.25f) // add match only if the squared descriptor distance is less than 0.25 (SHOT descriptor distances are between 0 and 1 by design)
  256. // 当描述子平均距离小于0.25,添加匹配点,SHOT描述子本身设计使其距离保持在01之间。
  257. {
  258. pcl::Correspondence corr(neigh_indices[0], static_cast<int> (i), neigh_sqr_dists[0]);
  259. model_scene_corrs->push_back(corr);
  260. }
  261. }
  262. std::cout << "Correspondences found: " << model_scene_corrs->size() << std::endl;
  263. //
  264. // Actual Clustering聚类。
  265. //
  266. std::vector<Eigen::Matrix4f, Eigen::aligned_allocator<Eigen::Matrix4f> > rototranslations;
  267. std::vector<pcl::Correspondences> clustered_corrs;
  268. // Using Hough3D Hough投票过程。
  269. if (use_hough_)
  270. {
  271. //
  272. // Compute (Keypoints) Reference Frames only for Hough
  273. // 利用Hough算法,需要计算关键点的局部参考坐标系。
  274. pcl::PointCloud<RFType>::Ptr model_rf(new pcl::PointCloud<RFType>());
  275. pcl::PointCloud<RFType>::Ptr scene_rf(new pcl::PointCloud<RFType>());
  276. // 局部参考坐标系LRF。
  277. pcl::BOARDLocalReferenceFrameEstimation<PointType, NormalType, RFType> rf_est;
  278. rf_est.setFindHoles(true);
  279. rf_est.setRadiusSearch(rf_rad_); // 估计局部参考坐标系时,当前点的领域搜索半径。
  280. rf_est.setInputCloud(model_keypoints);
  281. rf_est.setInputNormals(model_normals);
  282. rf_est.setSearchSurface(model);
  283. rf_est.compute(*model_rf);
  284. rf_est.setInputCloud(scene_keypoints);
  285. rf_est.setInputNormals(scene_normals);
  286. rf_est.setSearchSurface(scene);
  287. rf_est.compute(*scene_rf);
  288. // Clustering聚类。
  289. pcl::Hough3DGrouping<PointType, PointType, RFType, RFType> clusterer;
  290. clusterer.setHoughBinSize(cg_size_); // Hough空间的采样间隔
  291. clusterer.setHoughThreshold(cg_thresh_); // Hough空间确定是否有实例存在的最少票数阈值。
  292. clusterer.setUseInterpolation(true); // 是否对投票在Hough空间进行插值计算。
  293. clusterer.setUseDistanceWeight(false); // 在投票时是否将对应点之间的距离作为权重参与计算。
  294. clusterer.setInputCloud(model_keypoints);
  295. clusterer.setInputRf(model_rf); // 设置模型对应的LRF
  296. clusterer.setSceneCloud(scene_keypoints);
  297. clusterer.setSceneRf(scene_rf); // 设置场景对应的LRF
  298. clusterer.setModelSceneCorrespondences(model_scene_corrs); // 设置模型与场景的对应点对集合。
  299. //clusterer.cluster (clustered_corrs);
  300. clusterer.recognize(rototranslations, clustered_corrs); // 结果包含 变换矩阵 和对应点 聚类 结果。
  301. }
  302. else // Using GeometricConsistency // 使用几何一致性聚类
  303. {
  304. pcl::GeometricConsistencyGrouping<PointType, PointType> gc_clusterer; // 算法实例。
  305. gc_clusterer.setGCSize(cg_size_); // 检查几何一致性时的空间分辨率。
  306. gc_clusterer.setGCThreshold(cg_thresh_); // 设置最小的聚类数量。
  307. gc_clusterer.setInputCloud(model_keypoints); // 设置模型关键点。
  308. gc_clusterer.setSceneCloud(scene_keypoints); // 设置场景关键点。
  309. gc_clusterer.setModelSceneCorrespondences(model_scene_corrs);
  310. //gc_clusterer.cluster (clustered_corrs);
  311. gc_clusterer.recognize(rototranslations, clustered_corrs);// 结果包含 变换矩阵 和对应点 聚类 结果。
  312. }
  313. //
  314. // Output results
  315. // 识别算法返回一个Eigen::Matrix4f类型的矩阵向量,该矩阵向量代表场景中找到模型的每个实例的变换矩阵(旋转矩阵 + 平移向量);
  316. // 识别算法还返回对应的支持每个模型实例的对应点对聚类,以向量形式保存,该向量的每个元素依次都是对应点对的集合,这些集合代表与场景中具体实例相关联的对应点。
  317. std::cout << "Model instances found: " << rototranslations.size() << std::endl;
  318. for (size_t i = 0; i < rototranslations.size(); ++i)
  319. {
  320. std::cout << "\n Instance " << i + 1 << ":" << std::endl;
  321. std::cout << " Correspondences belonging to this instance: " << clustered_corrs[i].size() << std::endl;
  322. // Print the rotation matrix and translation vector
  323. Eigen::Matrix3f rotation = rototranslations[i].block<3, 3>(0, 0);
  324. Eigen::Vector3f translation = rototranslations[i].block<3, 1>(0, 3);
  325. printf("\n");
  326. printf(" | %6.3f %6.3f %6.3f | \n", rotation(0, 0), rotation(0, 1), rotation(0, 2));
  327. printf(" R = | %6.3f %6.3f %6.3f | \n", rotation(1, 0), rotation(1, 1), rotation(1, 2));
  328. printf(" | %6.3f %6.3f %6.3f | \n", rotation(2, 0), rotation(2, 1), rotation(2, 2));
  329. printf("\n");
  330. printf(" t = < %0.3f, %0.3f, %0.3f >\n", translation(0), translation(1), translation(2));
  331. }
  332. //
  333. // Visualization
  334. //
  335. pcl::visualization::PCLVisualizer viewer("点云库PCL学习教程第二版-基于对应点聚类的3D模型识别");
  336. viewer.addPointCloud(scene, "scene_cloud");
  337. viewer.setBackgroundColor(255, 255, 255);
  338. pcl::PointCloud<PointType>::Ptr off_scene_model(new pcl::PointCloud<PointType>());
  339. pcl::PointCloud<PointType>::Ptr off_scene_model_keypoints(new pcl::PointCloud<PointType>());
  340. if (show_correspondences_ || show_keypoints_)
  341. {
  342. // We are translating the model so that it doesn't end in the middle of the scene representation
  343. pcl::transformPointCloud(*model, *off_scene_model, Eigen::Vector3f(0, 0, 0), Eigen::Quaternionf(1, 0, 0, 0));
  344. pcl::transformPointCloud(*model_keypoints, *off_scene_model_keypoints, Eigen::Vector3f(0, 0, 0), Eigen::Quaternionf(1, 0, 0, 0));
  345. pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_color_handler(off_scene_model, 0, 255, 0);
  346. viewer.addPointCloud(off_scene_model, off_scene_model_color_handler, "off_scene_model");
  347. }
  348. if (show_keypoints_)
  349. {
  350. pcl::visualization::PointCloudColorHandlerCustom<PointType> scene_keypoints_color_handler(scene_keypoints, 0, 0, 255);
  351. viewer.addPointCloud(scene_keypoints, scene_keypoints_color_handler, "scene_keypoints");
  352. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "scene_keypoints");
  353. pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_keypoints_color_handler(off_scene_model_keypoints, 0, 0, 255);
  354. viewer.addPointCloud(off_scene_model_keypoints, off_scene_model_keypoints_color_handler, "off_scene_model_keypoints");
  355. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "off_scene_model_keypoints");
  356. }
  357. for (size_t i = 0; i < rototranslations.size(); ++i)
  358. {
  359. pcl::PointCloud<PointType>::Ptr rotated_model(new pcl::PointCloud<PointType>());
  360. pcl::transformPointCloud(*model, *rotated_model, rototranslations[i]);
  361. std::stringstream ss_cloud;
  362. ss_cloud << "instance" << i;
  363. pcl::visualization::PointCloudColorHandlerCustom<PointType> rotated_model_color_handler(rotated_model, 255, 0, 0);
  364. viewer.addPointCloud(rotated_model, rotated_model_color_handler, ss_cloud.str());
  365. if (show_correspondences_)
  366. {
  367. for (size_t j = 0; j < clustered_corrs[i].size(); ++j)
  368. {
  369. std::stringstream ss_line;
  370. ss_line << "correspondence_line" << i << "_" << j;
  371. PointType& model_point = off_scene_model_keypoints->at(clustered_corrs[i][j].index_query);
  372. PointType& scene_point = scene_keypoints->at(clustered_corrs[i][j].index_match);
  373. // We are drawing a line for each pair of clustered correspondences found between the model and the scene
  374. viewer.addLine<PointType, PointType>(model_point, scene_point, 0, 255, 0, ss_line.str());
  375. }
  376. }
  377. }
  378. while (!viewer.wasStopped())
  379. {
  380. viewer.spinOnce();
  381. }
  382. return (0);
  383. }

需要在 

添加参数如下: 

milk_pose_changed.pcd milk_cartoon_all_small_clorox.pcd -k -c

 bug记录。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/59523
推荐阅读
相关标签
  

闽ICP备14008679号