PCL中的八叉树

article/2025/10/28 23:33:29

目录

(1)什么是八叉树

(2)PCL中八叉树的体素形成和PCL中基于八叉树的点云压缩

(3)基于八叉树的k邻域搜索、半径搜索和体素近邻搜索

(4)基于八叉树和基于 kd-tree 的k邻域搜索、半径搜索性能比较

(5)基于八叉树的空间变化检测

(1)什么是八叉树

        八叉树(Octree)是一种用于描述三维空间的树状数据结构。想象一个正方体,我们最少可以切成多少个相同等分的小正方体?答案就是8个。再想象我们有一个房间,房间里某个角落藏着一枚金币,我们想很快的把金币找出来,怎么找最高效?我们可以把房间当成一个立方体,先切成八个小立方体,然后排除掉没有放任何东西的小立方体,再把有可能藏金币的小立方体继续切八等份….如此下去,平均在Log8(房间内的所有物品数)的时间内就可找到金币。因此,八叉树就是用在3D空间中的场景管理,可以很快地知道物体在3D场景中的位置,或侦测与其它物体是否有碰撞以及是否在可视范围内。

         以下是跟八叉树有关的几个概念的解释:

        体素:如上图所示,一个正方体空间被分割2次,形成64个边长相等的小正方体,这每个小正方体就被称为该八叉树的体素;

        深度:深度=正方体被切割的次数+1。如上图所示,一个正方体空间被分割2次,因此,该八叉树的深度就是3;

        分辨率:八叉树的分辨率就是体素的边长。如上图所示,假设大正方体的边长为1m,那么,该八叉树的体素的边长是0.25m,即,该八叉树的分辨率就是0.25。注意,PCL中点的距离单位默认就是m,因此,当进行点云压缩时,如果说给定的分辨率参数是5cm,那么,编程传入的参数就是0.05。

(2)PCL中八叉树的体素形成和PCL中基于八叉树的点云压缩

        点云由庞大的数据集组成,这些数据集通过距离、颜色、法线等附加信息来描述空间三维点。这么庞大的数据集要被高效的创建出来,需要占用相当大的存储资源,而一旦点云需要被存储或者通过速率受限制的通信信道进行传输,就需要对点云数据集进行压缩编码,以减少需要存储或者传输的数据量。

        PCL提供了点云压缩功能,它允许压缩编码所有类型的点云,包括“无序”点云,它具有无参考点和变化的尺寸、分辨率、分布密度和点顺序等结构特征。

        PCL进行点云的编码压缩之前,会将点云视为一个大的正方体,然后,将这个大的正方体按按八叉树的结构分割成多个小正方体组成的大正方体,这些小正方体就是八叉树的叶子节点,也被称为体素。PCL进行点云的压缩编码就是对这些体素分别进行编码,从而达到压缩的目的。

        PCL根据点云数据形成体素的方法是这样,首先,找出点云中3个坐标(x、y、z)的值最小的点作为参考点(xmin、ymin、zmin),然后以参考点为起点,分别沿3个坐标轴(x轴、y轴、z轴)的正方向画直线段,该直线段作为大正方体的3条棱边。直线段的长度是多少呢?这个直线段的长度这样计算,找出点云中3个坐标(x、y、z)的值最大的点(xmax、ymax、zmax),分别计算:

        xlen = xmax - xmin;

        ylen = ymax - ymin;

        zlen = zmax - zmin;

        然后,取最大值 len = max(xlen, ylen, zlen);

        创建PCL的八叉树对象时,会要求传入一个参数,参数名为“八叉树的分辨率”,记做octreeResolution,该参数就是体素(小正方体)的棱长。PCL会计算出八叉树的深度值n(n>=0,八叉树的深度就是大立方体被切割的层次数,第1层是1个立方体变8个立方体,第2层是8个立方体变64个立方体......),使之满足

        octreeResolution * 2 ^ n >= len;

        满足上式的最小n即为PCL创建的八叉树的深度。

        下面,编写2个小程序验证一下:

        创建Qt Console程序,

