关键点也称为兴趣点,它是2D图像或是3D点云或者曲面模型上,可以通过定义检测标准来获取的具有稳定性,区别性的点集,从技术上来说,关键点的数量相比于原始点云或图像的数据量减小很多,与局部特征描述子结合在一起,组成关键点描述子常用来形成原始数据的表示,而且不失代表性和描述性,从而加快了后续的识别,追踪等对数据的处理了速度,故而,关键点技术成为在2D和3D 信息处理中非常关键的技术

NARF(Normal Aligned Radial Feature)关键点是为了从深度图像中识别物体而提出的,对NARF关键点的提取过程有以下要求:

a) 提取的过程考虑边缘以及物体表面变化信息在内;

b)在不同视角关键点可以被重复探测;

c)关键点所在位置有足够的支持区域,可以计算描述子和进行唯一的估计法向量。

其对应的探测步骤如下:

(1) 遍历每个深度图像点,通过寻找在近邻区域有深度变化的位置进行边缘检测。

(2) 遍历每个深度图像点,根据近邻区域的表面变化决定一测度表面变化的系数,及变化的主方向。

(3) 根据step(2)找到的主方向计算兴趣点,表征该方向和其他方向的不同,以及该处表面的变化情况,即该点有多稳定。

(4) 对兴趣值进行平滑滤波。

(5) 进行无最大值压缩找到的最终关键点,即为NARF关键点。

关于NARF的更为具体的描述请查看这篇博客www.cnblogs.com/ironstark/p/5051533.html。

PCL中keypoints模块及类的介绍

(1)class pcl::Keypoint<PointInT,PointOutT>  类keypoint是所有关键点检测相关类的基类,定义基本接口,具体实现由子类来完成,其继承关系时下图:

具体介绍:

Public Member Functions

virtual void  setSearchSurface (const PointCloudInConstPtr &cloud)
  设置搜索时所用搜索点云,cloud为指向点云对象的指针引用
void  setSearchMethod (const KdTreePtr &tree)  设置内部算法实现时所用的搜索对象,tree为指向kdtree或者octree对应的指针
void  setKSearch (int k)   设置K近邻搜索时所用的K参数
void  setRadiusSearch (double radius)   设置半径搜索的半径的参数
int  searchForNeighbors (int index, double parameter, std::vector< int > &indices, std::vector< float > &distances) const
 

采用setSearchMethod设置搜索对象,以及setSearchSurface设置搜索点云,进行近邻搜索,返回近邻在点云中的索引向量,

indices以及对应的距离向量distance其中为查询点的索引,parameter为搜索时所用的参数半径或者K

(2)class  pcl::HarrisKeypoint2D<PointInT,PointOutT,IntensityT>

类HarrisKeypoint2D实现基于点云的强度字段的harris关键点检测子,其中包括多种不同的harris关键点检测算法的变种,其关键函数的说明如下:

Public Member Functions

  HarrisKeypoint2D (ResponseMethod method=HARRIS, int window_width=3, int window_height=3, int min_distance=5, float threshold=0.0)
  重构函数,method需要设置采样哪种关键点检测方法,有HARRIS,NOBLE,LOWE,WOMASI四种方法,默认为HARRIS,window_width  window_height为检测窗口的宽度和高度min_distance 为两个关键点之间 容许的最小距离,threshold为判断是否为关键点的感兴趣程度的阀值,小于该阀值的点忽略,大于则认为是关键点
 
void  setMethod (ResponseMethod type)设置检测方式
void  setWindowWidth (int window_width)  设置检测窗口的宽度
void  setWindowHeight (int window_height)  设置检测窗口的高度
void  setSkippedPixels (int skipped_pixels)  设置在检测时每次跳过的像素的数目
void  setMinimalDistance (int min_distance)   设置候选关键点之间的最小距离
void  setThreshold (float threshold)  设置感兴趣的阀值
void  setNonMaxSupression (bool=false)  设置是否对小于感兴趣阀值的点进行剔除,如果是true则剔除,否则返回这个点
void  setRefine (bool do_refine)设置是否对所得的关键点结果进行优化,
void  setNumberOfThreads (unsigned int nr_threads=0)  设置该算法如果采用openMP并行机制,能够创建线程数目

