【PCL】点云特征提取Harris、ISS、SIFT、Boundary

一、概要

点云特征提取是3D计算机视觉中的关键步骤,用于识别和描述点云中的显著特征。以下是几种常见的点云特征提取方法:

1.1 Harris

  • Harris角点检测:最初用于2D图像,通过计算像素灰度变化来检测角点。在3D点云中,Harris通过分析点云曲率或法向量变化来检测角点。
  • 应用:适用于需要高重复性和稳定性的场景,如物体识别和配准。

1.2 ISS (Intrinsic Shape Signatures)

  • ISS特征点检测:基于点云的局部几何特性,通过计算每个点的邻域协方差矩阵,选择具有显著几何变化的点作为特征点。
  • 应用:常用于3D物体识别和场景理解。

1.3 SIFT (Scale-Invariant Feature Transform)

  • SIFT特征提取:最初用于2D图像,通过检测尺度空间中的极值点并生成描述符。在3D点云中,SIFT通过分析点云的多尺度表示来提取特征。
  • 应用:适用于需要尺度不变性和旋转不变性的场景,如3D物体识别和配准。

1.4 Boundary

  • 边界特征提取:通过检测点云中的边界点来提取特征,通常基于点云的法向量或曲率变化。
  • 应用:适用于需要识别物体边界的场景,如场景分割和物体识别。

1.5 总结

  • Harris:基于曲率或法向量变化检测角点。
  • ISS:基于局部几何特性检测特征点。
  • SIFT:基于多尺度表示提取特征。
  • Boundary:基于法向量或曲率变化检测边界点。

2、代码和结果

2.1 PCL代码:

#include <pcl/point_types.h>
#include<pcl/common/common.h>
#include <pcl/io/ply_io.h>
#include<pcl/keypoints/harris_3d.h>
#include<pcl/keypoints/iss_3d.h>
#include<pcl/keypoints/sift_keypoint.h>
#include<pcl/features/pfh.h>
#include<pcl/features/fpfh.h>
#include<pcl/features/vfh.h>
#include<pcl/features/ppf.h>
#include <pcl/features/shot.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>  // Include the Boost.Thread header
#include <boost/date_time/posix_time/posix_time.hpp>  // Include for boost::posix_time
                                    
namespace pcl
{
  template<>
  struct SIFTKeypointFieldSelector<PointXYZ>
  {
    inline float
      operator () (const PointXYZ &p) const
{
      return p.z;
    }
  };
}


void computeNormal(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in, pcl::PointCloud<pcl::Normal>::Ptr cloud_normals) {

  pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> nores;
  pcl::search::KdTree< pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree< pcl::PointXYZ>());
  nores.setInputCloud(cloud_in);
  nores.setSearchMethod(tree);
  //nores.setKSearch(20);
  nores.setRadiusSearch(0.02);
  nores.compute(*cloud_normals);

}

void visualize(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr keypoint){
  
  pcl::visualization::PCLVisualizer viewer("3D Viewer");
  
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> cloud_h(cloud, 0, 0, 255);
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> keypoint_h(keypoint, 255, 0, 0);
  
  viewer.addPointCloud(cloud, cloud_h, "cloud");
  viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud");
  viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 0, 255, "cloud");

  viewer.addPointCloud(keypoint, keypoint_h, "keypoints");
  viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "keypoints");
  viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 255, 0, 0, "keypoints");
  /*viewer.addCoordinateSystem(1.0);*/
  while (!viewer.wasStopped())
  {
    viewer.spinOnce(100);
    boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  }
}

void harris3d(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints) {

  pcl::HarrisKeypoint3D<pcl::PointXYZ, pcl::PointXYZI, pcl::Normal> harris;
  pcl::PointCloud<pcl::PointXYZI>::Ptr keypoints_(new pcl::PointCloud<pcl::PointXYZI>());

  harris.setInputCloud(cloud);
  harris.setNonMaxSupression(true);
  harris.setRadius(0.005f);
  harris.setThreshold(0.005f);

  harris.compute(*keypoints_);

  pcl::PointXYZ temp;
  for (int i = 0; i < keypoints_->points.size(); i++) {
    temp.x = keypoints_->points[i].x;
    temp.y = keypoints_->points[i].y;
    temp.z = keypoints_->points[i].z;
    keypoints->points.push_back(temp);
  }



}

void iss3d(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints) {

  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
  pcl::ISSKeypoint3D<pcl::PointXYZ, pcl::PointXYZ> iss_detector;
  iss_detector.setSearchMethod(tree);
  iss_detector.setSalientRadius(0.005);
  iss_detector.setNonMaxRadius(0.005);
  iss_detector.setThreshold21(0.975);
  iss_detector.setThreshold32(0.975);
  iss_detector.setMinNeighbors(5);
  iss_detector.setNumberOfThreads(64);
  iss_detector.setInputCloud(cloud);
  iss_detector.compute(*keypoints);

}

void sift3d(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr keypoints) {

  const float min_scale = 0.0005f; 
  const int n_octaves = 6;
  const int n_scales_per_octave = 4;
  const float min_contrast = 0.0005f;
  pcl::SIFTKeypoint<pcl::PointXYZ, pcl::PointWithScale> sift;
  pcl::PointCloud<pcl::PointWithScale> result;
  pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());
  sift.setSearchMethod(tree);
  sift.setScales(min_scale, n_octaves, n_scales_per_octave);
  sift.setMinimumContrast(min_contrast);
  sift.setInputCloud(cloud);
  sift.compute(result);

  pcl::copyPointCloud(result, *keypoints);


}

