基于四叉树构建金字塔数据

三维GIS渲染引擎中的关键技术在于海量数据的加载及高效渲染。

众所周知,在三维地图显示中,三维GIS渲染引擎的渲染目标是影像数据、高程数据、矢量数据、光照数据及外部标准的地理数据,通过对这些数据的加载、编译、渲染实现的三维地图。要想实现高精度的三维地图显示,则必须提供足够多的数据支撑,这便带来了两个问题:

其一,当三维渲染数据提供的足够精细,那么需要加载到程序中的数据量将会成倍数增加,那么在程序运行过程中,不光要对当前已有的数据进行渲染还要抽取出部分时间对数据进行加载操作,这势必会影响到程序运行的流畅度。

其二,假如第一个问题我们已经解决了,在程序运行过程中,数据的加载效率很高,这样在渲染高清地形时加载到场景中的数据便会非常的大,此时又会对程序的运行过程中数据的渲染带来很大的负担,同样会影响程序的运行流畅度。

多分辨率的TIN不具备简单的跨越式空间结构,在执行诸如视景体裁减、碰撞检测等操作时显得更为复杂,并且对TIN的压缩算法也比较复杂,不利于数据的快速访问。

采用“分层分块”策略的瓦片金字塔不但大大缩短了数据访问时间而且能够表示多分辨率数据,是海量地形数据组织的理想结构,通过与树结构的结合,具备了快速数据索引的功能,从而形成了大范围空间数据组织方式——瓦片四叉树。采用四叉树描述的瓦拓扑关系简单,其索引只涉及加、减、加倍和减半操作,可以通过效率较高的位运算实现。

图 1‑1四叉树组织示意图

基于经纬的球面网格的瓦片的数据采用经纬度加高程描述,符合人们的习惯,也比较直观,而在显示时,则要转换为空间直角坐标系。

  1. 四叉树(Q-Tree)的原理

四叉树或四元树也被称为Q树(Q-Tree)。四叉树被广泛应用于图像处理、空间数据索引、2D中的快速碰撞检测、存储稀疏数据等。

四叉树(Q-Tree)是一种树形数据结构。四叉树(Q-Tree)的定义是:它的每个节点下至多可以有四个子节点,通常把一部分二维空间细分为四个象限或区域并把该区域里的相关信息存入到四叉树节点中。这个区域可以是正方形、矩形或是任意形状。以下为四叉树的二维空间结构(左)和存储结构(右)示意图。

图 1‑2金字塔结构

四叉树的每一个节点代表一个矩形区域(如上图黑色的根节点代表最外围黑色边框的矩形区域),每一个矩形区域又可划分为四个小矩形区域,这四个小矩形区域作为四个子节点所代表的矩形区域。

  1. 四叉树构建

利用四叉树分网格,如本文的第一张图<四层完全四叉树结构示意图>,根据左图的网格图形建立如右图所示的完全四叉树。假设在一个矩形区域里有N个对象,如下左图一个黑点代表一个对象,每个对象的坐标位置都是已知的,用四叉树的一个节点存储一个对象,构建成如下右图所示的四叉树。

图 1‑3四叉树构建过程

方法也是采用递归的方法对该矩形进行划分分区块,分完后再往里分,直到每一个子矩形区域里只包含一个对象为止。

  1. 四叉树查找

用四叉树查找某一对象主要有如下两种方法:采用盲目搜索,与二叉树的递归遍历类似,可采用后序遍历或前序遍历或中序遍历对其进行搜索某一对象,时间复杂度为O(n);根据对象在区域里的位置来搜索,采用分而治之思想,时间复杂度只与四叉树的深度有关。比起盲目搜索,这种搜索在区域里的对象越多时效果越明显。

图 1‑4四叉树查找示意

  1. 数据的金字塔格式设计

目前流行的虚拟地球软件的数据组织方式,均采用瓦片四叉树结构。为了实现以上关键技术难点,本引擎提供的解决方案如下:

在三维GIS渲染引擎中,数据的格式通常会有两种:平顶数据格式和尖顶数据格式,数据的管理使用树结构。

地理信息数据包括影像、高程、光照等数据都是使用树的机制进行管理的,在保证所有的数据的大小都是相同的同时,将低级别的数据在下一个高级别处将其一分为四,这四个数据的分辨率与低级别的数据的分辨率相同,这样便能保证在不断加载高级别数据的时候使得数据的精度更高。

图 1‑5金字塔多层结构

如上图所示,三维GIS渲染引擎渲染的地图中的世界范围是以经纬度来表示,若将球形表面剖开铺成平面,其最左为西经180度,最右为东经180度,最上为北纬90度,最下为南纬90度。每向下深入一层,每一个瓦片便会等分成四块。每一个瓦片均有一个TileKey进行管理,上一层的TileKey会有下一层的4个子key,由此体现了四叉树组织的思想。