#.pro文件 加上头文件引用和库引用INCLUDEPATH += C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\3rdParty\VTK\include\vtk-9.1 \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLWidgets \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGL win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_s.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_cpp_s.lib)win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_visualizationd.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_iod.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_octreed.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\VTK\lib\vtkCommonCore-9.1d.lib)
//main.cpp#include <QCoreApplication>
#include <QDateTime>
#include <QThread>
#include <iostream>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/compression/compression_profiles.h>
#include <pcl/compression/octree_pointcloud_compression.h>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//创建3个点的点云pcl::PointCloud<pcl::PointXYZRGB>::Ptr clound(new pcl::PointCloud<pcl::PointXYZRGB>());clound->width = 3;clound->height = 1;clound->resize(clound->width * clound->height);//将点云的点赋值为(-1, -1, -1)、(0.5, 0.5, 0.5)和(1.5, 1.5, 1.5)int index = 0;clound->points[index].x = -1;clound->points[index].y = -1;clound->points[index].z = -1;clound->points[index].r = 255;clound->points[index].g = 0;clound->points[index].b = 0;index = 1;clound->points[index].x = 0.5;clound->points[index].y = 0.5;clound->points[index].z = 0.5;clound->points[index].r = 255;clound->points[index].g = 0;clound->points[index].b = 0;index = 2;clound->points[index].x = 1.5;clound->points[index].y = 1.5;clound->points[index].z = 1.5;clound->points[index].r = 255;clound->points[index].g = 0;clound->points[index].b = 0;//显示点云for(int kk = 0; kk < clound->points.size(); ++kk){std::cout << "Old cloud pt " << (kk + 1) << " : ( " << clound->points[kk].x << " , " << clound->points[kk].y << " , " << clound->points[kk].z << " )" << std::endl;}pcl::visualization::CloudViewer * viewer = new pcl::visualization::CloudViewer("ss1");viewer->showCloud(clound, "ss1");if(!viewer->wasStopped()){//创建八叉树点云压缩对象pcl::io::compression_Profiles_e pro = pcl::io::MANUAL_CONFIGURATION;pcl::io::OctreePointCloudCompression<pcl::PointXYZRGB> * enc = new pcl::io::OctreePointCloudCompression<pcl::PointXYZRGB>(pro, true, 0.01, 1, true);std::stringstream ss;//编码压缩enc->encodePointCloud(clound->makeShared(), ss);int octee_depth = enc->getTreeDepth();std::cout << "enc->getTreeDepth() : " << octee_depth << std::endl;//编码压缩后的点云pcl::PointCloud<pcl::PointXYZRGB>::Ptr clound_out(new pcl::PointCloud<pcl::PointXYZRGB>());enc->decodePointCloud(ss, clound_out);for(int kk = 0; kk < clound_out->points.size(); ++kk){clound_out->points[kk].r = 0;clound_out->points[kk].g = 255;clound_out->points[kk].b = 0;std::cout << "New cloud pt " << (kk + 1) << " : ( " << clound_out->points[kk].x << " , " << clound_out->points[kk].y << " , " << clound_out->points[kk].z << " )" << std::endl;}//显示压缩后的点云viewer->showCloud(clound_out, "ss2");}while(!viewer->wasStopped()){}delete viewer;viewer = nullptr;return a.exec();
}

        以上代码中,创建的点云压缩对象 pcl::io::OctreePointCloudCompression,该类的构造函数有如下几个参数:

/** \brief Constructor* \param compressionProfile_arg:  define compression profile* \param octreeResolution_arg:  octree resolution at lowest octree level* \param pointResolution_arg:  precision of point coordinates* \param doVoxelGridDownDownSampling_arg:  voxel grid filtering* \param iFrameRate_arg:  i-frame encoding rate* \param doColorEncoding_arg:  enable/disable color coding* \param colorBitResolution_arg:  color bit depth* \param showStatistics_arg:  output compression statistics*/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 :压缩配置,代码中设置为 pcl::io::MANUAL_CONFIGURATION ,表示自己设置参数,不用默认参数;

        参数 showStatistics_arg :编码压缩过程中,是否在colsole窗口显示压缩信息;

        参数 pointResolution_arg :体素编码压缩时的点分辨率,用于体素压缩编码,这个参数只在参数 doVoxelGridDownDownSampling_arg == false 时有效;

        参数 octreeResolution_arg :体素(八叉树叶子节点正方体的棱长)的棱长;

        参数 doVoxelGridDownDownSampling_arg :false-根据特定公式计算体素压缩编码后的点;true-取体素的中心点作为体素压缩编码后的点;

        参数 iFrameRate_arg :用来决定进行I帧编码的频率,如果此数值为30,则每隔30帧进行一次I帧编码,中间的帧则进行P帧编码;

        参数 doColorEncoding_arg :false-不会对颜色进行编码;true-进行颜色编码,如果输入的点云文件没有颜色信息,则在解码时赋予其颜色默认值;

        参数 colorBitResolution_arg :用来表示颜色的RGB的保留的有效位数。

        根据代码中参数给定 pcl::io::OctreePointCloudCompression(pro, true, 0.01, 1, true);

        doVoxelGridDownDownSampling_arg == true :不进行体素编码,即取体素中心点作为新点云的点;

        octreeResolution_arg == 1 :八叉树分辨率为1,即体素正方体棱长为1。

        依据前文的分析,代码中点云最小点(-1, -1, -1),最大点(1.5, 1.5, 1.5), len = 1.5 - ( -1 ) = 2.5;octreeResolution=1,

        满足式子 octreeResolution * 2 ^ n >= len 的最小 n == 2。

        因此,这段代码会将点云以(-1, -1, -1)为参考点,将棱长为 (octreeResolution * 2 ^ n) == 4 的大正方体,切割2层,形成 64 个棱长为 1 的小正方体(体素),八叉树的深度为 n == 2,然后,因为参数 doVoxelGridDownDownSampling_arg == true ,所以,压缩后的点云将每个包含点的体素取其中心点形成编码压缩后的点云。

        根据 octreeResolution_arg == 1 , 可知源点云的3个点分别位于3个不同的体素,而这3个体素的中心点分别为(-0.5, -0.5, -0.5)、(0.5, 0.5, 0.5)和(1.5, 1.5, 1.5),因此,该代码编码压缩过后的新点云肯定是由这3个点组成。

        运行程序查看输出和预期一致:

(3)基于八叉树的k邻域搜索、半径搜索和体素近邻搜索

        基于八叉树的搜索和基于kd-tree的搜索差别不大,主要是创建的搜索对象不一致。

        基于八叉树要创建对象 pcl::octree::OctreePointCloudSearch。

        k邻域搜索调用 pcl::octree::OctreePointCloudSearch::nearestKSearch;

        半径搜索调用 pcl::octree::OctreePointCloudSearch::radiusSearch;

        体素近邻搜索调用 pcl::octree::OctreePointCloudSearch::voxelSearch。

        以下用一个例子演示这三种搜索函数的调用:

        创建Qt Console程序,

#.pro文件 加上头文件引用和库引用INCLUDEPATH += C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\3rdParty\VTK\include\vtk-9.1 \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLWidgets \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLwin32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_s.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_cpp_s.lib)win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_visualizationd.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_iod.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_octreed.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\VTK\lib\vtkCommonCore-9.1d.lib)
//main.cpp#include <QCoreApplication>
#include <QDateTime>
#include <QThread>
#include <iostream>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/compression/compression_profiles.h>
#include <pcl/compression/octree_pointcloud_compression.h>
#include <pcl/octree/octree_search.h>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//创建6个点的点云pcl::PointCloud<pcl::PointXYZ>::Ptr clound(new pcl::PointCloud<pcl::PointXYZ>());clound->width = 6;clound->height = 1;clound->resize(clound->width *clound->height);int index = 0;clound->points[index].x = -1;clound->points[index].y = -1;clound->points[index].z = -1;index = 1;clound->points[index].x = 0.5;clound->points[index].y = 0.5;clound->points[index].z = 0.5;index = 2;clound->points[index].x = 1.5;clound->points[index].y = 1.5;clound->points[index].z = 1.5;index = 3;clound->points[index].x = 1.8;clound->points[index].y = 1.8;clound->points[index].z = 1.8;index = 4;clound->points[index].x = 1.9;clound->points[index].y = 1.9;clound->points[index].z = 1.9;index = 5;clound->points[index].x = 1.2;clound->points[index].y = 1.2;clound->points[index].z = 1.2;//打印点云qDebug() << "Clound : ";for(int i = 0; i < clound->points.size(); ++i){qDebug() << clound->points[i].x << "," << clound->points[i].y << "," << clound->points[i].z;}//创建八叉树对象,并将点云对象赋值给它float octreeResolution_arg = 1.0f; //体素棱长为1pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(octreeResolution_arg);octree.setInputCloud(clound);octree.addPointsFromInputCloud();//构建八叉树int octee_depth = octree.getTreeDepth();std::cout << "octree.getTreeDepth() : " << octee_depth << std::endl;//输出八叉树深度//体素近邻搜索pcl::PointXYZ search_pt_for_neighbor_in_voxel;search_pt_for_neighbor_in_voxel.x = -0.3;search_pt_for_neighbor_in_voxel.y = -0.4;search_pt_for_neighbor_in_voxel.z = -0.6;std::cout << " Search point(" << search_pt_for_neighbor_in_voxel.x << "," << search_pt_for_neighbor_in_voxel.y << "," << search_pt_for_neighbor_in_voxel.z << ") in voxel 1 : ";std::vector<int> pt_index_for_neighbor_in_voxel;if(octree.voxelSearch(search_pt_for_neighbor_in_voxel, pt_index_for_neighbor_in_voxel)){std::cout << "Points in the same voxel 1 :" << std::endl;for(int i = 0; i < pt_index_for_neighbor_in_voxel.size(); ++i){int pt_index = pt_index_for_neighbor_in_voxel[i];if(i > 0)std::cout << ",";std::cout << " ( " << clound->points[pt_index].x << "," << clound->points[pt_index].y << "," << clound->points[pt_index].z << " ) ";}std::cout << std::endl;}elsestd::cout << "None point in the same voxel 1 ." << std::endl;search_pt_for_neighbor_in_voxel.x = 1.3;search_pt_for_neighbor_in_voxel.y = 1.4;search_pt_for_neighbor_in_voxel.z = 1.6;pt_index_for_neighbor_in_voxel.clear();std::cout << " Search point(" << search_pt_for_neighbor_in_voxel.x << "," << search_pt_for_neighbor_in_voxel.y << "," << search_pt_for_neighbor_in_voxel.z << ") in voxel 2 : ";if(octree.voxelSearch(search_pt_for_neighbor_in_voxel, pt_index_for_neighbor_in_voxel)){std::cout << "Points in the same voxel 2 :" << std::endl;for(int i = 0; i < pt_index_for_neighbor_in_voxel.size(); ++i){int pt_index = pt_index_for_neighbor_in_voxel[i];if(i > 0)std::cout << ",";std::cout << " ( " << clound->points[pt_index].x << "," << clound->points[pt_index].y << "," << clound->points[pt_index].z << " ) ";}std::cout << std::endl;}elsestd::cout << "None point in the same voxel 2 ." << std::endl;//k近邻搜索int K = 4;//最近的4个点pcl::PointXYZ search_pt_for_neighbor_k;search_pt_for_neighbor_k.x = 1.3;search_pt_for_neighbor_k.y = 1.4;search_pt_for_neighbor_k.z = 1.6;std::vector<int> pt_index_for_neighbor_k;std::vector<float> squared_distance_for_neighbor_k;std::cout << " Search point(" << search_pt_for_neighbor_k.x << "," << search_pt_for_neighbor_k.y << "," << search_pt_for_neighbor_k.z << ") for neighbor K( K=" << K << ") : ";if(octree.nearestKSearch(search_pt_for_neighbor_k, K, pt_index_for_neighbor_k, squared_distance_for_neighbor_k)){std::cout << "Points for neighbor K :" << std::endl;for(int i = 0; i < pt_index_for_neighbor_k.size(); ++i){int pt_index = pt_index_for_neighbor_k[i];float sd = squared_distance_for_neighbor_k[i];std::cout << " ( " << clound->points[pt_index].x << "," << clound->points[pt_index].y << "," << clound->points[pt_index].z << " ) "<< "- squared_distance : " << sd << " " << std::endl;}}elsestd::cout << "None point for neighbor K ." << std::endl;//半径近邻搜索double raius = 0.7;//距离为 (radius ^ 2) > (3 * (1.9 - 1.5) ^ 2),保证体素内4个点都在距离范围内pcl::PointXYZ search_pt_for_radius;search_pt_for_radius.x = 1.5;search_pt_for_radius.y = 1.5;search_pt_for_radius.z = 1.5;std::vector<int> pt_index_for_radius;std::vector<float> squared_distance_for_radius;std::cout << " Search point(" << search_pt_for_radius.x << "," << search_pt_for_radius.y << "," << search_pt_for_radius.z << ") for radius( radius=" << raius << ") : ";if(octree.radiusSearch(search_pt_for_radius, raius, pt_index_for_radius, squared_distance_for_radius)){std::cout << "Points for radius :" << std::endl;for(int i = 0; i < pt_index_for_radius.size(); ++i){int pt_index = pt_index_for_radius[i];float sd = squared_distance_for_radius[i];std::cout << " ( " << clound->points[pt_index].x << "," << clound->points[pt_index].y << "," << clound->points[pt_index].z << " ) "<< "- squared_distance : " << sd << " " << std::endl;}}elsestd::cout << "None point for radius ." << std::endl;return a.exec();
}

        以上程序的输出为:

