点云空间数据组织——八叉树

01基本逻辑
用八叉树来表示三维形体,并研究这种表示下的各种操作以及应用,是进入80年代后开展起来的一种方法。这种方法,既可以看成是四叉树方法在三维空间的推广,也可以认为是三维体素阵列表示形体方法的一种改进。 假设要表示的形体V可以放在一个充分大的正方体C中,C的边长为2n。
   八叉树的每一个节点与C的一个子立方体对应 ,树根与C本身相对应。如果V=C,那么V的八叉树只有树根。如果V不等于C,则将C等分为8个子立方体,每个子立方体与树根的一个子节点相对应。只要某个子立方体不是完全空白,或者完全为V所占据,就要被八等分,从而对应的节点也就有了八个子节点。这样的递归判断,分割一直要进行到节点所对应的立方体完全为空,或是完全为V所占据,或是其大小已经是预先定义的体素大小,并且对他与V之交作一定的“舍入”,使体素或认为是空白的,或认为是V占据的。
   在这里插入图片描述
02CloudCompare中的八叉树
CloudCompare中使用的DgmOctree作为八叉树数据结构,具体实现过程如下:
在这里插入图片描述
八叉树将空间分割成八块,根据2进制,3位2进制即可表示8个数字,3位中的顺序:zyx顺序区分,从小递增到大,如:011,即z为0,x为1,y为 1的块位置。所以64位操作系统最多可分割为64/3 = 21级,32位操作系统最多可分割为32/3 = 10级。
1、建立单维索引(CellCode)
将原有的1位2进制转为3位,如:001(1)转为000 000 001,010(2) 转为 000 001 000,以此类推。另外两个维度的code分别左移1位和2位即可。软件初始化时就计算完了所有CellCode。

//we compute all possible values for cell codes
    //(along a unique dimension, the other ones are just shifted)
    for (int value = 0; value < VALUE_COUNT; ++value)
    {
      int mask = VALUE_COUNT;//(1 << MAX_OCTREE_LEVEL)
      CCLib::DgmOctree::CellCode code = 0;
      for (unsigned char k = 0; k < CCLib::DgmOctree::MAX_OCTREE_LEVEL; k++)
      {
        mask >>= 1;
        code <<= 3;
        if (value & mask)
        {
          code |= 1;
        }
      }
      values[value] = code;
    }

2、判断点所在位置(CellPos)
会默认计算最高层级下的位置,按照矩形索引的方式推算到3维,其他层级的pos,则计算当前点在最高层级下的pos,然后右移(最高层级-当前层级)位。


const PointCoordinateType& cs = getCellSize(MAX_OCTREE_LEVEL);
    //DGM: if we admit that cs >= 0, then the 'floor' operator is useless (int cast = truncation)
    cellPos.x = static_cast<int>(/*floor*/(thePoint->x - m_dimMin.x)/cs);
    cellPos.y = static_cast<int>(/*floor*/(thePoint->y - m_dimMin.y)/cs);
    cellPos.z = static_cast<int>(/*floor*/(thePoint->z - m_dimMin.z)/cs)

3、CellPos与CellCode的转换
CellPos先左移(最高层级-当前层级)位,再将y左移1位,z左移2位,对xyz取或处理,然后右移3*(最高层级-当前层级)位。


//最高层级-当前层级
const unsigned char dec = MAX_OCTREE_LEVEL - level;
//PRE_COMPUTED_POS_CODES 为软件初始化时建立的单维CellCode
CellCode code = (PRE_COMPUTED_POS_CODES.values[cellPos.x << dec]
              | (PRE_COMPUTED_POS_CODES.values[cellPos.y << dec] << 1)
              | (PRE_COMPUTED_POS_CODES.values[cellPos.z << dec] << 2))
              >> (3 * (MAX_OCTREE_LEVEL - level));

