ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

PCL点云库学习笔记(二)——kd-tree和八叉树

2020-12-11 20:58:21  阅读:280  来源: 互联网

标签:kd 八叉树 tree points pcl 点云 include cloud


1.kd-tree、八叉树简介

1)kd-tree, 或称 k 维树,是计算机科学中使用的一种数据结构,用来组织表示 k 维空间 中的点集合。一般在点云处理中都是应用到三维的kd-tree。PCL 中 k-d tree 库提供了 k-d tree 数据结构,基于 FLANN 进行快速最近邻检索。最近邻检索在匹配、特征描述子计算、邻域特征提取中是非常基础的核心操作。 kd-tree 模块利用 两个类与两个函数实现了利用 k-d tree 数据结构对点云的高效管理和检索,其依赖于 pcl_ common 模块。
2)八叉树结构是 Hunter 博士于 1978 年首次提出的一种数据模型,如图 4.2 所示。八叉树)结构通&过循环递归的划分方法对大小为 2nx2nx2n 的三维空间的几何对象进行剖分,从而 构成一个具有根节点的方向图。
在这里插入图片描述

2.快速邻域搜索

利用kd-tree实现点云的k近邻搜索(当k为1是就是最近邻点)以及按照搜索点为中心的半径距离进行搜索。
1)k近邻搜索

#include <pcl/point_cloud.h>
#include <pcl/kdtree/kdtree_flann.h> //kd-tree依赖的头文件
#include <iostream>
#include <vector>
#include <ctime>

int main (int argc, char**argv)
{
srand (time (NULL));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
//生成点云
cloud->width =1000;
cloud->height =1;
cloud->points.resize (cloud->width * cloud->height);
for (size_t i=0; i< cloud->points.size (); ++i)
  {
cloud->points[i].x =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].y =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].z =1024.0f* rand () / (RAND_MAX +1.0f);
  }
 //建立kd-tree
pcl::KdTreeFLANN<pcl::PointXYZ>kdtree;
//输入点云
kdtree.setInputCloud (cloud);
//设定搜索的中心点
pcl::PointXYZ searchPoint;
searchPoint.x=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.y=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.z=1024.0f* rand () / (RAND_MAX +1.0f);
// 搜索k个与中心点的最近邻点
int K =10;
std::vector<int>pointIdxNKNSearch(K);//存储k个最近邻点的索引
std::vector<float>pointNKNSquaredDistance(K);//存储最近邻点的距离平方
//打印
std::cout<<"K nearest neighbor search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with K="<< K <<std::endl;
if ( kdtree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) >0 )//执行搜索,搜索到后返回值大于0
  {
for (size_t i=0; i<pointIdxNKNSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxNKNSearch[i] ].x 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].y 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].z 
<<" (squared distance: "<<pointNKNSquaredDistance[i] <<")"<<std::endl;
  }
 return 0;
}

2)按照搜索半径进行搜索

#include <pcl/point_cloud.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <iostream>
#include <vector>
#include <ctime>

int main (int argc, char**argv)
{
srand (time (NULL));
std::vector<int> pointIdxRadiusSearch;
std::vector<float> pointRadiusSquaredDistance;
float radius =256.0f* rand () / (RAND_MAX +1.0f);
std::cout<<"Neighbors within radius search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with radius="<< radius <<std::endl;
if ( kdtree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) >0 )//执行按半径搜索
  {
for (size_t i=0; i<pointIdxRadiusSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxRadiusSearch[i] ].x 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].y 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].z 
<<" (squared distance: "<<pointRadiusSquaredDistance[i] <<")"<<std::endl;
  }
return 0;
}

3.点云压缩

点云由庞大的数据集组成,这些数据集通过距离、颜色、法线等附加信息来描述空间三维点。此外,点云能以非常高的速率被创建出来,因此需要占用相当大的存储资源,一旦点云需要存储或者通过速率受限制的通信信道进行传输,提供针对这种数据的压缩方法就变得 十分有用。 PCL 库提供了点云压缩功能,它允许编码压缩所有类型的点云,包括“无序” 点云,它具有无参考点和变化的点尺寸、分辨率、分布密度和点顺序等结构特征。而且,底层的八叉树数据结构允许从几个输入源高效地合并点云数据。
参考