(3)pcl::HarrisKeypoint3D< PointInT, PointOutT, NormalT >

类HarrisKeypoint3D和HarrisKeypoint2D类似,但是没有在点云的强度空间检测关键点,而是利用点云的3D空间的信息表面法线向量来进行关键点检测,关于HarrisKeypoint3D的类与HarrisKeypoint2D相似,除了

HarrisKeypoint3D (ResponseMethod method=HARRIS, float radius=0.01f, float threshold=0.0f)

重构函数,method需要设置采样哪种关键点检测方法,有HARRIS,NOBLE,LOWE,WOMASI四种方法,默认为HARRIS,radius为法线估计的搜索半径,threshold为判断是否为关键点的感兴趣程度的阀值,小于该阀值的点忽略,大于则认为是关键点。

(4)pcl::HarrisKeypoint6D< PointInT, PointOutT, NormalT >

类HarrisKeypoint6D和HarrisKeypoint2D类似,只是利用了欧式空间域XYZ或者强度域来候选关键点,或者前两者的交集,即同时满足XYZ域和强度域的关键点为候选关键点,

HarrisKeypoint6D (float radius=0.01, float threshold=0.0)  重构函数,此处并没有方法选择的参数,而是默认采用了Tomsai提出的方法实现关键点的检测,radius为法线估计的搜索半径,threshold为判断是否为关键点的感兴趣程度的阀值,小于该阀值的点忽略,大于则认为是关键点。

(5)pcl::SIFTKeypoint< PointInT, PointOutT >

类SIFTKeypoint是将二维图像中的SIFT算子调整后移植到3D空间的SIFT算子的实现,输入带有XYZ坐标值和强度的点云,输出为点云中的SIFT关键点,其关键函数的说明如下:

void  setScales (float min_scale, int nr_octaves, int nr_scales_per_octave)
  设置搜索时与尺度相关的参数,min_scale在点云体素尺度空间中标准偏差,点云对应的体素栅格中的最小尺寸
int nr_octaves是检测关键点时体素空间尺度的数目,nr_scales_per_octave为在每一个体素空间尺度下计算高斯空间的尺度所需要的参数
void  setMinimumContrast (float min_contrast)   设置候选关键点对应的对比度下限

(6)还有很多不再一一介绍

实例分析

