三维场景重建整合笔记

近年来,随着智能手机、头盔立体显示等技术的发展和 普及,虚拟现实技术取得了飞速发展。在 VR 技术中场景建 模是最关键的一步。3D 游戏通常采用专业三维建模软件, 如 Autodesk Maya、Autodesk 3ds Max 等进行建模,此方法 耗时耗力,所建模型为虚拟场景。对真实场景一般需要借助 专业三维扫描设备,此方法测量精度高,但设备价格昂贵, 不利于大范围推广。为此本文将多视图三维重建技术应用于 VR,实现真实场景的建模。该方法直接从多个视角的 2D 图 像提取场景的三维信息,数据采集简单快捷,成本低廉,但 由于遮挡、光照变化、特征弱等各种因素,建模过程会出现 噪声、空洞等各种瑕疵。 目前基于多视角图像的三维重建算法主要有三类: ①基于体像素的三维重建算法;②基于深度图的三维重建算法;③基于特征点生长的三维重建算法。基于特征点生长的三维重建技术,通过对现实场景进项图像采集,然后对多幅图像进行特征点检测、匹配,生成稀疏种子点云, 并在此基础上增加生长点有条件的初值矫正优化等措施,提高重建的精确性,降低噪声、空洞等引起的重建错误。

 

三维场景渲染与重建是利用场景的图形或图像等信息渲染出特定观测视点的场景图像和重建出三维场景的结构模型,它是计算机视觉中的一个重要的研究课题,开展该方面的研宄对于模式识别、虚拟现实、探险救援、军事侦察等都具有非常重要的意义。经典的三维场景渲染与重建方法按照基本处理单位的不同分为:

以像素点作为基本处理单位逐点进行渲染与重建,该方法获得的渲染图像和重建模型比较真实,但是速度较慢

以网格作为基本处理单位进行渲染与重建,该计算速度较快,基本能满足实时渲染的要求,但是当网格内包含目标边界时导致渲染图像和重建模型失真

 

另外,现有的诸多渲染与重建算法均要求已知不同观测视点的坐标,而现实情况下并不一定能精确获得视点的坐标,例如水下机器人受水流冲击影响,在拍照时坐标很难确定。本文是以图像粒(即连通区域)作为基本处理单位,研究基于空间多视点图像的三维场景渲染与重建技术。主要研宄内容包括:

(1)基于形态学连通性理论的粒化研究

考虑到图像的色相、亮度和纹理等特性,提出了利用形态学属性连通算子研宄彩色图像的粒化方法,每个粒代表了一个不规则形状的连通区域。同一粒的内部像素点具有同质性或相同的纹理结构,不同粒中像素点的值具有不同的特性。探讨了基于隐马尔科夫模型的粒边界确定方法,降低了视角、光照对彩色图像粒化的影响。

(2)基于空间拓扑结构的图像配准研究

建立了基于有向图的图像粒(连通区域)空间拓扑结构关系,引入可达矩阵分析拓扑结构图中有向树和有向回路(即广义顶点)的特点。将图像粒进行模糊化,探讨了基于模糊推理的不同视点间图像粒的全局最佳匹配问题,在图像粒匹配的基础上,分析粒内特征点匹配关系,建立了匹配粒内像素点的单应映射。该方法消除了具有自相似结构的不同视点图像间错误匹配问题。

(3)在未知多视点相机位置关系的前提下,实现三维场景渲染与重建。

针对相机径向畸变等因素的影响,提出了基于加权Sampson近似的不同视点相机间

相对位置计算方法。提出了基于视图张力漂移的场景深度估计方法,并且研宄了基于图像粒的空间变换与场景深度图相结合的三维场景渲染方法,研宄了模型表面拟合与点云模型修正相结合的三维模型重建技术。上述方法在未知多视点相对位置关系的前提下,降低了累积误差的影响,能够比较真实的实现三维场景的渲染与重建。

 

基于RGBD的三维重建:

https://www.gameres.com/676770.html

