【PCL】教程narf_feature_extraction 如何从深度图像中提取 NARF 特征

如何从范围图像中提取 NARF 特征 

本教程演示如何从深度图像中在 NARF 关键点位置提取 NARF 描述符。该可执行文件使我们能够从磁盘加载点云(或创建它,如果没有提供),在其上提取兴趣点,然后在这些位置计算描述符。然后,它在图像和 3D 查看器中可视化这些位置。

68110133ff69bb48f889ebc0c245d6ea.png

45735638c416443d1e9b46744d0d9c5d.png

Found 10 key points.
Extracted 11 descriptors for 10 keypoints.

2936f069204eebc522b9dae2793fce3b.png

No *.pcd file given => Generating example point cloud.
Found 6 key points.
Extracted 6 descriptors for 6 keypoints.

这段代码写的是一个使用 PCL(Point Cloud Library,点云库)库的 C++ 程序,在3D 图像识别领域进行操作。其中包括:

  1. 参数设置:如角度解析度、参考框架等。

  2. 帮助功能:如果运行程序时添加-h作为参数,就会打印出程序使用方法,并返回0。

  3. 视窗姿态设置:setViewerPose函数用于设置3D视窗的观察角度和位置

  4. 主函数:

  • 命令行参数解析:对输入的命令行参数进行解析,如角度解析度、功能开关等。

  • 读取或创建点云数据:先检查输入的点云文件,如果不存在则会创建一个样例点云数据。

  • 利用点云创建 RangeImage(过程包括设定传感器方位及计算各点的深度信息)。

  • 在3D视窗中展示这个 RangeImage。

  • 提取 NARF 关键点

  • 在 RangeImage 中标记出这些关键点,并在3D视窗中显示这些关键点。

  • 对这些关键点执行 NARF 描述符提取操作

  • 主循环:持续运行,直到视窗关闭。

这个程序主要处理3D图像数据,特别是利用NARF算法来寻找并标记出图像中的关键点。完整包括了加载样例或现有数据,图形化显示,以及结果的提取。

/* \author Bastian Steder */  // 作者: Bastian Steder


// 引入所需要的头文件
#include <iostream>  // 提供输入输出流的功能


// PCL库相关的头文件
#include <pcl/range_image/range_image.h>  // 提供range image相关的功能
#include <pcl/io/pcd_io.h>  // 提供pcd文件的读写功能
#include <pcl/visualization/range_image_visualizer.h>  // 提供range image的可视化功能
#include <pcl/visualization/pcl_visualizer.h>  // 提供pcl的可视化功能
#include <pcl/features/range_image_border_extractor.h>  // 提供range image边界抽取的功能
#include <pcl/keypoints/narf_keypoint.h>  // 提供narf关键点检测的功能
#include <pcl/features/narf_descriptor.h>  // 提供narf描述符的功能
#include <pcl/console/parse.h>  // 提供命令行参数解析的功能
#include <pcl/common/file_io.h>  // 提供文件输入输出的功能:getFilenameWithoutExtension


typedef pcl::PointXYZ PointType;  // 定义PointType为pcl::PointXYZ类型


// 参数设定
float angular_resolution = 0.5f;  // 角度分辨率
float support_size = 0.2f;  // 描述符的支持尺度
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;  // 坐标系设定
bool setUnseenToMaxRange = false;  // 是否将看不见的点设为最大范围
bool rotation_invariant = true;  // 是否旋转不变