实验实现提取NARF关键点,并且用图像和3D显示的方式进行可视化,可以直观的观察关键点的位置和数量 narf_feature_extraction.cpp:

  1. #include <iostream>
  2.  
  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/features/narf_descriptor.h>
  11. #include <pcl/console/parse.h>
  12.  
  13. typedef pcl::PointXYZ PointType;
  14.  
  15. // --------------------
  16. // -----Parameters-----
  17. // --------------------
  18. float angular_resolution = 0.5f; ////angular_resolution为模拟的深度传感器的角度分辨率,即深度图像中一个像素对应的角度大小
  19. float support_size = 0.2f; //点云大小的设置
  20. pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME; //设置坐标系
  21. bool setUnseenToMaxRange = false;
  22. bool rotation_invariant = true;
  23.  
  24. // --------------
  25. // -----Help-----
  26. // --------------
  27. void
  28. printUsage (const char* progName)
  29. {
  30. std::cout << "\n\nUsage: "<<progName<<" [options] <scene.pcd>\n\n"
  31. << "Options:\n"
  32. << "-------------------------------------------\n"
  33. << "-r <float> angular resolution in degrees (default "<<angular_resolution<<")\n"
  34. << "-c <int> coordinate frame (default "<< (int)coordinate_frame<<")\n"
  35. << "-m Treat all unseen points to max range\n"
  36. << "-s <float> support size for the interest points (diameter of the used sphere - "
  37. "default "<<support_size<<")\n"
  38. << "-o <0/1> switch rotational invariant version of the feature on/off"
  39. << " (default "<< (int)rotation_invariant<<")\n"
  40. << "-h this help\n"
  41. << "\n\n";
  42. }
  43.  
  44. void
  45. setViewerPose (pcl::visualization::PCLVisualizer& viewer, const Eigen::Affine3f& viewer_pose) //设置视口的位姿
  46. {
  47. Eigen::Vector3f pos_vector = viewer_pose * Eigen::Vector3f (, , ); //视口的原点pos_vector
  48. Eigen::Vector3f look_at_vector = viewer_pose.rotation () * Eigen::Vector3f (, , ) + pos_vector; //旋转+平移look_at_vector
  49. Eigen::Vector3f up_vector = viewer_pose.rotation () * Eigen::Vector3f (, -, ); //up_vector
  50. viewer.setCameraPosition (pos_vector[], pos_vector[], pos_vector[], //设置照相机的位姿
  51. look_at_vector[], look_at_vector[], look_at_vector[],
  52. up_vector[], up_vector[], up_vector[]);
  53. }
  54.  
  55. // --------------
  56. // -----Main-----
  57. // --------------
  58. int
  59. main (int argc, char** argv)
  60. {
  61. // --------------------------------------
  62. // -----Parse Command Line Arguments-----
  63. // --------------------------------------
  64. if (pcl::console::find_argument (argc, argv, "-h") >= )
  65. {
  66. printUsage (argv[]);
  67. return ;
  68. }
  69. if (pcl::console::find_argument (argc, argv, "-m") >= )
  70. {
  71. setUnseenToMaxRange = true;
  72. cout << "Setting unseen values in range image to maximum range readings.\n";
  73. }
  74. if (pcl::console::parse (argc, argv, "-o", rotation_invariant) >= )
  75. cout << "Switching rotation invariant feature version "<< (rotation_invariant ? "on" : "off")<<".\n";
  76. int tmp_coordinate_frame;
  77. if (pcl::console::parse (argc, argv, "-c", tmp_coordinate_frame) >= )
  78. {
  79. coordinate_frame = pcl::RangeImage::CoordinateFrame (tmp_coordinate_frame);
  80. cout << "Using coordinate frame "<< (int)coordinate_frame<<".\n";
  81. }
  82. if (pcl::console::parse (argc, argv, "-s", support_size) >= )
  83. cout << "Setting support size to "<<support_size<<".\n";
  84. if (pcl::console::parse (argc, argv, "-r", angular_resolution) >= )
  85. cout << "Setting angular resolution to "<<angular_resolution<<"deg.\n";
  86. angular_resolution = pcl::deg2rad (angular_resolution);
  87.  
  88. // ------------------------------------------------------------------
  89. // -----Read pcd file or create example point cloud if not given-----
  90. // ------------------------------------------------------------------
  91. pcl::PointCloud<PointType>::Ptr point_cloud_ptr (new pcl::PointCloud<PointType>);
  92. pcl::PointCloud<PointType>& point_cloud = *point_cloud_ptr;
  93. pcl::PointCloud<pcl::PointWithViewpoint> far_ranges;
  94. Eigen::Affine3f scene_sensor_pose (Eigen::Affine3f::Identity ());
  95. std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument (argc, argv, "pcd");
  96. if (!pcd_filename_indices.empty ())
  97. {
  98. std::string filename = argv[pcd_filename_indices[]];
  99. if (pcl::io::loadPCDFile (filename, point_cloud) == -)
  100. {
  101. cerr << "Was not able to open file \""<<filename<<"\".\n";
  102. printUsage (argv[]);
  103. return ;
  104. }
  105. scene_sensor_pose = Eigen::Affine3f (Eigen::Translation3f (point_cloud.sensor_origin_[], //场景传感器的位置
  106. point_cloud.sensor_origin_[],
  107. point_cloud.sensor_origin_[])) *
  108. Eigen::Affine3f (point_cloud.sensor_orientation_);
  109. std::string far_ranges_filename = pcl::getFilenameWithoutExtension (filename)+"_far_ranges.pcd";
  110. if (pcl::io::loadPCDFile (far_ranges_filename.c_str (), far_ranges) == -)
  111. std::cout << "Far ranges file \""<<far_ranges_filename<<"\" does not exists.\n";
  112. }
  113. else
  114. {
  115. setUnseenToMaxRange = true;
  116. cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n";
  117. for (float x=-0.5f; x<=0.5f; x+=0.01f)
  118. {
  119. for (float y=-0.5f; y<=0.5f; y+=0.01f)
  120. {
  121. PointType point; point.x = x; point.y = y; point.z = 2.0f - y;
  122. point_cloud.points.push_back (point);
  123. }
  124. }
  125. point_cloud.width = (int) point_cloud.points.size (); point_cloud.height = ;
  126. }
  127.  
  128. // -----------------------------------------------
  129. // -----Create RangeImage from the PointCloud-----
  130. // -----------------------------------------------
  131. float noise_level = 0.0;
  132. float min_range = 0.0f;
  133. int border_size = ;
  134. boost::shared_ptr<pcl::RangeImage> range_image_ptr (new pcl::RangeImage);
  135. pcl::RangeImage& range_image = *range_image_ptr;
  136. range_image.createFromPointCloud (point_cloud, angular_resolution, pcl::deg2rad (360.0f), pcl::deg2rad (180.0f),
  137. scene_sensor_pose, coordinate_frame, noise_level, min_range, border_size);
  138. range_image.integrateFarRanges (far_ranges);
  139. if (setUnseenToMaxRange)
  140. range_image.setUnseenToMaxRange ();
  141.  
  142. // --------------------------------------------
  143. // -----Open 3D viewer and add point cloud-----
  144. // --------------------------------------------
  145. pcl::visualization::PCLVisualizer viewer ("3D Viewer");
  146. viewer.setBackgroundColor (, , );
  147. pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler (range_image_ptr, , , );
  148. viewer.addPointCloud (range_image_ptr, range_image_color_handler, "range image");
  149. viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, , "range image");
  150. //viewer.addCoordinateSystem (1.0f, "global");
  151. //PointCloudColorHandlerCustom<PointType> point_cloud_color_handler (point_cloud_ptr, 150, 150, 150);
  152. //viewer.addPointCloud (point_cloud_ptr, point_cloud_color_handler, "original point cloud");
  153. viewer.initCameraParameters ();
  154. setViewerPose (viewer, range_image.getTransformationToWorldSystem ());
  155.  
  156. // --------------------------
  157. // -----Show range image-----
  158. // --------------------------
  159. pcl::visualization::RangeImageVisualizer range_image_widget ("Range image");
  160. range_image_widget.showRangeImage (range_image);
  161. /*********************************************************************************************************
  162. 创建RangeImageBorderExtractor对象,它是用来进行边缘提取的,因为NARF的第一步就是需要探测出深度图像的边缘,
  163.  
  164. *********************************************************************************************************/
  165. // --------------------------------
  166. // -----Extract NARF keypoints-----
  167. // --------------------------------
  168. pcl::RangeImageBorderExtractor range_image_border_extractor; //用来提取边缘
  169. pcl::NarfKeypoint narf_keypoint_detector; //用来检测关键点
  170. narf_keypoint_detector.setRangeImageBorderExtractor (&range_image_border_extractor); //
  171. narf_keypoint_detector.setRangeImage (&range_image);
  172. narf_keypoint_detector.getParameters ().support_size = support_size; //设置NARF的参数
  173.  
  174. pcl::PointCloud<int> keypoint_indices;
  175. narf_keypoint_detector.compute (keypoint_indices);
  176. std::cout << "Found "<<keypoint_indices.points.size ()<<" key points.\n";
  177.  
  178. // ----------------------------------------------
  179. // -----Show keypoints in range image widget-----
  180. // ----------------------------------------------
  181. //for (size_t i=0; i<keypoint_indices.points.size (); ++i)
  182. //range_image_widget.markPoint (keypoint_indices.points[i]%range_image.width,
  183. //keypoint_indices.points[i]/range_image.width);
  184.  
  185. // -------------------------------------
  186. // -----Show keypoints in 3D viewer-----
  187. // -------------------------------------
  188. pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_ptr (new pcl::PointCloud<pcl::PointXYZ>);
  189.  
  190. pcl::PointCloud<pcl::PointXYZ>& keypoints = *keypoints_ptr;
  191.  
  192. keypoints.points.resize (keypoint_indices.points.size ());
  193. for (size_t i=; i<keypoint_indices.points.size (); ++i)
  194.  
  195. keypoints.points[i].getVector3fMap () = range_image.points[keypoint_indices.points[i]].getVector3fMap ();
  196. pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> keypoints_color_handler (keypoints_ptr, , , );
  197. viewer.addPointCloud<pcl::PointXYZ> (keypoints_ptr, keypoints_color_handler, "keypoints");
  198. viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, , "keypoints");
  199.  
  200. // ------------------------------------------------------
  201. // -----Extract NARF descriptors for interest points-----
  202. // ------------------------------------------------------
  203. std::vector<int> keypoint_indices2;
  204. keypoint_indices2.resize (keypoint_indices.points.size ());
  205. for (unsigned int i=; i<keypoint_indices.size (); ++i) // This step is necessary to get the right vector type
  206. keypoint_indices2[i]=keypoint_indices.points[i];
  207. pcl::NarfDescriptor narf_descriptor (&range_image, &keypoint_indices2);
  208. narf_descriptor.getParameters ().support_size = support_size;
  209. narf_descriptor.getParameters ().rotation_invariant = rotation_invariant;
  210. pcl::PointCloud<pcl::Narf36> narf_descriptors;
  211. narf_descriptor.compute (narf_descriptors);
  212. cout << "Extracted "<<narf_descriptors.size ()<<" descriptors for "
  213. <<keypoint_indices.points.size ()<< " keypoints.\n";
  214.  
  215. //--------------------
  216. // -----Main loop-----
  217. //--------------------
  218. while (!viewer.wasStopped ())
  219. {
  220. range_image_widget.spinOnce (); // process GUI events
  221. viewer.spinOnce ();
  222. pcl_sleep(0.01);
  223. }
  224. }