简单地说,三维重建就是从输入数据中建立3D模型。其中,在面向消费者层面的深度相机出现以前,三维重建技术的输入数据通常只有RGB图像(图1左)。通过对物体的不同角度拍摄的RGB图像,使用相关的计算机图形学和视觉技术,我们便可以重建出该物体的三维模型。不过,早期的三维重建技术得到的模型精度往往较低,且技术的适用范围有限。消费者层面的深度相机的出现为三维重建技术提供了深度图像(depth image)数据,大大降低了重建的难度,并使得三维重建技术可以应用到几乎任何现实场景中(图1右)。由于基于深度相机的三维重建技术所使用的数据是RGB图像和深度图像,因此,这类技术通常也被称为基于RGBD数据的三维重建技术(D指代depth)。

https://di.gameres.com/attachment/forum/201608/18/161629ohjpz06dd6i5miqg.jpg

深度值和三维数据

在介绍基于深度相机的三维重建技术之前,首先需要了解深度图像中的数据的具体含义。对于现实场景中的点,深度相机扫描得到的每一帧数据不仅包括了场景中的点的彩色RGB图像,还包括每个点到深度相机所在的垂直平面的距离值。这个距离值被称为深度值(depth),这些深度值共同组成了这一帧的深度图像(图1右)。也就是说,深度图像可以看做是一副灰度图像,其中图像中每个点的灰度值代表了这个点的深度值,即该点在现实中的位置到相机所在垂直平面的真实距离。图2简单说明了RGB图像和深度图像的关系。

https://di.gameres.com/attachment/forum/201608/18/16162922uprnzu7q9dje4m.jpg


如图所示,对于现实场景中点M,深度相机能够获取其在RGB图像中的成像点XM,以及M到相机所在的垂直平面(即XY平面)的距离,这个距离便是M的深度值。以相机位置为原点,相机所朝方向为Z轴,相机的垂直平面的两个轴向为X、Y轴,可以建立相机的局部三维坐标系。另外,RGB图像到相机位置的距离正是相机的焦距。通过这些数据并使用简单的三角几何公式,我们很容易得到M在相机的局部坐标系中的三维坐标。于是,RGB图像中的每个点,都会对应一个在相机的局部坐标系中的三维点。因此,深度相机的每一帧的深度图像就相当于一个在相机的局部三维坐标系中的点云模型。

基于深度相机的三维重建的核心问题

如果输入的RGBD数据只有一帧,那么只需要把这一帧对应的点云模型作为重建的模型输出即可。不过,通常的深度相机的帧率(FPS)普遍较高,所带来的数据量是非常庞大的。以微软的Kinect v1为例,其FPS=30,即1秒钟扫描30帧,也就是1秒钟便可得到30张RGB图像和30张深度图像。每一帧图像的分辨率通常是640x480,那么在短短的1秒钟,深度相机得到的点云的点的个数是640x480x30=9216000。那么,如何在重建过程中处理如此庞大的数据?另外,深度相机所得到的深度数据是存在误差的,即使相机位置固定,现实场景中的点在不同帧中的深度值也会有区别 。也即是说,对于每一个现实中的点,在扫描过程中会得到众多“测量值”位置。那么,如何估计点的最终位置?这个问题可以被称为“从大数据中建立模型”问题(图3)

https://di.gameres.com/attachment/forum/201608/18/16163052cvcyncshx6g9lu.jpg

除了上述问题外,重建过程中还有一个关键性问题——相机位置的估计。首先,为什么需要估计相机位置?通过本文之前内容讲述的深度值的原理可知,每一帧深度图像对应的点云模型是在相机的局部三维坐标系中。因此,不同的相机位置(即不同帧)便对应着不同的局部三维坐标系(local space/coordinate frame)。然而,重建后的模型需要坐落在一个坐标系,即世界坐标系或全局坐标系(world/global space/coordinate frame)中。于是,我们需要找到每一帧的相机局部坐标系同世界坐标系的位置关系,也就是确定每一帧中相机在世界坐标系中的位置(图4)。在计算机视觉和智能机器人领域,这个问题是经典的“同步定位与地图构建”(Simultaneous localization and mapping,简称SLAM)中的定位问题:机器人在未知环境中,如何通过获取的周围环境的数据来确定自己所在的位置。

https://di.gameres.com/attachment/forum/201608/18/1616305j9g6degfcfc95gi.jpg相机位置的估计

