PCL从理解到应用【04】Octree 原理分析 | 案例分析 | 代码实现

news2024/9/23 1:38:17

前言

Octree 作为一种高效的空间分割数据结构,具有重要的应用价值。

本文将深入分析 Octree 的原理,通过多个实际案例帮助读者全面理解其功能和应用,包括最近邻搜索、半径搜索、盒子搜索以及点云压缩(体素化)。

           特性         近邻搜索                半径搜索             盒子搜索    点云压缩(体素化)
描述查找距离给定点最近的一个或多个点查找给定点一定半径范围内的所有点查找给定空间盒子内的所有点将点云数据划分为均匀大小的立方体(体素)
输入参数目标点,近邻数量目标点,半径盒子的最小点和最大点分辨率
输出最近的一个或多个点的索引及距离半径范围内所有点的索引及距离盒子内所有点的索引每个体素的中心点
适用场景最近点查询,碰撞检测局部邻域分析,聚类空间范围查询,目标检测数据降采样,减少计算复杂度
优点精确查找最近点,效率高查找范围可调节,适用于局部分析可以查找任意形状的空间范围内的点大幅度减少数据量,保留数据空间结构
缺点仅限于查找最近的点,无法指定查找范围结果集大小随半径变化,计算量可能较大需要预先定义盒子范围,结果集大小不确定可能丢失部分细节信息
实现方法octree.nearestKSearchoctree.radiusSearchoctree.boxSearchoctree.getOccupiedVoxelCenters

此外,本文还提供了详细的源代码示例,便于读者实践和应用。

希望通过本文的学习,读者能够掌握 Octree 的基本原理及其在点云数据处理中的具体实现方法。 

看一下示例效果:

红色点表示搜索点,绿色点表示最近邻点,白色点表示原始点云数据。

一、Octree原理分析

Octree是一种用于分层分割三维空间的数据结构。它是将三维空间递归地划分成更小的立方体区域的树形结构。

每个节点代表一个空间区域,该区域可以进一步细分八个子区域

Octree的原理是基于递归地将三维空间,分割成更小的立方体区域,从而形成一棵树形结构。

这种结构能够有效地表示和操作三维空间中的数据,如下图所示:

特点:

  • 分层结构:Octree的每个节点对应一个立方体区域,当需要更详细的信息时,这些区域可以进一步划分成八个子区域。
  • 递归分割:通过递归地将空间划分为更小的部分,Octree可以有效地表示三维空间中的数据。
  • 适应性强:Octree可以灵活地适应不同密度的数据区域,在数据稀疏的区域使用较少的节点,而在数据密集的区域使用更多的节点。

用途:

  1. 三维计算机图形学:在图形渲染中,Octree用于加速光线追踪算法,通过快速确定光线与物体的交点。
  2. 碰撞检测:在物理模拟和游戏开发中,Octree用于高效的碰撞检测,特别是当物体分布在三维空间中的时候。
  3. 空间索引:在三维空间数据管理中,Octree用于空间索引和快速查询,例如在地理信息系统(GIS)中。
  4. 点云处理:在处理和表示三维扫描数据时,Octree可以有效地存储和操作点云数据。

KDTree与Octree进行对比分析,如下图所示:

          特点                               KDTree                                                                   Octree
适用维度k维三维
划分方式划分超平面划分立方体
结构类型二叉树八叉树
构建时间O(n log n)O(n log n)
查询效率平均O(log n),最坏情况O(n)平均O(log n),最坏情况O(n)
优势通用性强,高效查询任意维度点集适用于三维空间,高效处理三维点云数据
缺点高维效率下降,维度灾难仅适用于三维空间,可能需要较多内存和计算资源

二、Octree常用方法

PCL中的Octree提供了对点云数据进行分割、索引和查询的功能。

通过Octree,可以高效地实现点云的邻域搜索、体素化、下采样等操作。

