点云的降采样:八叉树采样(Octree)

Octree

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

Octree的用途
八叉树是一种用于描述三维空间的树状数据结构。

Octree的结构:
八叉树的每个节点表示一个正方体的体积元素,每个节点有八个子节点,将八个子节点所表示的体积元素加在一起就等于父节点的体积。
完整的八叉树,每个中间节点都有八个子节点,所有叶节点都有相同的树深度D,叶节点数为8的D次幂
点云的降采样:八叉树采样(Octree)_第1张图片
点云的降采样:八叉树采样(Octree)_第2张图片
点云的降采样:八叉树采样(Octree)_第3张图片

八叉树构建原理如下:
(1). 设定最大递归深度

(2). 找出场景的最大尺寸,并以此尺寸建立第一个立方体

(3). 依序将单位元元素丢入能被包含且没有子节点的立方体

(4). 若没有达到最大递归深度,就进行细分八等份,再将该立方体所装的单位元元素全部分担给八个子立方体

(5). 若发现子立方体所分配到的单位元元素数量不为零且跟父立方体是一样的,则该子立方体停止细分,因为跟据空间分割理论,细分的空间所得到的分配必定较少,若是一样数目,则再怎么切数目还是一样,会造成无穷切割的情形。

(6). 重复3,直到达到最大递归深度。

:构建八叉树的时候,要区分插入的各点云,将各点云进行编号,插入时主要通过递归的方式寻找无数据的child,生成的八叉树可以是是增长式的,即哪里有点云则往哪里插入栅格,无点云的地方不插入栅格。也可以是上图中的固定体素构建形式

空间栅格划分完成之后,去重叠的方法:
一种为只保留体素中心点,将体素中的其他点删除,这样的到的采样点云比较均匀,但在特征的保留上效果不是很好

一种为,计算体素中的点距离体素中心点的长度,然后计算距离的均值,大于(或小于)均值的点即删除,这种方法保留了原始点云数据,但重叠区域的点仍然会比较密集,因此,最好要将法向量、曲率等因素考虑在内,增加细节部分的显示,但还要均衡处理速度

一种为,通过K-Dtree搜索各点近邻的方式来判断各部分点云的密度,然后,遍历各体素,保留体素中密度较大的点云来达到去重叠的效果,但处理后会发现,各点云的交接处会有缝隙出现。解决办法:各交接点云去除时保留不带边缘的点云。最后,会有一个问题待解决,计算点云密度耗时较多。主要耗时部分为构建K-Dtree的时候。另外,这种处理方法会频繁计算点云密度,所以可以考虑以各体素中的点数来判断密度的方式替代。

另外,PCL中的octree框架中,每一个体素都产生了对应的编号,所以在查找体素和查找体素中对应点的时候方便且不会出错。 这一方法的主要优点在于可以非常方便地实现有广泛用途的集合运算(例如可以求两个物体的并、交、差等运算),而这些恰是其它表示方法比较难以处理或者需要耗费许多计算资源的地方。不仅如此,由于这种方法的有序性及分层性,因而对显示精度和速度的平衡、隐线和隐面的消除等,带来了很大的方便,特别有用。

最后,
奉上octree的源码:https://codeload.github.com/brandonpelfrey/SimpleOctree/zip/master
(要不要考虑点个赞!!!!!)

代码的主要部分:

void insert(OctreePoint* point)
{
     
	if(isLeafNode()) 
	{
     
    	    if(data==NULL) {
     
     		data = point;
     		return;
   	     }
   	    else {
     
  	 	OctreePoint *oldPoint = data;
   		data = NULL;
  	 	for(int i=0; i<8; ++i) {
     
   	 	Vec3 newOrigin = origin;
    		newOrigin.x += halfDimension.x * (i&4 ? .5f : -.5f);
   	 	newOrigin.y += halfDimension.y * (i&2 ? .5f : -.5f);
   	 	newOrigin.z += halfDimension.z * (i&1 ? .5f : -.5f);
   	 	children[i] = new Octree(newOrigin, halfDimension*.5f);
  	     }
  	     children[getOctantContainingPoint(oldPoint->getPosition())]->insert(oldPoint);
   	     children[getOctantContainingPoint(point->getPosition())]->insert(point);
         }
	 else {
     
   		int octant = getOctantContainingPoint(point->getPosition());
   		children[octant]->insert(point);
  	 }
}

你可能感兴趣的:(三维点云处理)