给定每一帧输入的RGBD数据,我们需要估计相机在世界坐标系中的位置。通常我们会把第一帧的相机位置当做是世界坐标系的原点,于是,我们需要估计的便是相机在此后每一帧相对于第一帧的位置的转移矩阵(transformation matrix)。使用数学语言描述是:在给定了第k-1帧重建的模型以及转移矩阵Tw,k-1,还有第k帧的输入RGBD数据,估计出第k帧的转移矩阵Tw,k(图5)。这里的w下标指代世界坐标系world,k是帧的编号,k>1。

https://di.gameres.com/attachment/forum/201608/18/161630ozwqossdlwoqoqfh.jpg

Newcombe 等人于2011年提出的三维重建的经典方法KinectFusion[1] 使用了迭代最近点(Iterative closest point,简称ICP)方法来解决以上问题。给定输入的原始数据(source)和目标数据(target),以及两者的数据点之间的对应关系(correspondence),ICP计算得到原始数据和目标数据之间的转移矩阵,该矩阵使得所有的目标数据点到其对应的原始数据点所在的切平面的距离之和最小(图6)。使用数学公式这个目标函数是:

https://di.gameres.com/attachment/forum/201608/18/16163039nfimciissnh9ci.jpg

这里的si和di是原始数据点和对应的目标数据点,ni是si所在的切平面的法向量(图6右)。

https://di.gameres.com/attachment/forum/201608/18/161631ull39zrhl1uh3zfv.jpg

为了给ICP算法找到合适的对应点,KinectFusion方法简单的将目标数据点——第k帧的数据点(图5中的黄色点)——通过转移矩阵Tw,k-1投影到原始数据点——第k-1帧的点(图5中的红色点),然后将两者作为对应相互对应的点。依照这种对应关系的ICP算法的最大优点是速度快,并且在扫描帧率较大,相邻两帧差别很小的情况下的精度很高。在估计了第k帧的转移矩阵后,将其作用到第k帧的在相机的局部坐标系的数据中,便可得到在全局坐标系中的数据。图7展示了典型的从输入数据(a),到估计相机位置并作用到数据上(b),然后到最终的优化之后的重建模型(c)的流程

https://di.gameres.com/attachment/forum/201608/18/1616316zb1o2xqbkyhbiku.jpg

KinectFusion中的ICP方法仅仅使用了三维空间中的数据,并未考虑到RGB数据信息。另外,ICP必须要建立在扫描帧率较大,相邻两帧差别很小的前提下。因此,这种估计相机位置的方法存在较大的局限性,尤其是对存在较大平面的场景(如墙面、天花板和地板等)时,这种估计方法会带来很大的误差。KinectFusion之后的科研工作者们也提出了一些改进方法。例如,在估计相机位置时,同时考虑RGB信息和三维信息,并建立新的目标函数来进行优化[2]。另外,使用已定义好的模型来模拟代替较大平面的物体[3],可以很好的排除掉这类物体所带来的扰动。不过,考虑到实时性和稳定性,这种基于ICP的框架依然是非常经典且最常见的估计相机位置的方法。

从大数据中建立模型

从上文以及图7(c)可以看出,在估计了相机位置后,我们需要把新一帧第k帧的数据同已有的第k-1帧的模型数据结合起来,以输出优化后的模型。这其实就是本文在前面介绍的问题:对于每个现实场景中的点,如何从该点的众多“测量值”位置中估计出最终位置?

这里继续讲述经典的KinectFusion中所采用的方法。KinectFusion在世界坐标系中定义了一个立方体,并把该立方体按照一定的分辨率切割成小立方体(voxel)。以图8上为例所示,图中定义了一个3x3x3米的立方体,并把立方体分为不同分辨率的小立方体网格。也就是说,这个大立方体限制了经过扫描重建的模型的体积。然后,KinectFusion使用了一种称为“截断有符号距离函数”(truncated signed distance function,简称TSDF)的方法来更新每个小网格中的一个数值,该数值代表了该网格到模型表面的最近距离,也称为TSDF值(图8下)。对于每个网格,在每一帧都会更新并记录TSDF的值,然后再通过TSDF值还原出重建模型。例如,通过图8下两幅图中的网格的TSDF数值分布,我们可以很快还原出模型表面的形状和位置。这种方法通常被称为基于体数据的方法(Volumetric-based method)。该方法的核心思想是,通过不断更新并“融合”(fusion)TSDF这种类型的测量值,我们能够 越来越接近所需要的真实值。

