前言
Octree 作为一种高效的空间分割数据结构,具有重要的应用价值。
本文将深入分析 Octree 的原理,通过多个实际案例帮助读者全面理解其功能和应用,包括最近邻搜索、半径搜索、盒子搜索以及点云压缩(体素化)。
特性 | 近邻搜索 | 半径搜索 | 盒子搜索 | 点云压缩(体素化) |
---|---|---|---|---|
描述 | 查找距离给定点最近的一个或多个点 | 查找给定点一定半径范围内的所有点 | 查找给定空间盒子内的所有点 | 将点云数据划分为均匀大小的立方体(体素) |
输入参数 | 目标点,近邻数量 | 目标点,半径 | 盒子的最小点和最大点 | 分辨率 |
输出 | 最近的一个或多个点的索引及距离 | 半径范围内所有点的索引及距离 | 盒子内所有点的索引 | 每个体素的中心点 |
适用场景 | 最近点查询,碰撞检测 | 局部邻域分析,聚类 | 空间范围查询,目标检测 | 数据降采样,减少计算复杂度 |
优点 | 精确查找最近点,效率高 | 查找范围可调节,适用于局部分析 | 可以查找任意形状的空间范围内的点 | 大幅度减少数据量,保留数据空间结构 |
缺点 | 仅限于查找最近的点,无法指定查找范围 | 结果集大小随半径变化,计算量可能较大 | 需要预先定义盒子范围,结果集大小不确定 | 可能丢失部分细节信息 |
实现方法 | octree.nearestKSearch | octree.radiusSearch | octree.boxSearch | octree.getOccupiedVoxelCenters |
此外,本文还提供了详细的源代码示例,便于读者实践和应用。
希望通过本文的学习,读者能够掌握 Octree 的基本原理及其在点云数据处理中的具体实现方法。
看一下示例效果:
红色点表示搜索点,绿色点表示最近邻点,白色点表示原始点云数据。
一、Octree原理分析
Octree是一种用于分层分割三维空间的数据结构。它是将三维空间递归地划分成更小的立方体区域的树形结构。
每个节点代表一个空间区域,该区域可以进一步细分为八个子区域。
Octree的原理是基于递归地将三维空间,分割成更小的立方体区域,从而形成一棵树形结构。
这种结构能够有效地表示和操作三维空间中的数据,如下图所示:
特点:
- 分层结构:Octree的每个节点对应一个立方体区域,当需要更详细的信息时,这些区域可以进一步划分成八个子区域。
- 递归分割:通过递归地将空间划分为更小的部分,Octree可以有效地表示三维空间中的数据。
- 适应性强:Octree可以灵活地适应不同密度的数据区域,在数据稀疏的区域使用较少的节点,而在数据密集的区域使用更多的节点。
用途:
- 三维计算机图形学:在图形渲染中,Octree用于加速光线追踪算法,通过快速确定光线与物体的交点。
- 碰撞检测:在物理模拟和游戏开发中,Octree用于高效的碰撞检测,特别是当物体分布在三维空间中的时候。
- 空间索引:在三维空间数据管理中,Octree用于空间索引和快速查询,例如在地理信息系统(GIS)中。
- 点云处理:在处理和表示三维扫描数据时,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 创建了一个八叉树,并将点云数据添加到其中。
- 定义了一个搜索盒子的最小和最大点(
minPoint
和maxPoint
),并使用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