// 定义printUsage函数,不返回任何值,接收一个字符常量指针,输出帮助信息
void 
printUsage (const char* progName)
{
  //将出现在命令行中的程序的名字和若干参数/options的信息打印出来
  std::cout << "\n\nUsage: "<<progName<<" [options] <scene.pcd>\n\n"
            // 打印出各参数的含义和默认值
            << "Options:\n"
            << "-------------------------------------------\n"
            << "-r <float>   angular resolution in degrees (default "<<angular_resolution<<")\n"
            << "-c <int>     coordinate frame (default "<< (int)coordinate_frame<<")\n"
            << "-m           Treat all unseen points to max range\n"
            << "-s <float>   support size for the interest points (diameter of the used sphere - "
                                                                  "default "<<support_size<<")\n"
            << "-o <0/1>     switch rotational invariant version of the feature on/off"
            <<               " (default "<< (int)rotation_invariant<<")\n"
            << "-h           this help\n"
            << "\n\n";
}


// 定义setViewerPose函数,不返回任何值,接收一个可视化对象和一个存储相机位姿的对象,
// 用于设置可视化对象的相机位姿
void 
setViewerPose (pcl::visualization::PCLVisualizer& viewer, const Eigen::Affine3f& viewer_pose)
{
  // 计算相机在当前位姿下的位置
  Eigen::Vector3f pos_vector = viewer_pose * Eigen::Vector3f (0, 0, 0);
  // 计算相机的观看方向
  Eigen::Vector3f look_at_vector = viewer_pose.rotation () * Eigen::Vector3f (0, 0, 1) + pos_vector;
  // 计算相机的上方向
  Eigen::Vector3f up_vector = viewer_pose.rotation () * Eigen::Vector3f (0, -1, 0);
  // 调用viewer的setCameraPosition函数设置相机的位姿
  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]);
}