常用类:

  • pcl::octree::OctreePointCloud(管理和操作三维点云数据
  • pcl::octree::OctreePointCloudSearch(点云中进行查询,搜索功能
  • pcl::octree::OctreePointCloudVoxel(点云数据进行体素化处理

 1)pcl::octree::OctreePointCloud

它是Octree数据结构的基本实现,专用于管理和操作三维点云数据。  

  • 提供基本的Octree构建和管理功能。
  • 支持添加、删除和更新点云数据。
  • 可以进行点云数据的空间划分和组织。

常用方法:

  • setInputCloud:设置输入点云。
  • addPointsFromInputCloud:从输入点云中添加点到Octree。
  • deleteVoxelAtPoint:删除指定点所在的体素。

示例代码: 

// 创建Octree对象,并指定分辨率为0.1
pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(0.1);

// 设置输入点云
octree.setInputCloud(cloud);

// 构建Octree
octree.addPointsFromInputCloud();

2)pcl::octree::OctreePointCloudSearch

它继承自OctreePointCloud,增加了搜索功能,用于高效地在点云中进行查询操作。

  • 在Octree的基础上,增加了搜索功能。
  • 支持各种类型的查询,如最近邻搜索、半径搜索、盒子搜索。

常用方法:

  • nearestKSearch:查找最近的K个邻居。
  • radiusSearch:查找给定半径内的所有点。
  • boxSearch:查找指定盒子区域内的所有点。

示例代码:

pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(0.1f); // 创建Octree对象,分辨率为0.1

octree.setInputCloud(cloud); // 设置输入点云
octree.addPointsFromInputCloud(); // 构建Octree

// 定义要查找的点
pcl::PointXYZ searchPoint;
std::vector<int> pointIdxNKNSearch;
std::vector<float> pointNKNSquaredDistance;
int K = 10;

// 最近邻搜索
octree.nearestKSearch(searchPoint, K, pointIdxNKNSearch, pointNKNSquaredDistance);

3)pcl::octree::OctreePointCloudVoxel

它继承自OctreePointCloud,增加了体素化功能,用于将点云数据进行体素化处理。

  • 在Octree的基础上,增加了体素化功能。
  • 能够计算每个体素的中心点,用于下采样和数据压缩。

常用方法:

  • addPointsFromInputCloud:从输入点云中添加点到Octree并进行体素化处理。
  • getVoxelCentroids:获取所有体素的中心点。

示例代码:

pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>());
pcl::octree::OctreePointCloudVoxel<pcl::PointXYZ> octree(0.1f); // 创建Octree对象,分辨率为0.1

octree.setInputCloud(cloud); // 设置输入点云
octree.addPointsFromInputCloud(); // 构建Octree并进行体素化

// 获取体素中心点
std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> voxelCentroids;
octree.getVoxelCentroids(voxelCentroids);

for (const auto& centroid : voxelCentroids)
{
    std::cout << "体素中心点: " << centroid.transpose() << std::endl;
}

这些类提供了在PCL中使用Octree进行点云数据管理和查询的基本功能,并能够高效地处理三维点云数据。

三、Octree案例——最近邻搜索

编写C++代码,调用pcl库实现Octree的最近邻搜索,通过cmake方式编译程序,最后可视化结果。

目录结构:

OctreeNearestNeighbor
├── CMakeLists.txt
├── src
│   └── main.cpp
└── build

在项目根目录下创建CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.10)
project(OctreeNearestNeighbor)