(4)基于八叉树和基于 kd-tree 的k邻域搜索、半径搜索的性能比较

        下面通过写一个小程序对比测试一下kd-tree和八叉树的k邻域搜索、半径搜索的性能。

        创建Qt Console程序,

#.pro文件 加上头文件引用和库引用INCLUDEPATH += C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\3rdParty\VTK\include\vtk-9.1 \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLWidgets \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLwin32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_s.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_cpp_s.lib)win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_visualizationd.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_iod.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_octreed.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\VTK\lib\vtkCommonCore-9.1d.lib)
//main.cpp#include <QCoreApplication>
#include <QDateTime>
#include <QThread>
#include <iostream>
#include <chrono>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/compression/compression_profiles.h>
#include <pcl/compression/octree_pointcloud_compression.h>
#include <pcl/octree/octree_search.h>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//创建1000个点的点云(取值范围为1~RAND_MAX)pcl::PointCloud<pcl::PointXYZ>::Ptr clound(new pcl::PointCloud<pcl::PointXYZ>());clound->width = 1000;//10000//100000//1000000clound->height = 1;clound->resize(clound->width *clound->height);for(int i = 0; i < clound->points.size(); ++i){clound->points[i].x = rand();clound->points[i].y = rand();clound->points[i].z = rand();}//kd-treepcl::KdTreeFLANN<pcl::PointXYZ> kdtree;kdtree.setInputCloud(clound);//八叉树float octreeResolution_arg = 1.0f; //体素棱长为1pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(octreeResolution_arg);octree.setInputCloud(clound);octree.addPointsFromInputCloud();//查找点pcl::PointXYZ search_pt;search_pt.x = rand();search_pt.y = rand();search_pt.z = rand();std::vector<int> pt_index;std::vector<float> pt_sqart_dis;std::cout << " Total points count : " << clound->points.size() << std::endl;//K邻域搜索std::cout << " Neighbor K search : " << std::endl;pt_index.clear();pt_sqart_dis.clear();int K = 10;std::chrono::system_clock::time_point start = std::chrono::system_clock::now();kdtree.nearestKSearch(search_pt, K, pt_index, pt_sqart_dis);std::chrono::system_clock::time_point end = std::chrono::system_clock::now();auto nao_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);std::cout << " kd-tree consuming time : " << nao_time.count() << " ns " << std::endl;std::cout << "Points :" << std::endl;for(int i = 0; i < pt_index.size(); ++i){int pti = pt_index[i];float sd = pt_sqart_dis[i];std::cout << " ( " << clound->points[pti].x << "," << clound->points[pti].y << "," << clound->points[pti].z << " ) "<< "- squared_distance : " << sd << " ; ";}std::cout << std::endl;pt_index.clear();pt_sqart_dis.clear();K = 10;start = std::chrono::system_clock::now();octree.nearestKSearch(search_pt, K, pt_index, pt_sqart_dis);end = std::chrono::system_clock::now();nao_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);std::cout << " octree consuming time : " << nao_time.count() << " ns " << std::endl;std::cout << "Points :" << std::endl;for(int i = 0; i < pt_index.size(); ++i){int pti = pt_index[i];float sd = pt_sqart_dis[i];std::cout << " ( " << clound->points[pti].x << "," << clound->points[pti].y << "," << clound->points[pti].z << " ) "<< "- squared_distance : " << sd << " ; ";}std::cout << std::endl;//半径搜索std::cout << " Radius search : " << std::endl;pt_index.clear();pt_sqart_dis.clear();float radius = 0x1000;start = std::chrono::system_clock::now();kdtree.radiusSearch(search_pt, radius, pt_index, pt_sqart_dis);end = std::chrono::system_clock::now();nao_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);std::cout << " kd-tree consuming time : " << nao_time.count() << " ns " << std::endl;std::cout << "Points count : " << pt_index.size() << std::endl;/*for(int i = 0; i < pt_index.size(); ++i){int pti = pt_index[i];float sd = pt_sqart_dis[i];std::cout << " ( " << clound->points[pti].x << "," << clound->points[pti].y << "," << clound->points[pti].z << " ) "<< "- squared_distance : " << sd << " ; ";}std::cout << std::endl;*/pt_index.clear();pt_sqart_dis.clear();radius = 0x1000;start = std::chrono::system_clock::now();octree.radiusSearch(search_pt,radius, pt_index, pt_sqart_dis);end = std::chrono::system_clock::now();nao_time = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);std::cout << " octree consuming time : " << nao_time.count() << " ns " << std::endl;std::cout << "Points count : " << pt_index.size() << std::endl;/*for(int i = 0; i < pt_index.size(); ++i){int pti = pt_index[i];float sd = pt_sqart_dis[i];std::cout << " ( " << clound->points[pti].x << "," << clound->points[pti].y << "," << clound->points[pti].z << " ) "<< "- squared_distance : " << sd << " ; ";}std::cout << std::endl;*/return a.exec();
}

        1000个点的计算结果:

         10,000个点的计算结果:

         100,000个点的计算结果:

         1,000,000个点的计算结果:

         从4次计算结果来看,对于k近邻搜索和半径搜索,kd-tree比八叉树的速度更快一些,性能更好。

