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

前言

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.1octree.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.1octree.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);// 设置输入点云并构建Octreeoctree.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);// 设置输入点云并构建Octreeoctree.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);// 设置输入点云并构建Octreeoctree.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);// 设置输入点云并构建Octreeoctree.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.mzph.cn/diannao/45550.shtml

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

相关文章

搞懂负载均衡,零基础也可以!

本文作者&#xff1a;程序员鱼皮 免费编程学习 - 编程导航网&#xff1a;https://www.code-nav.cn 大家好&#xff0c;我是鱼皮。 周末在家写代码&#xff0c;无意中跟朋友提了下 LB&#xff0c;还说 LB 好的呱呱叫。 朋友笑了笑&#xff0c;问我 LB 是谁&#xff1f; 我解释…

加密软件|让数据传输更安全

加密软件在当今数字化时代扮演着至关重要的角色&#xff0c;它们通过先进的加密算法和技术&#xff0c;确保数据在存储、传输和分享过程中的安全性&#xff0c;从而保护个人隐私和企业机密。一、加密软件的基本作用数据加密&#xff1a;加密软件通过应用复杂的加密算法&#xf…

Python转换PDF为PowerPoint演示文件

PDF文件以其跨平台兼容性和版面固定性成为了分享和存储文档资料的首选格式。然而&#xff0c;在需要进行生动、互动性强的演示时&#xff0c;PDF的静态特性便难以满足个性化演示需求。将PDF文件转换为PowerPoint演示文稿可以解决这一问题。PowerPoint不仅提供了丰富的动画和过渡…

网关设备BL122实现Modbus RTU/TCP转Profinet协议

Modbus与Profinet是两种广泛应用于工业自动化领域的通信协议&#xff1a;Modbus因其简单性和兼容性&#xff0c;在许多工业设备中得到广泛应用&#xff1b;而Profinet提供了高速、高精度的通信能力&#xff0c;适合于复杂控制系统和实时应用&#xff0c;但两者之间的差异导致了…

uniapp easycom组件冲突

提示信息 ​easycom组件冲突&#xff1a;[/components/uni-icons/uni-icons.vue,/uni_modules/uni-icons/components/uni-icons/uni-icons.vue]​ 问题描述 老项目&#xff0c;在uniapp插件商城导入了一个新的uniapp官方开发的组件》uni-data-picker 数据驱动的picker选择器 …

测试人必会 K8S 操作之 Dashboard

在云计算和微服务架构的时代&#xff0c;Kubernetes (K8S) 已成为管理容器化应用的标准。然而&#xff0c;对于许多新手来说&#xff0c;K8S 的操作和管理常常显得复杂而神秘。特别是&#xff0c;当你第一次接触 K8S Dashboard 时&#xff0c;你是否也感到有些无所适从&#xf…

【Python】一文详细向您介绍 scipy.cluster.vq.kmeans() 的原理、常见用法和使用场景举例等

【Python】一文详细向您介绍 scipy.cluster.vq.kmeans() 的原理、常见用法和使用场景举例等 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; …

python简单学习笔记