https://di.gameres.com/attachment/forum/201608/18/161631pt1jt851q55tq877.jpg

KinectFusion中TSDF的更新方法核心思想就是简单的对所有的测量值加权平均的过程。这种更新方式效率高,对于保证实时三维重建非常有必要。基于体数据的方法简单直观,而且容易使用并行计算实现,因此可以极大的增加扫描和重建效率。另外, 使用计算机图形学中的网格生成相关方法(例如MarchingCubes),我们可以很容易从这种体数据的结构中生成三角网格模型,这对于进一步的研究和渲染非常重要。不过,这种方法也有很大缺点。例如,KinectFusion这种基于体数据的方法提前已经限定了扫描空间(例如图8上的3x3x3米),超过这个空间的显示场景的物体将无法重建,这是因为定义立方体和网格需要的内存空间非常大 。这就意味着,KinectFusion无法用来扫描大范围空间。另外,立方体中的所有的网格中的TSDF都需要记录,即便这个网格在现实场景中根本没有点,这就造成了极大的内存空间的浪费,并限制了扫描范围。针对这些问题,KinectFusion之后的科研工作者们也提出了一些改进方法。例如,一种移动式的体数据网格(moving volume)方法可以不断移动定义好的网格模型到新的场景中,并不断输出已经重建好的模型到本地空间中,从而能够无限扩展扫描空间[4](图9)。另外,一些重建方法使用了点云的数据结构来代替体数据结构,在重建过程中不断把密集的点云数据融合成一定密度的点云[5]。这种方式不需要存储场景中的并不存在的点,因此能够节省大量空间以扩大扫描范围。

https://di.gameres.com/attachment/forum/201608/18/161632ccv3b1m1c65yh5x5.jpg

渲染

  vr的性能瓶颈来源

1.vr需要至少同时渲染2 张50fps的图像,比常规的pc渲染量都要大。

2.vr对输入延迟性要求很高,因为对输入超过20ms的延迟都会引起用户的不适。

VR WORKS的优化

1.vr sli 多显卡支持

通过增加显卡,增加渲染性能。

常规的多显卡渲染是这样的,

https://img-blog.csdn.net/20160323210415898?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

即显卡0和显卡1轮流渲染第n帧和第n1帧,但是要求cpu要提交的足够快,因为cpu还是要提交两份drawcall,cpu不能成为瓶颈,这种方式从绘制n到n绘制出来的延时如图。

vrworks 做了改进,如图

https://img-blog.csdn.net/20160323210625133?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

https://img-blog.csdn.net/20160323211045588?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

让显卡0和显卡1负责绘制左右两眼,而cpu为两个显卡提供一模一样的drawcall,因为zaivr的两眼绘制的东西基本是一样的,除了perspective矩阵不一样。而vrworks通过其api,实现了一组draw call 对多个显卡的广播,并可以为不同显卡设置不同的perspective(常量)。这样cpu一份drawcall对于两眼,而两眼的绘制在两个cpu并行,延迟大大节省。当然这里还有一些可能可以优化的,比如很多东西,例如shadow map,基于gpu的一些物理可能不需要在两个gpu都做一遍,可以优化一下。

 

2.Multy Resolution Rendering 多分辨率渲染

这个在vr领域算是经典的优化了,很多vr设备都做了这个,原理来源于,vr的图像为了适应眼镜的变形要做卷曲(wrap),如图

https://img-blog.csdn.net/20160323211736168?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

我们需要得到的是右边的图,但是渲染出来的都是左图,所以一般都是在最后将左图在图像上做处理,变成右图,这样我们可以看到其实左图在边缘处很多像素在卷曲后被浪费了,于是人们就像我们是不是可以对vr的图像做不同分辨率的渲染,即中心区域用比较高的分辨率,而边缘位置用较低的分辨率,极限情况下,将大部分边缘降低分辨率,可以减少50%的pixel的渲染,提高一倍的效率。

https://img-blog.csdn.net/20160323212119764?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

但是常规的渲染管线,要这样做,得不偿失,因为你要切分这个区域,定义多个不同大小的viewport,然后将物体依次渲染到多个viewport上,当然你可能会想到对不同的viewport可以剪裁掉不同的物体。

