八叉树 octree

八叉树

 
八叉树(Octree)的定义是:若不为空树的话,树中任一节点的子节点恰好只会有八个,或零个,也就是子节点不会有0与8以外的数目。那么,这要用来做什么?想象一个 立方体,我们最少可以切成多少个相同等分的小立方体?答案就是8个。再想象我们有一个房间,房间里某个角落藏着一枚金币,我们想很快的把金币找出来,聪明的你会怎么做?我们可以把房间当成一个立方体,先切成八个小立方体,然后排除掉没有放任何东西的小立方体,再把有可能藏金币的小立方体继续切八等份….如此下去,平均在Log8(房间内的所有物品数)的时间内就可找到金币。因此,八叉树就是用在3D空间中的场景管理,可以很快地知道物体在3D场景中的位置,或侦测与其它物体是否有碰撞以及是否在可视范围内。
中文名
八叉树
外文名
Octree
概    念
用于描述三维空间的树状 数据结构
释    义
藏金币的小立方体继续切八等份

简介

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

实现原理

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

存贮结构

编辑
本例中八叉树的存贮结构用一个有(若干+八)个字段的记录来表示树中的每个结点。其中若干字段用来描述该结点的特性(本例中的特性为:节点的值和节点坐标),其余的八个字段用来作为存放指向其八个子结点的指针。此外,还有线性存储和1托8式存储。

叉树对比

编辑
a) BSP树将场景分割为1个面,而八叉树分割为3个面。
b) BSP树每个节点最多有2个子结点,而八叉树最多有8个子结点
因此BSP树可以用在任意维度的场景中,而八叉树则常用于 三维空间场。
点云压缩:
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/openni_grabber.h>
#include <pcl/visualization/cloud_viewer.h>
#include <pcl/compression/octree_pointcloud_compression.h>
#include <stdio.h>
#include <sstream>
#include <stdlib.h>

#ifdef WIN32
# define sleep(x) Sleep((x)*1000)
#endif

class SimpleOpenNIViewer
{//创建一个实例 
public:
SimpleOpenNIViewer () :
viewer (" Point Cloud Compression Example")
  {
  }

void
cloud_cb_ (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr& cloud)
  {
if (!viewer.wasStopped ())
    {
// 存储压缩点云的字节流
std::stringstream compressedData;
// 输出点云
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudOut (new pcl::PointCloud<pcl::PointXYZRGBA> ());
// 压缩点云
PointCloudEncoder->encodePointCloud (cloud, compressedData);
// 解压缩点云
PointCloudDecoder->decodePointCloud (compressedData, cloudOut);
//可视化解压缩点云
viewer.showCloud (cloudOut);
    }
  }

void
run ()
  {
bool showStatistics=true;//设置在标准设备上面打印出压缩结果信息 
// 压缩选项详见 /io/include/pcl/compression/compression_profiles.h
pcl::octree::compression_Profiles_e compressionProfile=pcl::octree::MED_RES_ONLINE_COMPRESSION_WITH_COLOR;
// 初始化压缩与解压缩对象
PointCloudEncoder=new pcl::octree::PointCloudCompression<pcl::PointXYZRGBA> (compressionProfile, showStatistics);
PointCloudDecoder=new pcl::octree::PointCloudCompression<pcl::PointXYZRGBA> ();
//创建从 OpenNI获取点云的对象
pcl::Grabber* interface =new pcl::OpenNIGrabber ();
//建立回调函数
    boost::function<void
   (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr&)> f = boost::bind (&SimpleOpenNIViewer::cloud_cb_, this, _1);
// 建立回调函数与回调信号之间联系
boost::signals2::connection c = interface->registerCallback (f);
// 开始接收点云数据流
interface->start ();
while (!viewer.wasStopped ())
    {
sleep (1);
    }
interface->stop ();
// 删除点云压缩与解压缩对象实例
delete (PointCloudEncoder);
delete (PointCloudDecoder);
  }
pcl::visualization::CloudViewer viewer;
pcl::octree::PointCloudCompression<pcl::PointXYZRGBA>*PointCloudEncoder;
pcl::octree::PointCloudCompression<pcl::PointXYZRGBA>*PointCloudDecoder;
};

int
main (int argc, char**argv)
{
SimpleOpenNIViewer v;
v.run ();
return (0);
}	
octree空间划分以及搜索:
#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>
#include <iostream>
#include <vector>
#include <ctime>

int
main (int argc, char**argv)
{
srand ((unsigned int) time (NULL));
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);
  }