平顶数据格式和尖顶数据格式的区别只是第一层中数据的数量,在平顶数据格式中,数据的第一层包含了两个数据片源包括一个经度从-180~0、纬度从-90~90的一个片源和一个经度从0~180、纬度从-90~90的一个片源,如上图的数据格式;而尖顶数据格式中,数据的第一层则只包含了一个数据片源是经度从-180~180、纬度从-90~90的片源。

在三维GIS渲染引擎初始化时便会为该引擎默认分配一种数据格式的属性,这个属性中必包含了数据的格式表明引擎的数据格式为尖顶或者平顶数据。假如当前的引擎的数据格式为尖顶数据,但是加载的图层数据可以为两种中的任意一种,如果加载的图层数据为平顶格式的数据,则分析如下:

  1. 当三维GIS引擎请求第一层数据时,其需要的是(-180,180)(-90,90)的数据。
  2. 去请求每一个图层的数据,当这个图层的数据为平顶结构时,是不存在单张(-180,180)(-90,90)这个范围内的数据。
  3. 此时三维GIS引擎会根据图层和引擎的数据格式的不同而进行一些计算,从而计算出在图层中存在两个数据(-180,0)(-90,90)和(0,180)(-90,90),把这两个数据拼在一起正好构成(-180,180)(-90,90)。
  4. 读取(-180,0)(-90,90)和(0,180)(-90,90)两个片源数据,然后将读取结果平凑到一起,从而形成引擎需要的数据。
  5. 进行渲染。

但是如果加载的数据格式与引擎的数据格式同为尖顶格式的数据时,则分析如下:

  1. 当三维GIS引擎请求第一层数据时,其需要的是(-180,180)(-90,90)的数据;
  2. 去请求每一个图层的数据,当这个图层的数据为尖顶结构时,存在(-180,180)(-90,90)这个范围内的数据;
  3. 读取(-180,180)(-90,90)这个数据;
  4. 进行渲染。

对比两个步骤发现第二种情况节省了不同数据格式之间的计算时间以及读取一个文件的IO操作时间。通过这种将图层的数据格式与三维GIS引擎数据格式相一致的方法,节约了计算时间和一部分文件读取的时间,从而使加载的效率更加的高效。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
ORB算法中的四叉树均匀化是为了保证提取到的特征点在图像中均匀分布,避免在某些区域密集提取特征点,而在其他区域没有特征点的情况。下面是ORBSLAM2中的四叉树均匀化的代码实现,供您参考: ```c++ void ORBextractor::DistributeOctTree(vector<cv::KeyPoint>& vKeys, const int &minX, const int &maxX, const int &minY, const int &maxY, const int &nFeatures, const int &level) { if(level==0) { for(int i=minX; i<maxX; i+=mnMinX) for(int j=minY; j<maxY; j+=mnMinY) { cv::KeyPoint kp; kp.pt = cv::Point2f(float(i)+float(mnMinX)/2.0f, float(j)+float(mnMinY)/2.0f); kp.octave = level; vKeys.push_back(kp); } return; } const int nIni = vKeys.size(); for(int i=minX+mnMinX; i<maxX-mnMinX; i+=mnMinX) for(int j=minY+mnMinY; j<maxY-mnMinY; j+=mnMinY) { if(countNonZero(umax(ucvMask(cv::Range(j-mnMinY,j+mnMinY),cv::Range(i-mnMinX,i+mnMinX)),255))>0) continue; DistributeOctTree(vKeys,i-mnMinX,i+mnMinX,j-mnMinY,j+mnMinY,nFeatures,level-1); } if(level == mnMaxLevel && vKeys.size()-nIni<nFeatures) { int dx = maxX - minX; int dy = maxY - minY; while(vKeys.size()-nIni<nFeatures) { for(int i=minX+dx/10; i<maxX; i+=dx/10) for(int j=minY+dy/10; j<maxY; j+=dy/10) { cv::KeyPoint kp; kp.pt = cv::Point2f(float(i)+float(dx)/20.0f, float(j)+float(dy)/20.0f); kp.octave = level-1; vKeys.push_back(kp); if(vKeys.size()-nIni>=nFeatures) return; } dx+=mnMinX; dy+=mnMinY; } } } ``` 该函数接受一些参数,包括要提取的特征点范围(minX, maxX, minY, maxY),要提取的特征点数目(nFeatures),以及当前的金字塔层数(level)。函数首先检查当前是否是最底层(即level==0),如果是,则将特征点均匀分布到该层所有的网格中,每个网格的中心点就是一个特征点。如果不是最底层,则递归地调用该函数,将特征点均匀分布到子网格中。在递归过程中,如果某个子网格中已经有特征点,则不再向其中添加新的特征点。 当递归回到最顶层(即level==mnMaxLevel)时,如果提取的特征点数目仍然不足,就在每个网格的中心点附近添加新的特征点,直到达到所需的特征点数目为止。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值