(5)基于八叉树的空间变化检测

         八叉树是一种管理稀疏3D数据的树状数据结构,可以用于多个无序点云之间的空间变化检测,这些点云可能在尺寸、分辨率、密度和点顺序方面有所差异。

         通过递归地比较八叉树的树结构,可以鉴定出由八叉树产生的体素组成之间的区别多代表的空间变化。

         以下通过一个小程序看下PCL的八叉树如何检测出点云的体素变化:

         创建Qt Console程序,

#.pro文件 加上头文件引用和库引用INCLUDEPATH += C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\include\pcl-1.12 \C:\PCL1.12.1\3rdParty\FLANN\include \C:\PCL1.12.1\3rdParty\Boost\include\boost-1_78 \C:\PCL1.12.1\3rdParty\Eigen\eigen3 \C:\PCL1.12.1\3rdParty\VTK\include\vtk-9.1 \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLWidgets \C:\Qt\Qt6.3\6.3.0\msvc2019_64\include\QtOpenGLwin32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_s.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\FLANN\lib\flann_cpp_s.lib)win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_commond.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_visualizationd.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_iod.lib)
win32:LIBS += $$quote(C:\PCL1.12.1\lib\pcl_octreed.lib)win32:LIBS += $$quote(C:\PCL1.12.1\3rdParty\VTK\lib\vtkCommonCore-9.1d.lib)
//main.cpp#include <QCoreApplication>
#include <QDateTime>
#include <QThread>
#include <iostream>
#include <chrono>
#include <pcl/point_cloud.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/compression/compression_profiles.h>
#include <pcl/compression/octree_pointcloud_compression.h>
#include <pcl/octree/octree_search.h>
#include <pcl/octree/octree_pointcloud_changedetector.h>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//创建6个点的无序点云pcl::PointCloud<pcl::PointXYZ>::Ptr clound(new pcl::PointCloud<pcl::PointXYZ>());clound->width = 6;clound->height = 1;clound->resize(clound->width *clound->height);int index = 0;clound->points[index].x = -1;clound->points[index].y = -1;clound->points[index].z = -1;index = 1;clound->points[index].x = 0.5;clound->points[index].y = 0.5;clound->points[index].z = 0.5;index = 2;clound->points[index].x = 1.5;clound->points[index].y = 1.5;clound->points[index].z = 1.5;index = 3;clound->points[index].x = 1.8;clound->points[index].y = 1.8;clound->points[index].z = 1.8;index = 4;clound->points[index].x = 1.9;clound->points[index].y = 1.9;clound->points[index].z = 1.9;index = 5;clound->points[index].x = 1.2;clound->points[index].y = 1.2;clound->points[index].z = 1.2;float octreeResolution_arg = 1.0f; //体素棱长为1pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZ> octree(octreeResolution_arg);octree.setInputCloud(clound); //设置输入点云octree.addPointsFromInputCloud(); //从输入点云构造前台的八叉树//OctreePointCloudChangeDetector从Octree2BufBase继承,该类管理2个八叉树,通过switchBuffers可以切换前/后台的八叉树octree.switchBuffers();//再创建3个点的无序点云pcl::PointCloud<pcl::PointXYZ>::Ptr cloundB(new pcl::PointCloud<pcl::PointXYZ>());cloundB->width = 3;cloundB->height = 1;cloundB->resize(cloundB->width *cloundB->height);index = 0;cloundB->points[index].x = -1;cloundB->points[index].y = -1;cloundB->points[index].z = -1;//cloundB缺少了clound中的点(0.5, 0.5, 0.5)所在的体素index = 1;//clound->points[index].x = 0.5;//clound->points[index].y = 0.5;//clound->points[index].z = 0.5;cloundB->points[index].x = 1.5;cloundB->points[index].y = 1.5;cloundB->points[index].z = 1.5;//cloundB增加了点(2.5, 2.5, 2.5)所在的体素index = 2;cloundB->points[index].x = 2.5;cloundB->points[index].y = 2.5;cloundB->points[index].z = 2.5;octree.setInputCloud(cloundB); //设置输入点云octree.addPointsFromInputCloud(); //从输入点云构造前台的八叉树std::vector<int> indices;//点的索引octree.getPointIndicesFromNewVoxels(indices);std::cout << "Points :" << std::endl;for(int i = 0; i < indices.size(); ++i){int pti = indices[i];std::cout << " ( " << cloundB->points[pti].x << "," << cloundB->points[pti].y << "," << cloundB->points[pti].z << " ) " << " ; ";}std::cout << std::endl;return a.exec();
}

        程序输出为:

        根据代码中初始化的点云,cloundB形成的八叉树和clound形成的八叉树,在(包含点的)体素上,实际上是减少了一个体素(点(0.5,0.5,0.5)所在的体素)和增加了一个体素(点(2.5,2.5,2.5)所在的体素),但是,程序只给出了增加的体素,并未给出减少的体素。可见,PCL的基于八叉树的控件变化检测,只检测新点云比原点云增加的体素,而不检查新点云比原点云减少的体素。