# 查找PCL库
find_package(PCL 1.9 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

# 编译可执行文件
add_executable(OctreeNearestNeighbor src/main.cpp)
target_link_libraries(OctreeNearestNeighbor ${PCL_LIBRARIES})

src/main.cpp, 在src目录下创建main.cpp文件,并添加以下代码:

#include <pcl/point_cloud.h>
#include <pcl/octree/octree_search.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <iostream>
#include <cstdlib> 
#include <ctime>  

int main()
{
    // 初始化随机数生成器
    std::srand(static_cast<unsigned int>(std::time(0)));

    // 创建一个PointXYZ点云指针并填充点云数据
    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);
    }

    // 定义Octree分辨率并创建Octree对象
    float resolution = 128.0f;
    pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);

    // 设置输入点云并构建Octree
    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::cout << "要查找的点: (" << searchPoint.x << ", " << searchPoint.y << ", " << searchPoint.z << ")" << std::endl;

    // 查找最近的10个邻居
    int K = 10;
    std::vector<int> pointIdxNKNSearch;
    std::vector<float> pointNKNSquaredDistance;

    std::cout << "搜索最近邻点..." << 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
                      << " (平方距离: " << pointNKNSquaredDistance[i] << ")" << std::endl;
        }
    }

    // 创建PCLVisualizer对象进行可视化
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");

    // 可视化搜索点
    pcl::PointCloud<pcl::PointXYZ>::Ptr searchPointCloud(new pcl::PointCloud<pcl::PointXYZ>());
    searchPointCloud->points.push_back(searchPoint);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> red(searchPointCloud, 255, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(searchPointCloud, red, "search point");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "search point");

    // 可视化最近邻点
    pcl::PointCloud<pcl::PointXYZ>::Ptr neighborCloud(new pcl::PointCloud<pcl::PointXYZ>());
    for (size_t i = 0; i < pointIdxNKNSearch.size(); ++i)
    {
        neighborCloud->points.push_back(cloud->points[pointIdxNKNSearch[i]]);
    }
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> green(neighborCloud, 0, 255, 0);
    viewer->addPointCloud<pcl::PointXYZ>(neighborCloud, green, "neighbor points");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4, "neighbor points");

    // 开始主循环
    while (!viewer->wasStopped())
    {
        viewer->spinOnce(50); // 每50毫秒刷新一次显示,并处理用户事件
    }

    return 0;
}
  • 上述代码首先生成一个随机的点云数据集。
  • 使用OctreePointCloudSearch类创建Octree,并进行最近邻搜索。
  • 通过PCLVisualizer将点云数据、搜索点以及最近邻点可视化。

编译和运行

1)创建一个build目录,并进入该目录:

mkdir build
cd build

2)运行CMake以生成Makefile:

cmake ..

3)编译项目:

make

4)运行可执行文件:

./OctreeNearestNeighbor

看看可视化结果,如下图所示:

红色点表示搜索点,绿色点表示最近邻点,白色点表示原始点云数据。

输出结果信息:

要查找的点: (267.189, 466.127, 142.194)
搜索最近邻点...
    225.61 412.933 142.412 (平方距离: 4558.5)
    257.808 410.918 183.467 (平方距离: 4839.53)
    274 460.999 211.445 (平方距离: 4868.51)
    243.09 540.606 131.491 (平方距离: 6242.47)
    275.009 466.096 229.258 (平方距离: 7641.46)
    240.711 511.06 217.932 (平方距离: 8456.47)
    174.914 476.775 108.244 (平方距离: 9780.69)
    178.43 475.309 188.146 (平方距离: 10074.1)
    203.922 489.412 219.492 (平方距离: 10520)
    343.789 480.705 208.93 (平方距离: 10533.8)

四、Octree案例——半径搜索

半径搜索的代码思路:

  • 首先生成一个随机的点云数据集。
  • 使用OctreePointCloudSearch类创建Octree,并进行半径搜索。
  • 通过PCLVisualizer将点云数据、搜索点以及半径内的邻点可视化。
  • 红色点表示搜索点,绿色点表示半径内的邻点,白色点表示原始点云数据。

核心的代码,如下所示:

#include <pcl/point_cloud.h>
#include <pcl/octree/octree_search.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <iostream>
#include <cstdlib> 
#include <ctime>   