int 
main (int argc, char** argv)
{
  // 解析命令行参数
  // 检查是否有请求帮助信息的"-h"参数,如果有则打印使用方法并退出程序
  if (pcl::console::find_argument (argc, argv, "-h") >= 0)
  {
    printUsage (argv[0]);
    return 0;
  }
  
  // 检查是否有"-m"参数,用于将未看到的点设置为最大距离
  if (pcl::console::find_argument (argc, argv, "-m") >= 0)
  {
    setUnseenToMaxRange = true;
    std::cout << "Setting unseen values in range image to maximum range readings.\n";
  }
  
  // 解析是否启用旋转不变特性的"-o"参数
  if (pcl::console::parse (argc, argv, "-o", rotation_invariant) >= 0)
    std::cout << "Switching rotation invariant feature version "<< (rotation_invariant ? "on" : "off")<<".\n";
  
  // 解析坐标框架的"-c"参数
  int tmp_coordinate_frame;
  if (pcl::console::parse (argc, argv, "-c", tmp_coordinate_frame) >= 0)
  {
    coordinate_frame = pcl::RangeImage::CoordinateFrame (tmp_coordinate_frame);
    std::cout << "Using coordinate frame "<< (int)coordinate_frame<<".\n";
  }
  
  // 解析兴趣点支持区域大小的"-s"参数
  if (pcl::console::parse (argc, argv, "-s", support_size) >= 0)
    std::cout << "Setting support size to "<<support_size<<".\n";
    
  // 解析角分辨率的"-r"参数,并从度转换为弧度
  if (pcl::console::parse (argc, argv, "-r", angular_resolution) >= 0)
  {
    std::cout << "Setting angular resolution to "<<angular_resolution<<"deg.\n";
    angular_resolution = pcl::deg2rad (angular_resolution);
  }
  
  // -----读取pcd文件或者如果没有提供就创建示例点云-----
  // ------------------------------------------------------------------
  pcl::PointCloud<PointType>::Ptr point_cloud_ptr (new pcl::PointCloud<PointType>); // 创建一个新的点云指针
  pcl::PointCloud<PointType>& point_cloud = *point_cloud_ptr; // 创建一个点云的引用,简化之后的代码操作
  pcl::PointCloud<pcl::PointWithViewpoint> far_ranges; // 创建一个视点信息的点云,用于处理远距离的点
  Eigen::Affine3f scene_sensor_pose (Eigen::Affine3f::Identity ()); // 定义传感器的姿态,初始为单位矩阵
  std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument (argc, argv, "pcd"); // 解析命令行参数中的pcd文件名
  if (!pcd_filename_indices.empty ()) // 如果命令行中提供了pcd文件
  {
    std::string filename = argv[pcd_filename_indices[0]]; // 获取文件名
    if (pcl::io::loadPCDFile (filename, point_cloud) == -1) // 尝试加载这个pcd文件,如果失败则返回错误信息
    {
      std::cerr << "Was not able to open file \""<<filename<<"\".\n"; // 输出错误信息
      printUsage (argv[0]); // 打印如何使用这个程序的信息
      return 0; // 退出程序
    }
    // 使用点云中的传感器原点和方向设置传感器的姿态
    scene_sensor_pose = Eigen::Affine3f (Eigen::Translation3f (point_cloud.sensor_origin_[0],
                                                               point_cloud.sensor_origin_[1],
                                                               point_cloud.sensor_origin_[2])) *
                        Eigen::Affine3f (point_cloud.sensor_orientation_);
    // 尝试加载对应的远距离信息的pcd文件,如果失败则输出不存在的信息
    std::string far_ranges_filename = pcl::getFilenameWithoutExtension (filename)+"_far_ranges.pcd";
    if (pcl::io::loadPCDFile (far_ranges_filename.c_str (), far_ranges) == -1)
      std::cout << "Far ranges file \""<<far_ranges_filename<<"\" does not exists.\n"; // 文件不存在的提示
  }
  else // 没有提供pcd文件
  {
    setUnseenToMaxRange = true; // 将无法看到的点设置为最大范围值
    std::cout << "\nNo *.pcd file given => Generating example point cloud.\n\n"; // 提示正在生成示例点云
    // 生成一个简单的栅格形状的点云,用于之后的处理
    for (float x=-0.5f; x<=0.5f; x+=0.01f)
    {
      for (float y=-0.5f; y<=0.5f; y+=0.01f)
      {
        PointType point;  
        point.x = x;  
        point.y = y;  
        point.z = 2.0f - y;
        point_cloud.push_back (point); // 将点加入到点云中
      }
    }
    point_cloud.width = point_cloud.size ();  // 设置点云的宽度为点的数量
    point_cloud.height = 1; // 高度设置为1,表示点云是非有序的
  }
  
  // 从点云创建RangeImage
  // -----------------------------------------------
  float noise_level = 0.0; // 噪声级别设置为0
  float min_range = 0.0f; // 最小范围设置为0
  int border_size = 1; // 边框大小设置为1
  pcl::RangeImage::Ptr range_image_ptr (new pcl::RangeImage); // 创建一个新的RangeImage指针
  pcl::RangeImage& range_image = *range_image_ptr; // 通过引用方式使用它,方便操作   
  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); // 使用点云数据填充RangeImage
  range_image.integrateFarRanges (far_ranges); // 集成远端范围信息
  if (setUnseenToMaxRange) // 如果设置了将未看到的值设为最大范围
    range_image.setUnseenToMaxRange (); // 则对RangeImage应用该设置
  
  // 打开3D视图并添加点云
  // --------------------------------------------
  pcl::visualization::PCLVisualizer viewer ("3D Viewer"); // 创建3D视图
  viewer.setBackgroundColor (1, 1, 1); // 设置背景颜色为白色
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler (range_image_ptr, 0, 0, 0); // 设置范围图像的颜色处理器
  viewer.addPointCloud (range_image_ptr, range_image_color_handler, "range image"); // 添加范围图像到视图中
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image"); // 设置点云渲染属性
  viewer.initCameraParameters (); // 初始化相机参数
  setViewerPose (viewer, range_image.getTransformationToWorldSystem ()); // 设置视图的姿态
  
  // 显示range image
  // --------------------------
  pcl::visualization::RangeImageVisualizer range_image_widget ("Range image"); // 创建range image的视图
  range_image_widget.showRangeImage (range_image); // 显示range image
  
  // 提取NARF关键点
  // --------------------------------
  pcl::RangeImageBorderExtractor range_image_border_extractor; // 创建RangeImage边界提取器
  pcl::NarfKeypoint narf_keypoint_detector; // 创建NARF关键点检测器
  narf_keypoint_detector.setRangeImageBorderExtractor (&range_image_border_extractor); // 设置范围图像边界提取器
  narf_keypoint_detector.setRangeImage (&range_image); // 设置范围图像
  narf_keypoint_detector.getParameters ().support_size = support_size; // 设置支持区域大小
  
  pcl::PointCloud<int> keypoint_indices; // 创建保存关键点索引的点云
  narf_keypoint_detector.compute (keypoint_indices); // 计算关键点
  std::cout << "Found "<<keypoint_indices.size ()<<" key points.\n"; // 输出找到的关键点数量
  
  // 在3D视图中展示关键点
  // -------------------------------------
  pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints_ptr (new pcl::PointCloud<pcl::PointXYZ>); // 创建一个新的XYZ点云指针,用于存放关键点
  pcl::PointCloud<pcl::PointXYZ>& keypoints = *keypoints_ptr; // 通过引用操作点云
  keypoints.resize (keypoint_indices.size ()); // 调整关键点点云的大小
  for (std::size_t i=0; i<keypoint_indices.size (); ++i) // 遍历所有关键点索引
    keypoints[i].getVector3fMap () = range_image[keypoint_indices[i]].getVector3fMap (); // 转换关键点索引到3D坐标
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> keypoints_color_handler (keypoints_ptr, 0, 255, 0); // 设置关键点的颜色处理器
  viewer.addPointCloud<pcl::PointXYZ> (keypoints_ptr, keypoints_color_handler, "keypoints"); // 添加关键点到视图
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "keypoints"); // 设置关键点的渲染属性
  
  // 提取NARF描述子
  // ------------------------------------------------------
  std::vector<int> keypoint_indices2; // 创建保存关键点索引的向量
  keypoint_indices2.resize (keypoint_indices.size ()); // 调整大小
  for (unsigned int i=0; i<keypoint_indices.size (); ++i) // 转换关键点索引
    keypoint_indices2[i]=keypoint_indices[i];
  pcl::NarfDescriptor narf_descriptor (&range_image, &keypoint_indices2); // 创建NARF描述子提取器
  narf_descriptor.getParameters ().support_size = support_size; // 设置描述子的支持区域大小
  narf_descriptor.getParameters ().rotation_invariant = rotation_invariant; // 设置是否使用旋转不变性
  pcl::PointCloud<pcl::Narf36> narf_descriptors; // 创建NARF描述子的点云
  narf_descriptor.compute (narf_descriptors); // 计算描述子
  std::cout << "Extracted "<<narf_descriptors.size ()<<" descriptors for "
                      <<keypoint_indices.size ()<< " keypoints.\n"; // 输出描述子数量
  // 主循环,直到视图关闭
  while (!viewer.wasStopped ())
  {
    range_image_widget.spinOnce ();  // 处理GUI事件
    viewer.spinOnce ();
    //pcl_sleep(0.01);
    Sleep(10);  // 暂停一段时间,防止CPU使用率过高
  }
}
(pcl::console::parse(argc, argv, "-o", rotation_invariant) >= 0)