下面是我们可以使用的一些压缩文件:
- LOW_RES_ONLINE_COMPRESSION_WITHOUT_COLOR:分辨率1立方厘米,压缩完之后无颜色,快速在线编码
- LOW_RES_ONLINE_COMPRESSION_WITH_COLOR:分辨率1立方厘米,压缩完之后有颜色,快速在线编码
- MED_RES_ONLINE_COMPRESSION_WITHOUT_COLOR:分辨率5立方毫米,压缩完之后无颜色,快速在线编码
- MED_RES_ONLINE_COMPRESSION_WITH_COLOR:分辨率5立方毫米,压缩完之后有颜色,快速在线编码
- HIGH_RES_ONLINE_COMPRESSION_WITHOUT_COLOR:分辨率1立方毫米,压缩完之后无颜色,快速在线编码
- HIGH_RES_ONLINE_COMPRESSION_WITH_COLOR:分辨率1立方毫米,压缩完之后有颜色,快速在线编
- LOW_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR:分辨率1立方厘米,压缩完之后无颜色,高效离线编码
- LOW_RES_OFFLINE_COMPRESSION_WITH_COLOR:分辨率1立方厘米,压缩完之后有颜色,高效离线编码
- MED_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR:分辨率5立方毫米,压缩完之后无颜色,高效离线编码
- MED_RES_OFFLINE_COMPRESSION_WITH_COLOR:分辨率5立方毫米,压缩完之后有颜色,高效离线编码
- HIGH_RES_OFFLINE_COMPRESSION_WITHOUT_COLOR:分辨率1立方毫米,压缩完之后无颜色,高效离线编码
- HIGH_RES_OFFLINE_COMPRESSION_WITH_COLOR:分辨率1立方毫米,压缩完之后有颜色,高效离线编码
#include <pcl/compression/octree_pointcloud_compression.h>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/io/pcd_io.h>

#include <iostream>

int main()
{
    // 加载点云
    pcl::PointCloud<pcl::PointXYZRGB> sourceCloud;
    if (pcl::io::loadPCDFile("coloredBox.pcd", sourceCloud) == -1)
        return -1;

    // 是否查看压缩信息
    bool showStatistics = true;
    // 配置文件,如果想看配置文件的详细内容,可以参考: /io/include/pcl/compression/compression_profiles.h
    pcl::io::compression_Profiles_e compressionProfile = pcl::io::MED_RES_ONLINE_COMPRESSION_WITH_COLOR;

    // 初始化点云压缩器和解压器
    pcl::io::OctreePointCloudCompression<pcl::PointXYZRGB>* PointCloudEncoder;
    PointCloudEncoder = new pcl::io::OctreePointCloudCompression<pcl::PointXYZRGB>(compressionProfile, showStatistics);
    PointCloudEncoder = new pcl::io::OctreePointCloudCompression<pcl::PointXYZRGB>(compressionProfile,true,0.002);



    // 压缩结果stringstream
    std::stringstream compressedData;
    // 输出点云
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudOut(new pcl::PointCloud<pcl::PointXYZRGB>());
    // 压缩点云
    PointCloudEncoder->encodePointCloud(sourceCloud.makeShared(), compressedData);
    std::cout << compressedData.str() << std::endl;
    // 解压点云
    PointCloudEncoder->decodePointCloud(compressedData, cloudOut);
    pcl::visualization::CloudViewer viewer("Simple Cloud Viewer");
    viewer.showCloud(cloudOut);
    while (!viewer.wasStopped())
    {
    }

    std::system("pause");
    return 0;
}

完全自定义压缩参数
为了能完全控制压缩相关的参数, PointCloudCompression 类的构造函数可以在初始化时附加压缩参数。请注意,为了能够进行高级参数化, compressionProfile_arg 参数需要被设置成MANUAL_CONRGURATION。

OctreePointCloudCompression (compression_Profiles_e compressionProfile_arg = MED_RES_ONLINE_COMPRESSION_WITH_COLOR,
                               bool showStatistics_arg = false,
                               const double pointResolution_arg = 0.001,
                               const double octreeResolution_arg = 0.01,
                               bool doVoxelGridDownDownSampling_arg = false,
                               const unsigned int iFrameRate_arg = 30,
                               bool doColorEncoding_arg = true,
                               const unsigned char colorBitResolution_arg = 6) :