运行结果:

未完待续**********************88888

有兴趣这可以扫描下面的二维码关注公众号与我交流,

PCL关键点(1)的更多相关文章

  1. PCL—关键点检测(iss&Trajkovic)低层次点云处理

    博客转载自:http://www.cnblogs.com/ironstark/p/5069311.html 关键点检测往往需要和特征提取联合在一起,关键点检测的一个重要性质就是旋转不变性,也就是说,物 ...

  2. PCL—关键点检测(rangeImage)低层次点云处理

    博客转载自:http://www.cnblogs.com/ironstark/p/5046479.html 关键点又称为感兴趣的点,是低层次视觉通往高层次视觉的捷径,抑或是高层次感知对低层次处理手段的 ...

  3. PCL—关键点检测(Harris)低层次点云处理

    博客转载自:http://www.cnblogs.com/ironstark/p/5064848.html 除去NARF这种和特征检测联系比较紧密的方法外,一般来说特征检测都会对曲率变化比较剧烈的点更 ...

  4. PCL—关键点检测(NARF)低层次点云处理

    博客转载自:http://www.cnblogs.com/ironstark/p/5051533.html 关键点检测本质上来说,并不是一个独立的部分,它往往和特征描述联系在一起,再将特征描述和识别. ...

  5. PCL—低层次视觉—关键点检测(rangeImage)

    关键点又称为感兴趣的点,是低层次视觉通往高层次视觉的捷径,抑或是高层次感知对低层次处理手段的妥协. ——三维视觉关键点检测 1.关键点,线,面 关键点=特征点: 关键线=边缘: 关键面=foregro ...

  6. PCL—低层次视觉—关键点检测(iss&Trajkovic)

    关键点检测往往需要和特征提取联合在一起,关键点检测的一个重要性质就是旋转不变性,也就是说,物体旋转后还能够检测出对应的关键点.不过说实话我觉的这个要求对机器人视觉来说是比较鸡肋的.因为机器人采集到的三 ...

  7. PCL—低层次视觉—关键点检测(NARF)

    关键点检测本质上来说,并不是一个独立的部分,它往往和特征描述联系在一起,再将特征描述和识别.寻物联系在一起.关键点检测可以说是通往高层次视觉的重要基础.但本章节仅在低层次视觉上讨论点云处理问题,故所有 ...

  8. PCL—低层次视觉—关键点检测(Harris)

    除去NARF这种和特征检测联系比较紧密的方法外,一般来说特征检测都会对曲率变化比较剧烈的点更敏感.Harris算法是图像检测识别算法中非常重要的一个算法,其对物体姿态变化鲁棒性好,对旋转不敏感,可以很 ...

  9. [CC]Plugin-提取ISS3D关键点

    基于CloudCompare开发的提取ISS3D关键点. void qLxPluginPCL::doISS3D() { assert(m_app); if (!m_app) return; const ...

随机推荐

  1. Windows 平台下Myeclipse 10 中SVN 插件使用教程(TortoiseSVN)

    1.  TortoiseSVN 简介 版本控制是管理信息修改的艺术,它一直是程序员最重要的工具,程序员经常会花时间作出小的修改,然后又在某一天取消了这些修改,想象一下一个开发者并行工作的团队 - 或许 ...

  2. chrome 版本 29.0.1547.76 m 解决打开新标签页后的恶心页面的问题

    个人非常不喜欢这个版本的新标签页的样子,特别是一再输入框中输入要搜索的东西,自动跑到标题栏中去了,比吃屎还恶心.下面是解决办法: 在地址栏输入:chrome://flags/ 按Ctrl+F,输入下面 ...

  3. django -- 为模式增加方法

    在django中模式中的方法是行级的.也就是说它操作是表里的行.不是整个表 一.模式定义: from django.db import models # Create your models here ...

  4. ssh 反向代理和正向代理的文章

    搜索原因是,我希望访问家里内网的机器 技术要点: ssh 反向代理,正向代理 隧道 文章摘自: https://my.oschina.net/leejun2005/blog/94401 https:/ ...

  5. Android 版本对于 API

    Android版本 API 代号 官网链接 Android 2.3.3 API 10 Gingerbread 官网 Android 3.0 API 11 Android 3.1 API 12 Andr ...

  6. Quartz 任务调度框架之Hello World

    0x01 什么是Quartz? Quartz是一个完全由java编写的开源作业调度框架. 0x02 实战Quartz Hello World 创建Maven项目,POM 文件配置如下: <pro ...

  7. 【Unity】2.10 利用VS2015编辑Unity的C#脚本

    分类:Unity.C#.VS2015 创建日期:2016-03-31 一.简介 用VS2015编辑C#脚本时,如果让其"显示所有文件",就会看到VS2015解决方案资源管理器中项目 ...

  8. 手机前端开发调试利器 – vConsole

    我们在开发手机版网页的时候,常常会出现下面的情景: (1) 开发时,在自己电脑上运行得好好的,在手机上打开就挂了,但是手机上又看不到error log: (2) 上线后,某用户表示页面失灵,但我们自己 ...

  9. rdesktop 源码安装

    # ./configure --prefix=/data/apps/rdesktop-1.6.0 #make && make install

  10. What-are-P-NP-NP-complete-and-NP-hard

    https://www.amazon.com/Computational-Complexity-Approach-Sanjeev-Arora/dp/0521424267 http://theory.c ...