int main()
{
    // 初始化随机数生成器
    std::srand(static_cast<unsigned int>(std::time(0)));

    // 创建一个PointXYZ点云指针并填充点云数据
    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);
    }

    // 定义Octree分辨率并创建Octree对象
    float resolution = 128.0f;
    pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);

    // 设置输入点云并构建Octree
    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);
    // 打印 searchPoint 的值
    std::cout << "Search Point Coordinates: (" 
              << searchPoint.x << ", " 
              << searchPoint.y << ", " 
              << searchPoint.z << ")" << std::endl;

    // 查找给定半径内的所有点
    float radius = 256.0f;
    std::vector<int> pointIdxRadiusSearch;
    std::vector<float> pointRadiusSquaredDistance;

    std::cout << "搜索半径内的邻点..." << 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
                      << " (平方距离: " << pointRadiusSquaredDistance[i] << ")" << std::endl;
        }
    }

    // 创建PCLVisualizer对象进行可视化
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");

    // 可视化搜索点
    pcl::PointCloud<pcl::PointXYZ>::Ptr searchPointCloud(new pcl::PointCloud<pcl::PointXYZ>());
    searchPointCloud->points.push_back(searchPoint);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> red(searchPointCloud, 255, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(searchPointCloud, red, "search point");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "search point");

    // 可视化半径内的邻点
    pcl::PointCloud<pcl::PointXYZ>::Ptr neighborCloud(new pcl::PointCloud<pcl::PointXYZ>());
    for (size_t i = 0; i < pointIdxRadiusSearch.size(); ++i)
    {
        neighborCloud->points.push_back(cloud->points[pointIdxRadiusSearch[i]]);
    }
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> green(neighborCloud, 0, 255, 0);
    viewer->addPointCloud<pcl::PointXYZ>(neighborCloud, green, "neighbor points");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4, "neighbor points");

    // 开始主循环
    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);  // 每100毫秒刷新一次显示,并处理用户事件
    }

    return 0;
}

看看可视化结果,如下图所示:

红色点表示搜索点,绿色点表示最近邻点,白色点表示原始点云数据。

输出结果信息:

Search Point Coordinates: (672.53, 848.881, 756.348)
搜索半径内的邻点...
    563.58 637.83 689.303 (平方距离: 60907.5)
    536.462 790.479 550.964 (平方距离: 64107.5)
    540.754 715.457 739.671 (平方距离: 35445.1)
    500.653 835.636 724.763 (平方距离: 30714.7)
    578.384 833.712 832.827 (平方距离: 14942.7)
    548.618 856.429 914.777 (平方距离: 40510.9)
    527.55 796.035 880.061 (平方距离: 39116.9)
    449.564 904.382 649.582 (平方距离: 64192.9)
    499.839 872.314 616.479 (平方距离: 49934.4)
    487.801 908.75 908.802 (平方距离: 60951.5)
    546.375 1016.81 859.397 (平方距离: 54735.8)
    636.812 604.273 751.585 (平方距离: 61131.5)
    659.817 739.358 680.051 (平方距离: 17978.1)
    652.143 649.493 668.577 (平方距离: 47874.9)
    659.318 757.01 681.943 (平方距离: 14150.9)
    834.803 730.708 613.067 (平方距离: 60826.7)
    777.22 699.299 659.459 (平方距离: 42722.2)
    781.315 696.036 643.011 (平方距离: 48040.7)
    598.679 665.372 752.981 (平方距离: 39140.7)
    620.251 672.501 777.007 (平方距离: 34269.7)
    642.869 713.274 783.507 (平方距离: 20006.7)
    672.395 668.634 835.468 (平方距离: 38748.9)
    671.215 861.606 710.117 (平方距离: 2300.95)
    622.279 757.322 709.815 (平方距离: 13073.6)
    658.69 820.696 730.139 (平方距离: 1672.82)
    604.35 823.723 742.655 (平方距离: 5468.91)
    661.581 819.162 861.857 (平方距离: 12135.4)
    768.492 663.652 782.472 (平方距离: 44201)
    773.832 660.906 725.373 (平方距离: 46555.8)
    723.146 708.955 721.807 (平方距离: 23334.2)
    808.975 731.02 721.804 (平方距离: 33701.7)
    745.785 703.49 901.843 (平方距离: 47673.5)
    778.82 710.248 922.674 (平方距离: 58181.1)
    798.036 736.621 857.316 (平方距离: 38548.7)
    740.366 712.4 891.645 (平方距离: 41534)
    782.45 746.209 816.893 (平方距离: 26289.7)
    764.26 821.114 801.76 (平方距离: 11247.8)
    601.229 914.836 544.503 (平方距离: 54312.4)
    648.524 962.854 701.196 (平方距离: 16607.9)
    653.201 956.799 618.907 (平方距离: 30909.9)
    734.031 892.564 565.114 (平方距离: 42260.7)
    720.409 943.268 534.037 (平方距离: 60623.3)
    758.06 870.037 644.098 (平方距离: 20362.8)
    800.157 963.6 689.941 (平方距离: 33858.9)
    656.089 916.227 956.117 (平方距离: 44713.5)
    674.688 983.219 959.94 (平方距离: 59501.3)
    665.463 919.025 857.996 (平方距离: 15302.6)
    774.945 993.878 800.679 (平方距离: 33478.2)
    789.017 945.61 886.762 (平方距离: 39933.5)
    792.737 1010.02 883.729 (平方距离: 56642.8)
    860.811 843.444 639.952 (平方距离: 49027.2)
    878.83 751.169 714.356 (平方距离: 53870.4)
    855.926 1006.99 694.897 (平方距离: 62407.9)
    920.233 897.31 750.461 (平方距离: 63736.7)
    903.325 913.274 828.116 (平方距离: 62563.3)
    618.377 802.345 964.152 (平方距离: 48280.7)
    705.442 855.34 963.439 (平方距离: 44011.6)
    718.328 888.42 1005 (平方距离: 65489.4)