64936e4a69bb306e8ddf638dedab80e2.png

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);

1d0cf623cd59f7368f7b7bd613ac26fe.png

range_image.integrateFarRanges(far_ranges);

5dd88f0ab2fe112411636f1bc5aac093.png

setViewerPose(viewer, range_image.getTransformationToWorldSystem());

227383a1723bc3feacd191a616727d7e.png

keypoints[i].getVector3fMap() =
        range_image[keypoint_indices[i]].getVector3fMap();

9bb3bd8e35bf32f3b63cbebc59c8eca1.png

NARF关键点检测过程

ab0f6409fdc5a648659b090d0ea32047.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/572304.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

spring @value @configurationProperties比较

今天项目中需要使用数组的方式 来加载一批 配置 yml: xxxx: - xxxxx - xsssss javaBean Value("${xxxxx.xxxxx}") private List<String> xxxs; 启动时候报错&#xff0c;无法加载&#xff0c;TM试验了1个小时&#xff0c;我一开始想到是格式的问题&#x…

Android 10.0 Launcher3替换桌面app图标后大小和其他app图标不一样的问题解决方案

1.前言 在10.0的系统ROM产品定制化开发中,在关于launcher3的产品定制化开发中,在有些时候需要对一些第三方的app图标做 替换或者是做一些动态图标的替换,发现在替换以后图标大小和其他app的图标大小不一样,所以就需要看是具体哪里 对app的图标做了缩放功能,接下来就需要去…

