论文《Real-time 3D Reconstruction at Scale using Voxel Hashing》阅读

先介绍体素哈希。

隐式体积融合

我们考虑隐式SDF(implicit signed distance field)方式存储物体的三维模型,即一个稠密体素网格(voxel grid),每个体素存储两个值:1带符号距离(表达了从体素中心到被观测表面的距离,正值代表体素位于表面的前方),2权值。物体的表面由体素的零值面隐式地表达。
对于每个输入的深度图,我们扫描整个模型,保留视锥范围内(当前帧的视锥)的体素,并将体素映射到深度图平面上,带权更新体素的SDF值。
该过程即为隐式SDF方法的融合过程。
为了减少计算代价,通过引入TSDF(Truncated signed distance field),即当体素距离某个表面的距离大于一个阈值后,我们忽略该体素的SDF值。
采用TSDF后,大部分体素不再存储有效数据,包括在表面前面的自由空间(free space)或者是表面后面的无法观测区域。因此设计了一个数据结构可以有效地利用这种稀疏性。

哈希表和哈希函数

我们方法的核心是一个哈希表(hash table)数据结构,由 n n n个哈希条目组成,每个哈希条目(hash entries)记录着体素块指针 p o i n t e r pointer pointer和对应的空间位置 [ x , y , z ] [x,y,z] [x,y,z]
world被均匀的网格分为一个一个的体素,每个体素包含一个TSDF值、颜色和权值。 8 3 8^3 83个体素(或者说网格)组成一个体素块(voxel block)。
我们将定义一个哈希函数(hash function)实现从一个空间位置 ( x , y , z ) (x,y,z) (x,y,z)到哈希条目(hash entries)的映射,而该条目将会存储指向 ( x , y , z ) (x,y,z) (x,y,z)处体素块的指针。
哈希函数为:
H ( x , y , z ) = ( x ⋅ p 1 ⊕ y ⋅ p 2 ⊕ z ⋅ p 3 )   m o d   n H(x, y, z)=\left(x \cdot p_{1} \oplus y \cdot p_{2} \oplus z \cdot p_{3}\right) \bmod n H(x,y,z)=(xp1yp2zp3)modn
其中, p 1 p_1 p1, p 2 p_2 p2, p 3 p_3 p3为三个大质数(项目中实际赋值为 73856093,
19349669, 83492791), n n n是哈希表大小。
在这里插入图片描述

处理冲突——哈希桶

因为无法保证不同位置映射到不同的哈希值,所以我们将每个哈希条目拓展为一个哈希桶(hash bucket),每个哈希桶中包含多个哈希条目,但对应同一个哈希值。
当冲突发生时(即此时哈希桶中第一个哈希条目已经被占用),我们将体素块指针存储在哈希桶中的下一个可用的哈希条目中。

哈希桶的溢出

一般,如果哈希表和哈希桶的尺寸选择合理,那么哈希桶将会很少溢出。为了处理溢出情况,我们扩展哈希条目,增添一个偏移量offset,用来指示下一个哈希条目的位置。

即当插入新的哈希条目 a a a时,且对应的哈希桶 A A A已经满时,我们顺着哈希表,寻找其他哈希桶中的空闲位置,如果找到,在该空闲位置插入该哈希条目,并在哈希桶 A A A中的最后一个哈希条目的offset记录新插入的哈希条目的位置。
如果该哈希桶 A A A还有新的哈希条目 b b b,我们使用之前新插入的哈希条目 a a a的offset记录哈希条目 b b b的位置。
这样,由溢出的哈希条目及其offset字段形成了链表,我们称为linked list。
其中,注意,溢出的哈希条目不能占用哈希桶的最后一个条目位置,该位置需要存储该哈希桶的linked list的表头。
哈希条目结构如下:

struct HashEntry {
	short position[3];//偏移
	short offset;//体素块位置
	int pointer;//体素块指针
};
哈希操作
插入/检索

我们首先计算哈希函数以确定需要插入/检索的哈希桶。然后我们迭代哈希桶中的所有元素,包括哈希桶的linked list。如果我们找到一个条目具有相同的position,即可返回检索结果。如果我们在哈希桶中找到了空闲条目,那么插入新的哈希条目。如果哈希桶已满,那么我们将哈希条目放入linked list。
注意,为了避免内存冲突,在确定插入位置时,锁定该哈希桶。

删除