float resolution =128.0f;//设置分辨率进行初始化,用叶节点存放索引向量 
pcl::octree::OctreePointCloudSearch<pcl::PointXYZ> octree(resolution);
octree.setInputCloud (cloud);//设置输入点云 
octree.addPointsFromInputCloud ();//创建一个octree实例
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::vector<int>pointIdxVec;
if (octree.voxelSearch (searchPoint, pointIdxVec))
  {
std::cout<<"Neighbors within voxel search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z<<")"
<<std::endl;
for (size_t i=0; i<pointIdxVec.size (); ++i)
std::cout<<"    "<< cloud->points[pointIdxVec[i]].x 
<<" "<< cloud->points[pointIdxVec[i]].y 
<<" "<< cloud->points[pointIdxVec[i]].z <<std::endl;
  }
//K近邻搜索
int K =10;
std::vector<int>pointIdxNKNSearch;
std::vector<float>pointNKNSquaredDistance;
std::cout<<"K nearest neighbor search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with K="<< K <<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 
<<" (squared distance: "<<pointNKNSquaredDistance[i] <<")"<<std::endl;
  }
//半径内近邻搜索
std::vector<int>pointIdxRadiusSearch;
std::vector<float>pointRadiusSquaredDistance;
float radius =256.0f* rand () / (RAND_MAX +1.0f);
std::cout<<"Neighbors within radius search at ("<<searchPoint.x
<<" "<<searchPoint.y
<<" "<<searchPoint.z
<<") with radius="<< radius <<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 
<<" (squared distance: "<<pointRadiusSquaredDistance[i] <<")"<<std::endl;
  }
}

点云数据空间变化检测:
#include <pcl/point_cloud.h>
#include <pcl/octree/octree.h>
#include <iostream>
#include <vector>
#include <ctime>
	
int main (intargc, char**argv)
{
srand ((unsignedint) time (NULL));
// 八叉树分辨率 即体素的大小
float resolution =32.0f;
// 初始化空间变化检测对象
pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZ>octree (resolution);
pcl::PointCloud<pcl::PointXYZ>::PtrcloudA (newpcl::PointCloud<pcl::PointXYZ> );
//为cloudA创建点云
cloudA->width =128;
cloudA->height =1;
cloudA->points.resize (cloudA->width *cloudA->height);
for (size_ti=0; i<cloudA->points.size (); ++i)
  {
cloudA->points[i].x =64.0f* rand () / (RAND_MAX +1.0f);
cloudA->points[i].y =64.0f* rand () / (RAND_MAX +1.0f);
cloudA->points[i].z =64.0f* rand () / (RAND_MAX +1.0f);
  }
//添加点云到八叉树,建立八叉树
octree.setInputCloud (cloudA);
octree.addPointsFromInputCloud ();
// 交换八叉树缓存,但是cloudA对应的八叉树仍在内存中
octree.switchBuffers ();
pcl::PointCloud<pcl::PointXYZ>::PtrcloudB (new pcl::PointCloud<pcl::PointXYZ> );
// 为cloudB创建点云
cloudB->width =128;
cloudB->height =1;
cloudB->points.resize (cloudB->width *cloudB->height);
for (size_ti=0; i<cloudB->points.size (); ++i)
  {
cloudB->points[i].x =64.0f* rand () / (RAND_MAX +1.0f);
cloudB->points[i].y =64.0f* rand () / (RAND_MAX +1.0f);
cloudB->points[i].z =64.0f* rand () / (RAND_MAX +1.0f);
  }
//添加 cloudB到八叉树
octree.setInputCloud (cloudB);
octree.addPointsFromInputCloud ();
std::vector<int>newPointIdxVector;
//获取前一cloudA对应的八叉树在cloudB对应八叉树中没有的体素
octree.getPointIndicesFromNewVoxels (newPointIdxVector);
//打印输出点
std::cout<<"Output from getPointIndicesFromNewVoxels:"<<std::endl;
for (size_ti=0; i<newPointIdxVector.size (); ++i)
std::cout<<i<<"# Index:"<<newPointIdxVector[i]
<<"  Point:"<<cloudB->points[newPointIdxVector[i]].x <<" "
<<cloudB->points[newPointIdxVector[i]].y <<" "
<<cloudB->points[newPointIdxVector[i]].z <<std::endl;
}



建立空间索引在点云数据处理中有着广泛的应用,常见的空间索引一般 是自顶而下逐级划分空间的各种空间索引结构,比较有代表性的包括BSP树,KD树,KDB树,R树,四叉树,八叉树等索引结构,而这些结构中,KD树和八叉树使用比较广泛

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AIGC Studio

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

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

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

打赏作者

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

抵扣说明:

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

余额充值