【注解和反射】类加载器

继上一篇博客【注解和反射】什么时候类会和不会被初始化&#xff1f;-CSDN博客 目录 六、类加载器 测试&#xff1a;获得类加载器 &#xff08;1&#xff09;如何获取Java中的类加载器及其父类加载器 &#xff08;2&#xff09;测试当前类是哪个类加载器 &#xff08;3&am…

【C++】STL-vector模拟实现

目录 1、vactor的模拟实现 1.1 成员变量 1.2 size、capacity 1.3 迭代器 1.4 构造、析构、拷贝构造、operator 1.5 push_back、pop_back、reserve 1.6 operator[] 1.7 insert、erase 1.8 resize 2、使用memcpy拷贝问题 1、vactor的模拟实现 1.1 成员变量 vector是顺…

时尚新选择,小塔RFID技术重塑样衣管理

在时尚领域&#xff0c;样衣是创意与工艺的完美结合&#xff0c;每一件都承载着设计师的心血与期待。然而&#xff0c;当这些珍贵的样版在传统的管理体系下流转时&#xff0c;样版管理成为一个令人头疼的问题。手动记录、盘点和样板追溯成为常态&#xff0c;但这种方式容易出错…

机器学习(二)之监督学习

前言&#xff1a; 上一节大概讲解了几种学习方式&#xff0c;下面几张就具体来讲讲监督学习的几种算法。 以下示例中和都是权重的意思&#xff01;&#xff01;&#xff01; 注&#xff1a;本文如有错误之处&#xff0c;还请读者指出&#xff0c;欢迎评论区探讨&#xff01; 1…

17. map和set的模拟实现(也就是用红黑树封装map和set)

1.map和set底层调用的红黑树的实现 有不清楚的地方&#xff0c;参考AVL树的模拟实现和红黑树的模拟实现 红黑树迭代器的实现 // 红黑树迭代器的类模板 template<class T, class Ref, class Ptr> struct __RBTreeIterator {// 将红黑树节点的类类型定义为Nodetypedef R…

绽放新笑容:儿童换牙期的关怀与注意

引言&#xff1a; 儿童的换牙期是成长过程中的重要阶段&#xff0c;标志着他们逐渐迈向成人世界。然而&#xff0c;伴随着牙齿的脱落和新牙的生长&#xff0c;孩子们可能会经历一些不适和困扰。本文将探讨儿童换牙期的注意事项&#xff0c;以帮助家长和孩子们度过这一特殊时期&…

扎根理论分析原理、方法与Nvivo技术应用

扎根理论越来越流行&#xff0c;成为经常被采用的研究方法之一。扎根理论的研究者来自广泛的研究领域&#xff0c;例如社会工作、护理、医药、综合医疗保健、教育、管理和商业。这些从业者和学者试图从他们所在学科范围内解释行为模式。对于扎根理论本质和实践的研究引发了知名…

这个表格为什么在VS Code里面预览可以显示,在浏览器预览就没有显示