compressionProfile_arg:配置文件,若要启用高级参数化应设置为MANUAL_CONFIGURATION
showStatistics_arg:是否将压缩相关的统计信息打印到标准输出上
octreeResolution_arg:八叉树分辨率
pointResolution_arg:定义点坐标的编码精度,该参数应设为小于传感器精度的一个值
doVoxelGridDownDownSampling_arg:是否进行体素下采样(每个体素内只留下体素中心一个点)
iFrameRate_arg:点云压缩模式对点云进行差分编码压缩,用这种方法对新引入的点云和之前编码的点云之间的差分进行编码,以便获得最大压缩性能,iFrameRate_arg指定了一个速率,如果数据流中的帧速率低于这个速率则不进行差分编码压缩
doColorEncoding_arg:是否对彩色编码压缩
colorBitResolution_arg:定义每一个彩色成分编码后所占的位数

4.基于八叉树的点云搜索和空间划分

八叉树是一种用于管理稀疏 3D 数据的树状数据结构,每个内部节点都正好有八个子节点。
分别介绍基于八叉树的体素内搜索、k近邻搜索和半径内近邻搜索

#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>
#include <iostream>
#include <vector>
#include <ctime>

int
main (int argc, char**argv)
{
srand ((unsigned int) time (NULL));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 生成点云
cloud->width =1000;
cloud->height =1;
cloud->points.resize (cloud->width * cloud->height);
for (size_t i=0; i< cloud->points.size (); ++i)
  {
cloud->points[i].x =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].y =1024.0f* rand () / (RAND_MAX +1.0f);
cloud->points[i].z =1024.0f* rand () / (RAND_MAX +1.0f);
  }

  //构建八叉树
float resolution =128.0f;//设置八叉树分辨率,描述的是最低一级八叉树的最小体素尺寸
pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);
octree.setInputCloud (cloud);//设置输入点云
octree.addPointsFromInputCloud ();//载入点云
//随机生成搜索点
pcl::PointXYZ searchPoint;
searchPoint.x=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.y=1024.0f* rand () / (RAND_MAX +1.0f);
searchPoint.z=1024.0f* rand () / (RAND_MAX +1.0f);
// 体素最近邻搜索:搜索查询点所在的体素中的其他点,返回结果为其他点的索引向量。查询点和被搜索点之间的距离取决于八叉树的分辨率
std::vector<int>pointIdxVec;//存储搜索结果索引
if (octree.voxelSearch (searchPoint, pointIdxVec))//执行搜索
  {
std::cout<<"Neighbors within voxel search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z<<")"
<<std::endl;
for (size_t i=0; i<pointIdxVec.size (); ++i)
std::cout<<"    "<< cloud->points[pointIdxVec[i]].x 
<<" "<< cloud->points[pointIdxVec[i]].y 
<<" "<< cloud->points[pointIdxVec[i]].z <<std::endl;
  }
//K近邻搜索
int K =10;
std::vector<int>pointIdxNKNSearch;//保存索引
std::vector<float>pointNKNSquaredDistance;//保存距离平方值
std::cout<<"K nearest neighbor search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with K="<< K <<std::endl;
if (octree.nearestKSearch (searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance) >0)
  {
for (size_t i=0; i<pointIdxNKNSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxNKNSearch[i] ].x 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].y 
<<" "<< cloud->points[pointIdxNKNSearch[i] ].z 
<<" (squared distance: "<<pointNKNSquaredDistance[i] <<")"<<std::endl;
  }
//半径内最近邻搜索
std::vector<int>pointIdxRadiusSearch;
std::vector<float>pointRadiusSquaredDistance;
float radius =256.0f* rand () / (RAND_MAX +1.0f);
std::cout<<"Neighbors within radius search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with radius="<< radius <<std::endl;
if (octree.radiusSearch (searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance) >0)
  {
for (size_t i=0; i<pointIdxRadiusSearch.size (); ++i)
std::cout<<"    "<<   cloud->points[ pointIdxRadiusSearch[i] ].x 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].y 
<<" "<< cloud->points[pointIdxRadiusSearch[i] ].z 
<<" (squared distance: "<<pointRadiusSquaredDistance[i] <<")"<<std::endl;
  }
}