如果删除对象在该哈希桶中,且不在最后一个位置(该位置存储linked list表头),那么直接删除。
如果删除对象在哈希桶中的最后一个条目位置,那么,将linked list的中的第二个哈希条目移动到哈希桶中的最后一个条目位置,然后修改相应的offset值,以保证linked list的正确性。
如果删除对象在linked list(不包括哈希桶中的最后一个条目位置),那么直接删除对象,并修改相应的offset值。

在这里插入图片描述
上图描述了条目的插入和删除的过程。

算法流程

在这里插入图片描述

体素块分配

这一部分因为没有看到代码,没有看明白论文表述的究竟是个什么过程。

体素块融合

在上一节中,我们将所有表面的截断区域中的体素块分配到了哈希表中。当接收深度图时,我们需要更新当前视锥中所有已分配的体素块。

体素块选择

我们并行访问哈希表中的所有哈希条目,并创建一个缓冲区,复制存储所有指向视锥中的体素块的哈希条目。
注意,不会复制体素块,仅复制存储其关联的哈希条目。
在这里插入图片描述
缓冲区建立示意图。

隐式表面更新

按照TSDF更新规则,并行处理哈希条目列表(上一步创建的缓冲区)。
其中,每个GPU kernel处理一个哈希条目,即一个体素块,每个线程处理一个体素。
(这种方法因为在单个GPU处理器上处理体素块,缓存命中率高,处理效果比较好)
深度的更新有赖于体素的深度权值,会根据实际深度值设置体素的深度权值,因为噪声与测量距离有关,表面距离摄像头越近,噪声越小,相应的深度权值越大。而颜色多采用平均值,但会将更多的权重分配给最近的颜色输入。

异常处理

如果遇到动态物体、或出现异常值时,会出现更新次数很少,但影响建模质量的体素块。我们在进行TSDF更新后,计算视锥内的每个体素块中的1最大权值,2最小TSDF值。删除最大权值为0或者最小TSDF大于阈值的体素块:1删除对应哈希条目,2释放体素块内存。

表面提取

这一步是通过raycast提取TSDF模型的隐式表面,获得指定视点的深度图,并通过颜色渲染获得rgb图。
对每个像素,我们沿着射线,由最小深度行进到最大深度,行进期间,如果当前位置处在被分配的体素块中,使用三线性插值评估当前位置的TSDF值。为了加快检索零交叉点位置,我们以截断值的一半为最小行径间隔。一旦检索到零交叉点,我们就使用迭代线性搜索来估计真实得表面位置。

相机跟踪

我们使用 上一步raycast得到的深度图和输入的深度图,采用point-plane ICP和投影匹配,进行位姿估计。其误差函数不仅包含几何误差、还包含颜色误差。

内存管理——双向数据传输(streaming)

我们使用双向的GPU-Host数据传输方案,以解决因体素块的存储带来的GPU内存占用和性能问题。
我们创建一个球形的活动区域(active region),该区域包含了当前帧的视锥范围。以Kinect相机为例,假定可探测深度为八米,我们将活动区域的中心定位在距离相机位置四米处,半径为八米。
在完成当前帧位姿估计后,即进行双向数据传输(streaming)

从GPU/活动区域中移出体素块

首先,在哈希表中标记出所有需要移出的体素块,然后,将这些体素块、哈希条目,借由中间缓冲区(intermediate buffer)送到host,删除/释放其原有的哈希条目/体素块。建立堆(heap),存储删去的体素块的位置,以便之后再次使用。
在host中,我们建立多个chunks,每个chunks对应现实世界的一个区域,使用linked list将体素块附加到这些chunks上。并为每个体素块创建一个描述符,存储哈希条目数据与体素数据。

体素块的传回

我们首先确定落入活动区域的chunks,直接将chunks中的所有体素块传输给GPU。考虑到HOST-GPU的高带宽,以及在GPU中进行体素块剔除的高效性,这样可以提高性能。
由于CPU性能有限,导致HOST-GPU的传输速度为1chunks/帧。因此,距离摄像机视锥中心最近的chunks优先传输。
为了保证体素块在被传回GPU之前,其相应位置不会被更新,我们需要在GPU上创建一个二进制占用网格,每个项对应一个特定的体素块,指示其是否在GPU中。如果显示目标体素块在Host中,应避免进一步操作。
该二进制网格在 256 m 3 256m^3 256m3的空间中仅有不到512KB的开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值