但是nvidia的maxwell架构的芯片,即GTX900以上(也就是为什么做vr要用好的显卡)支持了一种叫做multi-projection的技术,即在显卡层面,支持同时运行多个视口和投影,他不同于常规管线,他在管线中并行了多个投影,同时渲染到多个viewport,pipeline还是一次,只是在后面将这些像素绘制到多个viewport,这就是硬件层面的多适口多分辨率。

3.asyncchronous timewarp 异步时间卷曲

timewarp也是vr很经典的优化,在occlus等早已使用,如果没有timewarp,我们会感觉很大的延时和眩晕。因为就算帧率再高,我们看到影响的那一刹那,渲染的也是过去某个时刻的图像,和我们当时所处的位置是不一样的,这种不一致随着帧渲染耗时的增长而增长,这种timewarp的做法是,在gpu绘制结束,扫描给显示屏前,将这个图像做一个图像空间的位移,以校准我们当前的位置,也就是说处于p0位置渲染的图像,在p1位置绘制好给我们,我们需要将其校准成p1位置的样子,这种校准有很多算法,都是在图像处理上做的平移,这样我们会感觉到看见的和我们的位置是同步的。

但是在传统的渲染中,这些工作是在一个流水线上的,也就是同步的,当某一帧很耗时很卡时,用户会迟迟收不到当前位置校准的图片,一直停留在上一帧的图片,因为gpu卡住了,后面的校准(timewarp)也不能进行,用户会感觉强烈的卡和眩晕。

这里就提出了一个异步 timewarp的概念,即在gpu上有一个独立的线程做这个warp,即不管你主线程渲染卡成什么样,我这个独立的线程会按照帧率给你每个位置的warp,给你最新的基于你位置的图像,这能解决很卡很卡时我们依然能够得到模拟的图像。但是传统的gpu不支持这种独立的线程。nvdia的vrworks加入了这个。他加入了一个high-priority context的概念,允许用户启动一个优先级最高的线程最warp,独立于你的渲染线程。

4. direct mode

传统的渲染,pc的显示器会把vr眼镜作为显示器的一个显示扩展,vr眼镜和gpu是没有直接交互的,而vrwroks里可以开启vr headset的直接模式,让gpui直接将图像扫描到vr设备。

5.Front Buffer Rendering

 

 

  1. 预测渲染:因为vr对渲染的延迟特别敏感,vr设备可能每时每刻都在动,而正常来说gpu上渲染出来的结果一定都会比cpu计算提交的时候玩,看到的总是和当前所处位置不一致的图像,延迟越大就越会头晕,valve解决的思路是大量应用了预测,等于在在T0时刻就预测T1时刻应该所处的位置,计算T1的渲染结果,这样提交后,GPU在T1时刻渲染出来恰好敢上正处于T1的位置,因为这种T0到T1的提前量都是在几十ms的范围内,所以预测的误差不会很大,可以说为了减少延迟一定要“”预测“”,而具体这个提前量是多少,其实文章也是用nvsight或者gpuview这种大量的progile得到的经验值,这种预测渲染的CPU和GPU工作的流程是这样的,等于在GPU渲染本帧的时候,CPU已经在同时计算和提交“预测的”下一帧

  1. 因为vr渲染的图像是要wrapped,所以一些边缘的像素是不用渲染的,可以提前做个stencil mask,可以减少17%渲染量。
  2. MSAA(多重采样抗锯齿)在vr渲染是可用的且是必要的

 

几何模型

移除你的几何体中的任何用VR永远看不到的面。我们不想要渲染永远不会被看见的东西。举个例子,如果用户永远不会看见一个碗柜的背面因为它靠着墙的原因,那么我们在模型中不需要任何那样的面。

尽可能简化模型设计。根据你的目标平台,你可能想尝试通过贴图增加细节,潜在的视差映射贴图以及曲面细分,这可能影响性能,也许对于你的目标平台也不适合、有效。

Overdraw

Overdraw可以让你查看有哪些对象绘制在了其他对象的前面,而这是在浪费GPU时间。尽可能考虑减少使用overdraw。通过使用场景视图控制栏(Scene View Control Bar),你能查看场景视图中的overdraw。