http://chatgpt.dhexx.cn/article/bcIahwTX.shtml

相关文章

八叉树 java_java简单实现八叉树图像处理代码示例

一晃工作有段时间了&#xff0c;第一次写博客&#xff0c;有点不知道怎么写&#xff0c;大家将就着看吧&#xff0c;说的有什么不正确的也请大家指正。 最近工作中用到了一个图像压缩的功能。找了一些工具&#xff0c;没有太好的选择。最后选了一个叫jdeli的&#xff0c;奈何效…

八叉树

http://hi.baidu.com/onlywater/blog/item/905c5e162ed18f4021a4e9c1.html 一、八叉树基本原理&#xff1a; 用八叉树来表示三维形体&#xff0c;并研究这种表示下的各种操作以及应用&#xff0c;是进入80年代后开展起来的。这种方法&#xff0c;既可以看成是四叉树方法在三维…

Octree(八叉树)

1. 算法原理 八叉树&#xff08;Octree&#xff09;是一种用于描述三维空间的树状数据结构。八叉树的每个节点表示一个正方体的体积元素&#xff0c;每个节点有八个子节点&#xff0c;将八个子节点所表示的体积元素加在一起就等于父节点的体积。八叉树是四叉树在三维空间上的扩…

【PCL自学:ocTree】八叉树(octree)的原理及应用案例(点云压缩,搜索,空间变化)