4、CloudCompare中八叉树的应用(查找给定立方体内的点)
a.计算出给定立方体的最大角点和最小角点
b.判断最大角点和最小角点所在给定层级的Pos
c.判断当前层级是否有在给定立方体相交的pos
d.循环所有pos中的点
e.判断点是否在立方体内
在这里插入图片描述

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一份简单的 Python 代码,用于对点云的 las 文件构建八叉树索引: ```python import laspy import numpy as np from octree import Octree # 使用自己实现的 Octree 类 # 读取 las 文件 inFile = laspy.file.File("input.las", mode="r") # 将点云数据转换为 Numpy 数组 points = np.vstack((inFile.x, inFile.y, inFile.z)).transpose() # 构建八叉树 octree = Octree(points) # 将索引写入 las 文件的新属性中 outFile = laspy.file.File("output.las", mode="w", header=inFile.header) outFile.define_new_dimension(name="octree_index", data_type=5, description="Octree index") for i in range(len(points)): outFile.set_point(i, inFile.x[i], inFile.y[i], inFile.z[i], octree.get_index(points[i])) outFile.close() ``` 其中,`Octree` 类需要自己实现。你可以参考以下代码: ```python class Octree: def __init__(self, points, max_depth=8, min_points=10): self.max_depth = max_depth self.min_points = min_points self.root = OctreeNode(points, 0, max_depth, min_points) def get_index(self, point): return self.root.get_index(point) def get_leaf_nodes(self): return self.root.get_leaf_nodes() class OctreeNode: def __init__(self, points, depth, max_depth, min_points): self.bbox = BoundingBox(points) self.points = points self.depth = depth self.max_depth = max_depth self.min_points = min_points self.is_leaf = True self.children = None self.index = None if len(points) > min_points and depth < max_depth: self.split() def split(self): self.is_leaf = False self.children = [] for i in range(8): child_points = self.bbox.get_child_bbox(i).get_points_inside_bbox(self.points) if len(child_points) > 0: self.children.append(OctreeNode(child_points, self.depth+1, self.max_depth, self.min_points)) def get_index(self, point): if self.is_leaf: if self.index is None: self.index = self.bbox.get_index(point) return self.index else: for child in self.children: if child.bbox.contains_point(point): return child.get_index(point) def get_leaf_nodes(self): if self.is_leaf: return [self] else: leaf_nodes = [] for child in self.children: leaf_nodes += child.get_leaf_nodes() return leaf_nodes class BoundingBox: def __init__(self, points): self.min_xyz = np.min(points, axis=0) self.max_xyz = np.max(points, axis=0) self.center = (self.min_xyz + self.max_xyz) / 2 def contains_point(self, point): return all(self.min_xyz <= point) and all(point <= self.max_xyz) def get_child_bbox(self, index): center = self.center half_size = (self.max_xyz - self.min_xyz) / 2 if index == 0: return BoundingBox(np.array([center, center + half_size])) elif index == 1: return BoundingBox(np.array([center + np.array([half_size[0], 0, 0]), center + np.array([half_size[0]*2, half_size[1], half_size[2]])])) elif index == 2: return BoundingBox(np.array([center + np.array([0, half_size[1], 0]), center + np.array([half_size[0], half_size[1]*2, half_size[2]])])) elif index == 3: return BoundingBox(np.array([center + np.array([half_size[0], half_size[1], 0]), center + np.array([half_size[0]*2, half_size[1]*2, half_size[2]])])) elif index == 4: return BoundingBox(np.array([center + np.array([0, 0, half_size[2]]), center + np.array([half_size[0], half_size[1], half_size[2]*2])])) elif index == 5: return BoundingBox(np.array([center + np.array([half_size[0], 0, half_size[2]]), center + np.array([half_size[0]*2, half_size[1], half_size[2]*2])])) elif index == 6: return BoundingBox(np.array([center + np.array([0, half_size[1], half_size[2]]), center + np.array([half_size[0], half_size[1]*2, half_size[2]*2])])) elif index == 7: return BoundingBox(np.array([center + half_size, center + half_size*2])) def get_points_inside_bbox(self, points): return points[(points[:,0] >= self.min_xyz[0]) & (points[:,0] <= self.max_xyz[0]) & (points[:,1] >= self.min_xyz[1]) & (points[:,1] <= self.max_xyz[1]) & (points[:,2] >= self.min_xyz[2]) & (points[:,2] <= self.max_xyz[2])] def get_index(self, point): index = 0 if point[0] > self.center[0]: index |= 1 if point[1] > self.center[1]: index |= 2 if point[2] > self.center[2]: index |= 4 return index ``` 这份代码中,我们首先使用 `laspy` 库读取点云 las 文件,然后将点云数据转换为 Numpy 数组。接下来,我们使用 `Octree` 类来构建八叉树,并将每个点的索引写入 las 文件的新属性中。最后,将新的 las 文件保存到磁盘上。 注意,这份代码中的 `Octree` 类实现可能并不完美,你可以根据自己的需求进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值