五、Octree案例——盒子搜索

盒子搜索(Box Search)是一种在 Octree 中查找指定三维范围内所有点的方法。

该方法定义了一个盒子,指定其最小和最大点坐标,然后在这个盒子内查找所有落在该范围内的点。

思路逻辑:

  • 生成了一个包含 1000 个随机点的点云。
  • 使用 OctreePointCloudSearch 创建了一个八叉树,并将点云数据添加到其中。
  • 定义了一个搜索盒子的最小和最大点(minPointmaxPoint),并使用 boxSearch 方法在盒子内查找点。
  • 将搜索到的点打印到控制台。
  • 使用 PCLVisualizer 可视化整个点云和搜索到的点。

核心的代码,如下所示:

#include <pcl/point_cloud.h>
#include <pcl/octree/octree_search.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <Eigen/Dense> // 包含 Eigen 库

int main()
{
    // 初始化随机数生成器
    std::srand(static_cast<unsigned int>(std::time(0)));

    // 创建一个PointXYZ点云指针并填充点云数据
    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);
    }

    // 定义Octree分辨率并创建Octree对象
    float resolution = 128.0f;
    pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);

    // 设置输入点云并构建Octree
    octree.setInputCloud(cloud);
    octree.addPointsFromInputCloud();

    // 定义搜索盒子的边界
    Eigen::Vector3f minPoint;
    Eigen::Vector3f maxPoint;
    minPoint[0] = 256.0f;
    minPoint[1] = 256.0f;
    minPoint[2] = 256.0f;
    maxPoint[0] = 768.0f;
    maxPoint[1] = 768.0f;
    maxPoint[2] = 768.0f;

    // 执行盒子搜索
    std::vector<int> pointIdxBoxSearch;
    if (octree.boxSearch(minPoint, maxPoint, pointIdxBoxSearch) > 0)
    {
        std::cout << "找到 " << pointIdxBoxSearch.size() << " 个点在盒子内:" << std::endl;
        for (size_t i = 0; i < pointIdxBoxSearch.size(); ++i)
        {
            std::cout << "    " << cloud->points[pointIdxBoxSearch[i]].x
                      << " " << cloud->points[pointIdxBoxSearch[i]].y
                      << " " << cloud->points[pointIdxBoxSearch[i]].z << std::endl;
        }
    }
    else
    {
        std::cout << "未找到任何点在盒子内。" << std::endl;
    }

    // 创建PCLVisualizer对象进行可视化
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
    viewer->setBackgroundColor(0, 0, 0);
    viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");

    // 可视化盒子内的点
    pcl::PointCloud<pcl::PointXYZ>::Ptr boxCloud(new pcl::PointCloud<pcl::PointXYZ>());
    for (size_t i = 0; i < pointIdxBoxSearch.size(); ++i)
    {
        boxCloud->points.push_back(cloud->points[pointIdxBoxSearch[i]]);
    }
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> green(boxCloud, 0, 255, 0);
    viewer->addPointCloud<pcl::PointXYZ>(boxCloud, green, "box points");
    viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4, "box points");

    // 开始主循环
    while (!viewer->wasStopped())
    {
        viewer->spinOnce(100);  // 每100毫秒刷新一次显示,并处理用户事件
    }

    return 0;
}