void acBoundary(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr boundarys) {

  pcl::PointCloud<pcl::Normal>::Ptr normal(new pcl::PointCloud<pcl::Normal>);
  computeNormal(cloud, normal);
  pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>); //声明一个boundary类指针,作为返回值
  boundaries->resize(cloud->size()); //初始化大小
  pcl::BoundaryEstimation<pcl::PointXYZ, pcl::Normal, pcl::Boundary> boundary_estimation; //声明一个BoundaryEstimation类
  boundary_estimation.setInputCloud(cloud); //设置输入点云
  boundary_estimation.setInputNormals(normal); //设置输入法线
  pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree_ptr(new pcl::search::KdTree<pcl::PointXYZ>);
  boundary_estimation.setSearchMethod(kdtree_ptr); //设置搜寻k近邻的方式
  boundary_estimation.setKSearch(30); //设置k近邻数量
  boundary_estimation.setAngleThreshold(M_PI * 0.6); //设置角度阈值,大于阈值为边界
  boundary_estimation.compute(*boundaries); //计算点云边界,结果保存在boundaries中

  for (int i = 0; i < cloud->size(); i++)
  {
    if (boundaries->points[i].boundary_point != 0)
    {
      boundarys->points.emplace_back(cloud->points[i]);
    }
  }
}

int main() {

  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());

  pcl::PointCloud<pcl::PointXYZ>::Ptr keypoint(new pcl::PointCloud<pcl::PointXYZ>());

  pcl::io::loadPLYFile("../../../data/monkey.ply", *cloud);

  sift3d(cloud, keypoint);
  visualize(cloud, keypoint);

  iss3d(cloud, keypoint);
  visualize(cloud, keypoint);

  harris3d(cloud, keypoint);
  visualize(cloud, keypoint);

  acBoundary(cloud, keypoint);
  visualize(cloud, keypoint);

  return 0;

}

2.3 CMakeList.txt:

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(cloud_viewer)

find_package(PCL 1.14 REQUIRED)
find_package(Boost REQUIRED COMPONENTS thread)  # 查找 Boost 并指定需要链接的组件(thread)
# 添加头文件路径
include_directories(${PCL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable (demo demo .cpp)
target_link_libraries(demo ${PCL_LIBRARIES})
target_link_libraries(demo Boost::thread)  # 链接 Boost.Thread 库

2.3 代码运行结果

  • Harris:
    在这里插入图片描述

  • ISS
    在这里插入图片描述

  • SIFT
    在这里插入图片描述

  • Boundary:
    在这里插入图片描述

### 实现点云特征提取 为了利用 Python 和 PCL 库实现点云特征提取,可以采用 `pclpy` 这一接口库。此过程涉及多个方面的工作流设计,从环境配置到具体算法的应用。 #### 环境准备 对于 Windows 用户,在使用特定版本的 Python (如 Python 3.6) 的情况下,推荐通过 Anaconda 来管理依赖关系和创建虚拟环境[^2]。这有助于简化安装流程以及解决潜在的兼容性问题。 #### 数据预处理 一旦开发环境搭建完毕,则需加载待分析的点云数据集。通常这些数据会被保存成 `.pcd` 文件格式以便于后续操作: ```python import pclpy from pathlib import Path cloud = pclpy.io.load(str(Path('path_to_your_file/input_cloud.pcd'))) ``` #### 特征估计设置 针对不同类型的几何特性需求,PCL 提供了一系列工具用于计算诸如法线向量、曲率等属性。下面的例子展示了如何估算给定点云上每一点处的表面法线方向: ```python ne = pclpy.features.NormalEstimation() tree = pclpy.kdtree.KdTreeFLANN(cloud) ne.setInputCloud(cloud) ne.setSearchMethod(tree) ne.setKSearch(50) # 设置邻域查询数量 normals = ne.compute() # 计算得到的结果存储在一个新的PointCloud对象中 ``` 上述代码片段定义了一个基于 KNN(K-Nearest Neighbors) 方法来进行局部平面拟合的过程,并最终获得每个顶点对应的单位长度外法线矢量集合[^4]。 #### 边界检测实施 当涉及到更复杂的结构化信息获取时——比如识别物体边缘或轮廓线条的位置分布情况,可以通过调用专门为此目的而设的功能模块完成任务。例如,应用 Boundary Estimation 技术能够帮助区分哪些部分属于目标实体内部区域之外的部分: ```python boundary_estimation = pclpy.features.BoundaryEstimation() boundary_estimation.setInputCloud(cloud) boundary_estimation.setInputNormals(normals) boundary_estimation.setRadiusSearch(0.03) # 设定搜索半径大小 boundaries = boundary_estimation.compute() ``` 在此基础上进一步筛选出满足一定条件(如角度阈值)下的候选边界点作为输出结果的一部分。 综上所述,借助于强大的 PCL 工具箱及其便捷易用的 Python 封装形式—即 `pclpy` ,研究者们得以高效地开展各类三维空间数据分析工作;同时也能编写更加直观清晰且易于维护扩展的研究程序代码[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值