PCL中八叉树&#xff08;octree&#xff09;的原理及应用案例 一、什么是八叉树ocTree&#xff1f;1.八叉树原理 二、八叉树应用案例1.点云压缩2.用八叉树进行空间划分和搜索操作3.无序点云数据的空间变化检测 一、什么是八叉树ocTree&#xff1f; 1.八叉树原理 上世纪80年代&…

十进制小数化为二进制小数的方法是什么_十进制转成二进制的两种方式

第一种&#xff1a;用2整除的方式。 用2整除十进制整数&#xff0c;得到一个商和余数&#xff1b;再用2去除商&#xff0c;又会得到一个商和余数&#xff0c;如此重复&#xff0c;直到商为小于1时为止&#xff0c;然后把先得到余数作为二进制数的低位有效位&#xff0c;后得到的…

Python 利用内置函数把二进制小数转换为十进制

如果需要把一个二进制整数转换为十进制整数&#xff0c;只需要简单的一行&#xff1a; int(1101,2) 但如果有一个二进制小数的话&#xff0c;就需要自己实现一个函数了。 不过&#xff0c;许多人是这样写的&#xff1a;(图片取自这里) 可是&#xff0c;由于python本身并不适…

[学习笔记] 二进制小数表示方法

文章目录 科学计数法二进制推广计算机中的小数EXCESS表示系统特殊情况举例&#xff08;float&#xff09;普通情况最大正实数普通情况最小负实数普通情况最小正实数特殊情况最大正实数 科学计数法 科学计数法想必大家都很熟悉了&#xff0c;往往通过如下形式表示一个实数&…

十进制小数化为二进制小数的方法是什么_二进制的转换

二进制是在计算机中常用的一种进制数&#xff0c;其数据用0和1两个数码来表示数据。我们人类常用的是十进制&#xff0c;那么二进制和十进制之间是有一个转换方法的。 二进制转换十进制 一个二进制数转换为十进制数&#xff0c;是比较简单的&#xff0c;其方法就是用每一个位置…

二进制小数的意义

回忆小学学的十进制小数的意义&#xff1a; 15.23这个小数&#xff0c;1是十位&#xff0c;5是个位&#xff0c;2是十分位&#xff0c;3是百分位。这个小数的意义为&#xff1a;&#xff0c;因为最低位为百分位&#xff0c;所以分母是100。 小数末尾加上0或去掉0&#xff0c;小…

浮点数(小数)在计算机中如何用二进制存储?

浮点数在计算机中如何用二进制存储&#xff1f; 前言 前面我有篇博文详解了二进制数&#xff0c;以及如何同二进制数表示整数。但是&#xff0c;计算机处理的不仅仅是整数&#xff0c;还有小数&#xff0c;同样小数在计算机也是用二进制进行存储的&#xff0c;但是&#xff0…