看看可视化结果,如下图所示:

绿色点表示最近邻点,白色点表示原始点云数据。

输出结果信息:

找到 129 个点在盒子内:
    305.738 283.948 366.022
    390.078 263.586 358.944
    498.589 369.01 290.586
    418.491 412.725 313.189
    435.588 318.347 467.745
    404.428 418.288 374.571
    485.747 368.239 418.217

............

六、Octree案例——点云压缩-体素化

 基于 Octree 的体素化(Voxelization)是一种将点云数据划分成均匀大小的立方体(体素)的技术。

每个体素可以包含一个或多个点,通常只保留体素内的一个代表点来简化数据,从而实现降采样和加速后续处理。

思路流程:

  • 生成点云数据:生成一个包含 1000 个随机点的点云数据。
  • 构建 Octree:设置 Octree 的分辨率,并将点云数据添加到 Octree 中。
  • 体素化处理:调用 octree.getOccupiedVoxelCenters 方法获取所有体素的中心点,并将这些点存储在 voxelizedCloud 中。这个方法会返回每个占据的体素的中心点,实现体素化效果。
  • 可视化:使用 PCLVisualizer 同时可视化原始点云和体素化后的点云。原始点云用白色显示,体素化后的点云用绿色显示,以便进行对比。

核心的代码,如下所示:

#include <pcl/point_cloud.h>
#include <pcl/octree/octree_pointcloud.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <iostream>
#include <cstdlib>
#include <ctime>

int main()
{
    // 初始化随机数生成器
    std::srand(static_cast<unsigned int>(std::time(0)));

    // 创建一个PointXYZ点云指针并填充点云数据
    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);
    }

    // 定义Octree分辨率并创建Octree对象
    float resolution = 128.0f;
    pcl::octree::OctreePointCloud<pcl::PointXYZ> octree(resolution);

    // 设置输入点云并构建Octree
    octree.setInputCloud(cloud);
    octree.addPointsFromInputCloud();

    // 体素化处理:获取所有体素的中心点
    pcl::PointCloud<pcl::PointXYZ>::Ptr voxelizedCloud(new pcl::PointCloud<pcl::PointXYZ>());
    pcl::octree::OctreePointCloud<pcl::PointXYZ>::AlignedPointTVector voxelCenters;
    octree.getOccupiedVoxelCenters(voxelCenters);

    for (const auto& voxelCenter : voxelCenters)
    {
        voxelizedCloud->points.push_back(voxelCenter);
    }

    std::cout << "原始点云大小: " << cloud->points.size() << std::endl;
    std::cout << "体素化后点云大小: " << voxelizedCloud->points.size() << std::endl;

    // 创建PCLVisualizer对象进行可视化
    pcl::visualization::PCLVisualizer::Ptr viewerOriginal(new pcl::visualization::PCLVisualizer("Original Cloud Viewer"));
    viewerOriginal->setBackgroundColor(0, 0, 0);

    pcl::visualization::PCLVisualizer::Ptr viewerVoxelized(new pcl::visualization::PCLVisualizer("Voxelized Cloud Viewer"));
    viewerVoxelized->setBackgroundColor(0, 0, 0);

    // 可视化原始点云
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> originalColor(cloud, 255, 255, 255);
    viewerOriginal->addPointCloud<pcl::PointXYZ>(cloud, originalColor, "original cloud");
    viewerOriginal->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original cloud");

    // 可视化体素化后的点云
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> voxelColor(voxelizedCloud, 0, 255, 0);
    viewerVoxelized->addPointCloud<pcl::PointXYZ>(voxelizedCloud, voxelColor, "voxelized cloud");
    viewerVoxelized->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 4, "voxelized cloud");

    // 开始主循环
    while (!viewerOriginal->wasStopped() && !viewerVoxelized->wasStopped())
    {
        viewerOriginal->spinOnce(100);  // 每100毫秒刷新一次显示,并处理用户事件
        viewerVoxelized->spinOnce(100);  // 每100毫秒刷新一次显示,并处理用户事件
    }

    return 0;
}