八叉树部分类关键点说明
PCLoctree 组件提供了几个八叉树类型。它们各自的叶节点特征基本上是不同的。
OctreePointQoudPointVector (等于 OctreePointCloud ):该八叉树能够保存每一个叶节点上的点索引列。
OctreePointCloudSinglePoint: 该八叉树类仅仅保存每一个叶节点上的单个点索引。仅仅保存最后分配给 叶节点的
点索引。
OctreePointCloudOccupancy: 该八叉树不存储它的叶节点上的任何点信息。它能用于空间填充情况 检查。
OctreePointCloudDensily: 该八叉树存储每一个叶节点体素中的点的数目。它可以进行空间点集密集程 度查询。
如果需要高频率创建八叉树,请参看八叉树双缓冲技术实现( Octree2BufBase 类 )o 该 类在内存中同时保存了两个类似的八叉树对象。除了搜索操作之外,也可以用于空间变化检 测 c 此外,高级内存管理减少了八叉树建立过程中的内存分配和释放操作 G 双缓冲技术对八 叉树的实现可以通过设置模板参数 “OctreeT” 实例化不同的 OctreePointCloud 类。所有的八 叉树都支持八叉树结构和八叉树内容的串行化和反串行化。

5.无序点云数据集的空间变化检测

通过八叉树递归对比,检测被测点云CloudB和参考点云CloudA之间的不同点集,即存在与CloudB而不存在于CloudA的点集。这其中应用到八叉树双缓冲技术,使两片点云同时存储于内存中,共享一个八叉树对象。
这种检测的局限:只能检测CloudB相对于CloudA增加的点集,而不能检测减少的点集。

#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>
#include <iostream>
#include <vector>
#include <ctime>
	
int
main (int argc, char** argv)
{
srand ((unsigned int) time (NULL));
// 设置八叉树的分辨率
float resolution =32.0f;
// 实例化检测器对象
pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZ>octree (resolution);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudA (new pcl::PointCloud<pcl::PointXYZ> );//参考点云
//填充点云
cloudA->width =128;
cloudA->height =1;
cloudA->points.resize (cloudA->width *cloudA->height);
for (size_t i=0; i<cloudA->points.size (); ++i)
  {
cloudA->points[i].x =64.0f* rand () / (RAND_MAX +1.0f);
cloudA->points[i].y =64.0f* rand () / (RAND_MAX +1.0f);
cloudA->points[i].z =64.0f* rand () / (RAND_MAX +1.0f);
  }

octree.setInputCloud (cloudA);
octree.addPointsFromInputCloud ();
// 重置八叉树对象的缓冲区,但CloudA数据依然存储于内存中。两个点云共享八叉树对象。
octree.switchBuffers ();
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudB (new pcl::PointCloud<pcl::PointXYZ> );//被检测点云
//点云填充数据
cloudB->width =128;
cloudB->height =1;
cloudB->points.resize (cloudB->width *cloudB->height);
for (size_t i=0; i<cloudB->points.size (); ++i)
  {
cloudB->points[i].x =64.0f* rand () / (RAND_MAX +1.0f);
cloudB->points[i].y =64.0f* rand () / (RAND_MAX +1.0f);
cloudB->points[i].z =64.0f* rand () / (RAND_MAX +1.0f);
  }

octree.setInputCloud (cloudB);
octree.addPointsFromInputCloud ();
std::vector<int>newPointIdxVector;
//探测CloudB相对于CloudA增加的点集
octree.getPointIndicesFromNewVoxels (newPointIdxVector);
//打印
std::cout<<"Output from getPointIndicesFromNewVoxels:"<<std::endl;
for (size_t i=0; i<newPointIdxVector.size (); ++i)
std::cout<<i<<"# Index:"<<newPointIdxVector[i]
<<"  Point:"<<cloudB->points[newPointIdxVector[i]].x <<" "
<<cloudB->points[newPointIdxVector[i]].y <<" "
<<cloudB->points[newPointIdxVector[i]].z <<std::endl;
}

标签:kd,八叉树,tree,points,pcl,点云,include,cloud
来源: https://blog.csdn.net/qq_36814762/article/details/110946583

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有