前言
在前几篇PCL学习文章中,我们搭建起相关环境,以及KD Tree的相关搜索算法。本篇将带大家学习相关八叉树相关内容,还是在上文的代码基础下,进行相关的修改。
1. 准备
1.1 oct-Tree基本概念
八叉树是一种用于描述三维空间的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。
- 一般中心点作为节点的分叉中心。
- 八叉树若不为空树的话,树中任一节点的子节点恰好只会有八个,或零个,也就是子节点不会有0与8以外的数目。
- 分割一直要进行到节点所对应的立方体或是完全空白,或是完全为V占据,或是其大小已是预先定义的体素大小,并且对它与V之交作一定的“舍入”,使体素或认为是空白的,或认为是V占据的。
1.2 添加按钮
依旧如上篇一样,在上面的代码基础上添加相关的按钮,以及对应的槽函数:如下
// oct分割
QPushButton *octTreeDivisionBtn;
// oct 搜索
QPushButton *octTreeSearchBtn;
octTreeDivisionBtn = new QPushButton(PCLView);
octTreeDivisionBtn->setObjectName(QString::fromUtf8("Oct Division Tree"));
octTreeDivisionBtn->setGeometry(QRect(50, 160, 120, 40));
// octTreeSearchBtn
octTreeSearchBtn = new QPushButton(PCLView);
octTreeSearchBtn->setObjectName(QString::fromUtf8("Oct Search Tree"));
octTreeSearchBtn->setGeometry(QRect(50, 210, 120, 40));
octTreeDivisionBtn->setText(QCoreApplication::translate("PCLView","Oct Tree Division", nullptr));
octTreeSearchBtn->setText(QCoreApplication::translate("PCLView","Oct Tree Search", nullptr));
// oct 按钮点击事件
void octTreePressed();
void octTreeSearchPressed();
运行如图所示:
2. octTree 八叉树分割
八叉树的算法步骤逻辑如下:
- 设定最大递归深度。
- 找出场景的最大尺寸,并以此尺寸建立第一个立方体。
- 依序将单位元元素丢入能被包含且没有子节点的立方体。
- 若没达到最大递归深度,就进行细分八等份,再将该立方体所装的单位元元素全部分担给八个子立方体。
- 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体是一样的,则该子立方体停止细分,因为跟据空间分割理论,细分的空间所得到的分配必定较少,若是一样数目,则再怎么切数目还是一样,会造成无穷切割的情形。
- 重复3,直到达到最大递归深度。
PCL库中实现相关的逻辑算法,我们只需调用相应的SDK即可,代码如下:
void UI::PclViewer::octTreePressed() {
// 设置划分分辨率
float resolution = .5f;
pcl::octree::OctreePointCloudSearch<PointT> octree(resolution);
octree.setInputCloud(pointCloud);
// 进行对点云进行octTree的建立
octree.addPointsFromInputCloud();
// 显示分割后的点云
for (auto branch:octree){
if (branch->getNodeType() == pcl::octree::LEAF_NODE){
auto children = dynamic_cast<pcl::octree::OctreeLeafNode<pcl::octree::OctreeContainerPointIndices> *>(branch);
std::vector<int> pointIndexes;
children->getContainer().getPointIndices(pointIndexes);
auto r = random() % 155 + 100;
auto g = random() % 155 + 100;
auto b = random() % 155 + 100;
for (auto i:pointIndexes){
pointCloud->points[i].r = r ;
pointCloud->points[i].g = g;
pointCloud->points[i].b = b ;
}
}
}
// 重新更新点云
updatePointCloud();
}
上面代码因为PCL库给我们完成很多地方,因此调用起来很简单。先申明一个octree对象在设置点云,并且构建octTree,最后将分割的结果进行展示。运行代码,其结果如下:
3. octTree 八叉树搜索
根据所建立的octree,对指定点进行收拾到分割到最小的单元体。其实现如下:
void UI::PclViewer::octTreeSearchPressed() {
//建立octTree
float resolution = .5f;
pcl::octree::OctreePointCloudSearch<PointT> octree(resolution);
octree.setInputCloud(pointCloud);
octree.addPointsFromInputCloud();
PointT searchPoint;
// 搜索点
searchPoint.x = 1024 * rand() / (RAND_MAX + 1.0f);
searchPoint.y = 1024* rand() /(RAND_MAX + 1.0f);
searchPoint.z = 1024 * rand() / (RAND_MAX + 1.0f);
std::vector<int> pointIdxVec;
// 搜索最近的单元体
if (octree.voxelSearch(searchPoint,pointIdxVec)){
for (int i: pointIdxVec) {
// 修改搜索后点的颜色
pointCloud->points[i].r = 255 ;
pointCloud->points[i].g = 250 ;
pointCloud->points[i].b = 250 ;
}
}
updatePointCloud();
}
代码上依旧是先建立起octTree,在指定点去搜索最小分割单元,最后一个展示结果。其结果如下:
4.总结
本篇我们了解PCL相关八叉树的学习,后续将继续更新后续内容