看看可视化结果,如下图所示:

第一个窗口中显示原始点云,用白色点表示;

第二个窗口中显示体素化后的点云,用绿色点表示;

打印结果信息:

原始点云大小: 1000
体素化后点云大小: 496

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1922664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB - 查询操作符:比较查询、逻辑查询、元素查询、数组查询

文章目录 1. 构造数据2. MongoDB 比较查询操作符1. $eq 等于1.1 等于指定值1.2 嵌入式文档中的字段等于某个值1.3 数组元素等于某个值1.4 数组元素等于数组值 2. $ne 不等于3. $gt 大于3.1 匹配文档字段3.2 根据嵌入式文档字段执行更新 4. $gte 大于等于5. $lt 小于6. $lte 小于…

(Vue+SpringBoot+elementUi+WangEditer)仿论坛项目

项目使用到的技术与库 1.前端 Vue2 elementUi Cookie WangEditer 2.后端 SpringBoot Mybatis-Plus 3.数据库 MySql 一、效果展示 1.1主页效果&#xff1a; 1.2 文章编辑页面&#xff1a; 1.3 成功发布文章 1.4 文章关键字搜索提示 1.5 文章查询结果展示 1.6 文章内容及交互展示…

统信UOS服务器操作系统离线安装postgresql数据库

原文链接&#xff1a;统信UOS服务器离线安装postgresql数据库 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在统信UOS服务器操作系统上离线安装PostgreSQL数据库的文章。PostgreSQL是一款功能强大的开源对象-关系型数据库管理系统。由于某些环境中无法直接访问…

免费开源的工业物联网(IoT)解决方案

什么是 IoT&#xff1f; 物联网 (IoT) 是指由实体设备、车辆、电器和其他实体对象组成的网络&#xff0c;这些实体对象内嵌传感器、软件和网络连接&#xff0c;可以收集和共享数据。 IoT 设备&#xff08;也称为“智能对象”&#xff09;范围广泛&#xff0c;包括智能恒温器等…

SpringBoot+Vue(2)excel后台管理页面

一、需求 SpringBootVue写excel后台管理页面&#xff08;二级页面打开展示每一个excel表&#xff0c;数据库存储字段为“下载、删除、文件详情、是否共享、共享详情”&#xff09; 二、解答 后端(Spring Boot) 1. 项目设置 使用Spring Initializr创建一个新的Spring Boot项目…

深入理解 Elasticsearch 分页技术

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/609576187 Elasticsearch 是一款分布式的搜索引擎&#xff0c;提供了灵活的分页技术。本文主要介绍 Elasticsearch&#xff08;简称 ES&#xff09; 的几种分页技术&#xff0c;并深入分析各种分页技术的优缺点及应用场景。 …

基于AT89C51单片机篮球计时计分器的设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于AT89C51单片机篮球计时计分器的设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 绪论 原理图 ​编辑 仿真图 系统总体设计图 代码实现 系统论文 资源下载 绪论 本次…

内网服务器通过squid代理访问外网

一、背景 现在要对172.16.58.158服务器进行openssh升级操作,我用之前写好的升级脚本执行后,发现没有备份旧的ssh程序文件,然后还卸载了oenssl-devel,然后我发现其他服务器ssh该服务器失败。同时脚本执行时报错“ configure: error: *** zlib.h missing - please install first …