在VS Code里面预览可以显示如图&#xff1a; 在浏览器预览就不能显示了&#xff0c;刚开始还好的后来不知道弄错了哪里了&#xff0c;哭死 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"vi…

Swagger:在线接口文档

Swagger介绍及使用 官网:https://swagger.io/ 介绍 使用Swagger你只需要按照它的规范去定义接口及接口相关的信息&#xff0c;就可以做到生成接口文档&#xff0c;以及在线接口调试页面。 Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。 使用方式 1.导入 kni…

qt5-入门-QListWidget-通过右键快捷菜单复制item内容

参考&#xff1a; C GUI Programming with Qt 4, Second Edition 本地环境&#xff1a; win10专业版&#xff0c;64位&#xff0c;Qt5.12 效果 在某个item上右键&#xff0c;点击copy后&#xff0c;item的内容已复制到剪贴板。 实现 #include <QMenu> #include <…

如何用微信发布考试成绩(如月考、期中、期末等)

自教育部《未成年人学校保护规定》颁布后,教育部明确表示:学校不得公开学生的考试成绩、排名等信息!同时学校应采取措施,便利家长知道学生的成绩等学业信息,对于教师来说,如何用微信发布考试成绩(如:月考、期中、期末等)就成了一道难题... 公开吧,会伤害到学生自尊心,甚至被投诉…

创建钉钉审批流实例

1、依赖 <!--钉钉 api --> <dependency><groupId>com.aliyun</groupId><artifactId>dingtalk</artifactId><version>2.0.14</version> </dependency> <!--钉钉 事件订阅--> <dependency><groupId>co…

CUDA编程技术概述

CUDA&#xff08;Compute Unified Device Architecture&#xff0c;统一计算设备架构&#xff09;是由英伟达&#xff08;NVIDIA&#xff09;公司推出的一种软硬件集成技术&#xff0c;是该公司对于GPGPU&#xff08;通用图形处理器计算&#xff09;的正式名称。透过这个技术&a…

Levenberg-Marquardt (LM) 算法进行非线性拟合

目录 1. LM算法2. 调包实现3. LM算法实现4. 源码地址 1. LM算法 LM算法是一种非线性最小二乘优化算法&#xff0c;用于求解非线性最小化问题。LM主要用于解决具有误差函数的非线性最小二乘问题&#xff0c;其中误差函数是参数的非线性函数&#xff0c;需要通过调整参数使误差函…

eNSP学习——静态路由及默认路由基本配置

目录 知识背景 实验目的 实验步骤 实验内容 实验拓扑 实验编址 实验前期准备 实验步骤 1、基本配置&#xff08;按照实验编址设置好对应的IP地址&#xff09; 2、是实现主机之间的通信 3、实现全网全通来增强网络的可靠性 4、使用默认路由实现简单的网络优化 需要各…

HTB靶场 Perfection

端口 打开了ssh和http服务 访问 Perfection靶机的网站 是一个根据权重计算总成绩的网站 Wappalyzer查看网页用的什么编写搭建的 抓包看一下是怎么工作的 发送,&#xff0c;返回的结果 如果我在 类别 后面多加一句命令 就会出现提示 恶意输入阻止 大概率有命令注入 通过插件…

解决宏定义后面无法加分号

总结&#xff1a;注意是针对单行if语句使用&#xff0c;并且宏定义后面必须带分号&#xff08;格式统一&#xff09; 参考连接 C语言种do_while(0)的妙用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1vk4y1R7VJ/?spm_id_from333.337.search-card.all.click&vd_…

2万8金句美句格言签名句子ACCESS\EXCEL数据库

优美句子类的数据已经有《33万多优美句子经典句子ACCESS数据库》、《近2万签名的句子网络签名ACCESS数据库》、《24万QQ伤感签名微信签名ACCESS数据库》、《2万多条QQ签名论坛签名大全ACCESS数据库》&#xff0c;今天又遇到一个&#xff0c;感觉也很不错&#xff0c;发上来看看…