二进制小数的表示

二级制小数分为两大类&#xff1a;1、定点数&#xff1b;2、浮点数。 定点数 定点数&#xff1a; &#xff08;1&#xff09;小数点位置固定不变的数。 &#xff08;2&#xff09;定点数有定点整数和定点小数。 &#xff08;定点整数&#xff1a;小数部分为0&#xff1b;定点…

腾讯TDSQL全时态数据库系统论文入选VLDB

当地时间2019年8月26至30日&#xff0c;VLDB 2019会议在美国加利福尼亚召开&#xff0c;腾讯分布式数据库TDSQL与中国人民大学最新联合研究成果被VLDB 2019接收并将通过长文形式发表。VLDB是国际数据管理与数据库领域顶尖的学术会议之一&#xff0c;这是继去年腾讯TDSQL相似度计…

顶会VLDB‘22论文解读:CAE-ENSEMBLE算法

摘要&#xff1a;针对时间序列离群点检测问题&#xff0c;提出了基于CNN-AutoEncoder和集成学习的CAE-ENSEMBLE深度神经网络算法&#xff0c;并通过大量的实验证明CAE-ENSEMBLE算法能有效提高时间序列离群点检测的准确度与效率。 本文分享自华为云社区《VLDB22 CAE-ENSEMBLE论文…

【轨迹压缩】Trajectory Simplification: On Minimizing the Direction-based Error [2015] [VLDB]

一、一个动机 保护方向信息的方向保持轨迹简化&#xff08;DPTS&#xff09;已被证明表现良好&#xff0c;而现有关于 DPTS 的研究 要求用户指定一个容错&#xff0c;在某些情况下用户可能不知道如何正确设置&#xff08;例如&#xff0c;容错只能在未来某个时间知道&#xff…

VLDB 2021 EAB最佳论文:深度解析机器学习的基数估计为何无法实现?

©作者 | 曲昌博单位 | 西蒙菲莎大学近日&#xff0c;IEEE 数据工程新星奖王健楠团队论文《Are We Ready for Learned Cardinality Estimation?》夺得数据库顶会 VLDB 2021 年度的 EA&B 最佳论文奖。 数据库是企业管理和查询数据的复杂软件系统。 近年来随着机器学习以…

Transformers如何处理表格数据?【VLDB2022教程】Transformer表格数据表示:模型和应用...

来源&#xff1a;专知 本文为教程介绍&#xff0c;建议阅读5分钟最近的研究工作通过开发表格数据的神经表示扩展了语言模型。 在过去的几年中&#xff0c;自然语言处理界见证了基于transformer的语言模型(LM)在自由文本的神经表示方面的进展。鉴于关系表中可用知识的重要性&…

openGauss亮相VLDB2020,展示内存优化研究成果

VLDB&#xff08;Very Large Data Base&#xff09;作为数据库领域的三大顶级国际会议之一&#xff0c;是面向数据库研究人员&#xff0c;内核开发人员&#xff0c;开发商以及用户的年度国际会议论坛&#xff0c;代表数据库系统领域最杰出的研究和工程进展。在2020年&#xff0…

VLDB 2023 | 北大河图发布分布式训练神器Galvatron,一键实现大模型高效自动并行...

©作者 | 北京大学河图团队 单位 | 北京大学数据与智能实验室 北大河图团队提出了一套面向大模型的自动并行分布式训练系统 Galvatron&#xff0c;相比于现有工作在多样性、复杂性、实用性方面均具有显著优势&#xff0c;论文成果已经被 VLDB 2023 接收。 最近一段时间&…

利用 Map-Reduce 从文件中找到出现频率最高的 10 个 URL(2021 VLDB Summer School Lab0)

这篇博文主要是对 2021 VLDB Summer School Lab0 的一个总结 这个lab与MIT 6.824 的 lab1 相似&#xff0c;个人感觉比MIT 6.824 的 lab1 要稍微简单些&#xff0c;更容易上手。通过这个lab&#xff0c;可以学习到一些 Golang 的基础知识并对分布式系统有一个基础的了解&#…

Flink OLAP 助力 ByteHTAP 亮相数据库顶会 VLDB

复杂查询 QPS 破百&#xff0c;字节跳动 Flink OLAP 助力 ByteHTAP 亮相数据库顶会 VLDB。 2022 年 9 月 5 日至 9 月 9 日&#xff0c;VLDB 2022 在澳大利亚悉尼举行。字节跳动基础架构研究成果《ByteHTAP: ByteDance’s HTAP System with High Data Freshness and Strong Dat…