windows查看局域网所有设备ip

windows如何查看局域网所有设备ip 操作方法 一 . 在搜索栏里输入cmd 二 .在命令行黑窗口输入arp -a 三 . 最上面显示的动态地址就是所有设备ip

day20、21、22补卡

235. 二叉搜索树的最近公共祖先 这道题的解题思路&#xff0c;我想了一会都没想出来&#xff0c;看了题解想&#xff1a;对于二叉搜索树&#xff0c;当我们从上向下去递归遍历&#xff0c;第一次遇到 cur节点是数值在[q, p]区间中&#xff0c;那么cur就是 q和p的最近公共祖先。…

Database数据库 vs Data Warehouse数据仓库 vs Data Mart数据集市 vs Data Lake数据湖

1.DATABASE 数据库 数据库是一个结构化的数据集合&#xff0c;用于存储、管理和检索数据。数据库设计用于支持事务处理&#xff08;OLTP&#xff0c;Online Transaction Processing&#xff09;和日常操作。 数据库通常由数据库管理系统&#xff08;DBMS&#xff09;控制&…

webRtc架构与目录结构

整体架构 目录结构 webrtc Modules目录

基于PCIe总线架构的2路1GSPS AD、4路1GSPS DA信号处理平台(100%国产化)

板卡概述 PCIE723-165是基于PCIE总线架构的2通道1GSPS采样率14位分辨率、4通道1GSPS采样率16位分辨率信号处理平台&#xff0c;该板卡采用国产16nm FPGA作为实时处理器&#xff0c;支持2路高速采集以及4路高速数据回放&#xff0c;板载2组DDR4 SDRAM大容量数据缓存&#xff0c;…

宝兰德参编金融智能体标准,深耕大模型场景化落地

随着数智化浪潮的不断推进&#xff0c;人工智能技术正深刻影响着金融服务的模式和流程&#xff0c;金融智能体在大模型的加持下&#xff0c;业务场景的应用能力得到强化。然而&#xff0c;作为新型技术&#xff0c;金融智能体在隐私保护、透明性、数据泄露等方面仍存在诸多风险…

图片存储问题总结

参考博客&#xff1a; https://blog.csdn.net/BUPT_Kwong/article/details/100972964 今天发现图片保存的一个神奇的问题&#xff0c;就是说原始的jpg图片打开后&#xff0c;重新保存成jpg格式&#xff0c;会发现这个结果不是很对的 example from PIL import Image import n…

房屋出租管理系统小程序需求分析及功能介绍

房屋租赁管理系统适用于写字楼、办公楼、厂区、园区、商城、公寓等商办商业不动产的租赁管理及租赁营销&#xff1b;提供资产管理&#xff0c;合同管理&#xff0c;租赁管理&#xff0c; 物业管理&#xff0c;门禁管理等一体化的运营管理平台&#xff0c;提高项目方管理运营效率…

【qt】QTcpSocket相关的信号

QTcpSocket可以在这里找到相关的信号 进行信号槽的关联 connect():这个信号在connectToHost()被调用并且连接已经成功建立之后发出 disconnected():该信号在套接字断开连接时发出 stateChanged(QAbstractSocket::SocketState socketState):每当QAbstractSocket的状态发生变化…

基于Adaboost的数据分类算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于Adaboost的数据分类算法matlab仿真,分别对比线性分类和非线性分类两种方式。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运行 &#xff08;完整程序…

Python - Word转TXT文本,或TXT文本转Word

Word文档&#xff08;.doc或.docx&#xff09;和纯文本文件&#xff08;.txt&#xff09;是两种常用的文件格式。Word文档通常用于复杂的文档处理和排版&#xff0c;而纯文本文件则用于存储和传输纯文本信息。了解如何在这两种格式之间进行转换能提高工作效率&#xff0c;并便于…

Spring Boot中@Async注解的使用及原理 + 常见问题及解决方案

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…