1. print 输出 print(2024, 年&#xff0c;我要想娘) # sep:设置打印多个内容的分隔符&#xff0c;默认值为空格 print(2024, 年&#xff0c;我要想娘, sep, end\n) # end:设置print执行结束后的操作&#xff0c;默认值为换行格式化输出 print(格式化字符串 % (变量1, 变量…

org.springframework.jdbc.BadSqlGrammarException异常

Bug 记录 概述 在执行定时任务更新电子书统计信息时&#xff0c;遇到了 org.springframework.jdbc.BadSqlGrammarException 异常&#xff0c;具体表现为 SQL 函数 count 被错误地解析为自定义函数 wiki.count&#xff0c;导致数据库更新操作失败。 详细描述 错误信息&#x…

计算机视觉之SSD目标检测

模型简介 SSD是一种单阶段目标检测算法&#xff0c;通过卷积神经网络进行特征提取&#xff0c;并在不同的特征层进行检测输出&#xff0c;实现多尺度检测。它采用了anchor的策略&#xff0c;预设不同长宽比例的anchor&#xff0c;并在每个输出特征层上预测多个检测框。SSD框架…

C#变量、常量与运算符

文章目录 变量变量定义命名规则作用域和生命周期 常量特殊字符常量 运算符算术运算符关系运算符逻辑运算符位运算符赋值运算符其他运算符 变量 变量就是一个存储空间的名字&#xff0c;变量是什么类型&#xff0c;这个空间里面存储的就是什么类型的数据。 变量定义 <data_t…

什么是面向对象编程

什么是面向对象编程&#xff1f;&#xff08;OOP&#xff09; ● 面向对象编程是一种基于对象概念的编程范式&#xff1b;&#xff08;所谓的编程范式&#xff0c;就是代码风格&#xff0c;我们“如何”编写和组织代码&#xff09;&#xff1b; ● 我们使用对象来模拟&#xf…

如何制作文件的二维码?支持设置文件下载功能

现在为了能够更快的将文件分享给其他人查看&#xff0c;会选择将文件转换二维码的方式&#xff0c;用户可以通过扫描二维码在手机上预览或者下载文件到本地&#xff0c;能够有效的减少文件对内存的占用&#xff0c;在很多场景下都有所应用&#xff0c;那么文件转二维码具体该怎…

LabVIEW电子水泵性能测试平台

开发了一种车用电子水泵性能测试平台&#xff0c;该平台以工控机为载体&#xff0c;利用LabVIEW开发上位机软件&#xff0c;采用PLC控制阀门和水泵等电气元件&#xff0c;通过RS485进行数据采集并传输到上位机。通过上位机与下位机的协同控制&#xff0c;实现了数据交互处理和性…

基于Java Web的考编论坛网站的设计与实现+lw+源码+讲解+调试+视频演示

第3章 系统分析 用户的需求以及与本系统相似的在市场上存在的其它系统可以作为系统分析中参考的资料&#xff0c;分析人员可以根据这些信息确定出本系统具备的功能&#xff0c;分析出本系统具备的性能等内容。 3.1可行性分析 尽管系统是根据用户的要求进行制作&#xff0c;但…

聚鼎贸易:装饰画生意能不能做起来2024

在2024年&#xff0c;随着人们对家居美学和个性化表达的需求日益增长&#xff0c;装饰画作为一种简单而直接的方式&#xff0c;来提升空间品味及展示个人风格的市场需求也随之扩大。许多人都对涉足这一行业抱有浓厚的兴趣&#xff0c;但究竟这门生意能否成功做大&#xff0c;需…

Unity 资源 之 战斗魔法咒语 - 第二卷(Combat Magic Spells - Volume II)

&#x1f389;Unity Asset Store 宝藏免费资源&#xff1a;战斗魔法咒语 - 第二卷 前言资源包内容领取兑换码 前言 各位游戏开发爱好者、Unity 开发者们&#xff0c;今天要给大家带来一个超棒的消息&#xff01;在 Unity Asset Store 上&#xff0c;全新的免费资源“战斗魔法咒…

Java高级重点知识点-27-Java反射机制

文章目录 Java反射机制概述理解Class类并获取Class的实例Class 类 类的加载与ClassLoader类加载过程类的初始化触发条件类加载器的作用ClassLoader 创建运行时类的对象获取运行时类的完整结构调用运行时类的指定结构反射的应用&#xff1a;动态代理Proxy类AOP&#xff08;Aspec…

JavaWeb系列二十一: 数据交换和异步请求(JSON, Ajax)

文章目录 官方文档official documents官方文件官方文件official documentsJSON介绍JSON快速入门JSON对象和字符串对象转换应用案例注意事项和细节 JSON在java中使用说明JSON在Java中应用场景应用实例 Ajax基本介绍Ajax是什么Ajax经典应用场景 Ajax原理示意图传统的web应用Ajax原…

Vue3打包发布,刷新出现的空白页面和错误

Vue3打包发布出现的错误&#xff1a;Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of text/html. Strict MIME type checking is enforced for module scripts per HTML spec. 第一次点击访问到这个路径&…