PCL - octTree八叉树

文章介绍了如何在PCL库中使用八叉树数据结构进行点云处理,包括八叉树的概念、建立与分割点云的步骤,以及如何进行点云搜索。通过添加按钮触发八叉树分割和搜索功能,展示了PCL库提供的便捷接口。

前言

在前几篇PCL学习文章中,我们搭建起相关环境,以及KD Tree的相关搜索算法。本篇将带大家学习相关八叉树相关内容,还是在上文的代码基础下,进行相关的修改。

1. 准备

1.1 oct-Tree基本概念

八叉树是一种用于描述三维空间的树状数据结构。八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,这八个子节点所表示的体积元素加在一起就等于父节点的体积。

  1. 一般中心点作为节点的分叉中心。
  2. 八叉树若不为空树的话,树中任一节点的子节点恰好只会有八个,或零个,也就是子节点不会有0与8以外的数目。
  3. 分割一直要进行到节点所对应的立方体或是完全空白,或是完全为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();

运行如图所示:
octTree按钮

2. octTree 八叉树分割

八叉树的算法步骤逻辑如下:

  1. 设定最大递归深度。
  2. 找出场景的最大尺寸,并以此尺寸建立第一个立方体。
  3. 依序将单位元元素丢入能被包含且没有子节点的立方体。
  4. 若没达到最大递归深度,就进行细分八等份,再将该立方体所装的单位元元素全部分担给八个子立方体。
  5. 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体是一样的,则该子立方体停止细分,因为跟据空间分割理论,细分的空间所得到的分配必定较少,若是一样数目,则再怎么切数目还是一样,会造成无穷切割的情形。
  6. 重复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相关八叉树的学习,后续将继续更新后续内容

### 使用PCL 1.8.1 实现八叉树压缩 为了利用PCL库中的八叉树方法来实现点云数据的压缩与解压操作,在`usr/include/pcl/compression/compression_profiles.h`文件中定义了一系列用于配置不同压缩级别的参数选项[^1]。具体来说,这些配置项允许开发者指定诸如分辨率、体素大小以及是否启用颜色信息保存等细节。 对于希望基于PCL 1.8.1版本执行此功能的应用程序而言,除了确保正确设置了环境变量(例如将`%PCL_ROOT%`指向安装目录),还需要特别注意编译过程中的依赖关系管理[^2]。当遇到编译错误时,应当仔细检查并调整项目属性里有关外部库链接的部分,特别是确认已指定了正确的`.lib`文件位置及其对应的头文件路径[^4]。 下面给出一段简单的C++代码片段作为示范,展示了怎样创建一个八叉树结构并对给定点集应用该算法: ```cpp #include <pcl/io/pcd_io.h> #include <pcl/octree/octree_pointcloud_compression.h> int main(int argc, char** argv){ pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); // 加载原始PCD文件到内存中 if (pcl::io::loadPCDFile<pcl::PointXYZ>("input.pcd", *cloud) == -1){ PCL_ERROR ("Couldn't read file input.pcd \n"); return (-1); } // 创建octree对象实例化 pcl::octree::OctreePointCloudCompression<pcl::PointXYZ> octree(0.1f); // 设置输入点云数据 octree.setInputCloud(cloud); // 执行编码流程 std::stringstream compressedData; octree.compressPointCloud(compressedData); // 解码恢复原状 pcl::PointCloud<pcl::PointXYZ>::Ptr decompressedCloud(new pcl::PointCloud<pcl::PointXYZ>()); octree.decompressPointCloud(compressedData,*decompressedCloud); // 输出处理后的结果至新文件 pcl::io::savePCDFileASCII("output_decompressed.pcd",*decompressedCloud); return 0; } ``` 这段示例说明了如何加载点云数据、初始化八叉树类的对象、调用成员函数完成压缩动作并将得到的数据流传递回去以便后续解压还原成初始状态。值得注意的是,这里使用的构造器接受浮点型参数表示最大枝叶节点尺寸;实际应用场景下可根据需求灵活设定这一数值以平衡存储效率和重建精度之间的权衡。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

myenjoy_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值