osvr_distortion_correction

来源:互联网 发布:激光脉冲价格知乎 编辑:程序博客网 时间:2024/06/14 05:31

OSVR畸变矫正

这是一个关于VR畸变的文章,有理论描述,有源代码,学习之后觉得非常不错,将文章翻译过来,该文章英文地址,翻译中有一些个人添加的辅助信息,以括号标识,以粗体表示,例如(以下为个人翻译,水平有限,欢迎指正).
本文档描述OSVR畸变矫正功能,这种矫正在显示被渲染到平面矩形(屏幕)上之后发生.这里介绍了如何处理这个渲染阶段.

简介

畸变矫正的基本功能是收集通过透镜后观察到的物理显示像素,这些像素在矩形平面屏幕上(或显示在非平面屏幕上)的相应位置存在畸变.经过投影和视图矩阵来使图像正确的显示在矩形平面屏上.为了避免看到未定义的区域,屏幕的overfill因子必须足够覆盖那些反畸变映射视图的所有的点.
概念上:(1)投影和视图变换取3D模型空间中的点,并将他们投影到规则矩形平面屏上.(2)畸变矫正调整最终的图像,以消除通过透镜或曲面屏幕产生的非线性效果将每个点从规则的位置上拉回到正确的物理位置上(所使用的矩形片面投影屏也被确定为畸变处理过程中的一部分).

方法

有许多潜在的畸变类型,包括透镜产生的哪些以及在非平面屏幕表面产生的.在渲染管线中,OSVR使用畸变网格方式处理通用的情况;它的配置文件还包括指定每种颜色的径向失真参数的功能.下面讨论两种具体的情况,和通用的解决办法.

曲面屏

下图显示了一个曲面显示器(如当前流行的OLED电视)的例子,视点的方向在屏幕的中间的法线上,视点和屏幕之间没有经过透镜.图片的左边显示一个从上向下看的屏幕的一维的视图,右边显示从眼睛的角度看到的屏幕的第一人称视图.与径向畸变透镜类似,曲面屏幕的畸变矫正取决于观察者的眼睛的位置.屏幕越弯曲,效果就越明显.
这里写图片描述
在这个图像中,蓝色标识的图像是被看到的(畸变的)屏幕图像,绿色的矩形是一个经过反畸变的屏幕图像的一个可能的选择.只要我们调整它的投影尺寸来匹配二维视图中所看到的范围,我们可以自由的选择屏幕的图像的深度-在靠近的时候缩小图像或者在拉远时放大图像.屏幕的特定图像比物理屏幕更小-物理屏幕上存在虚拟屏幕外的位置.
下图显示了OSVR圆柱形投影反畸变的方法.
这里写图片描述
what is not done:图片右侧显示了一种处理畸变的方法,但是我们没有采用这种方法.它通过与圆柱形投影完成的光学畸变相反的方式对原始场景图形进行预畸变的方式,使最终得到的渲染图像可以直接显示在(蓝色)屏幕上,并且有矫正后(绿色)的投影图像.这需要对几何进行任意的非线性映射,并且需要在三角形之间进行分段线性插值的操作,这是不容易做到的.处理预畸变的另一种方式是先按标准渲染过程执行一次,然后执行第二次渲染,将第一次生成的原始矩形文理投影到屏幕的反畸变相应的的位置上(类似与下面的方法中描述的第二个渲染过程),上面的任何一种方法生成的图像,包括了非线性的几何变形,导致图像不能响应头部移动或旋转产生效果,这使得OSVR系统选择了其他的方法.
what is done:图像的左侧显示了OSVR使用的方法,它确定了虚拟屏幕(绿色)上的点与畸变的真实的屏幕(蓝色)的点一一对应的关系..在最终渲染的过程中,调整每个点的纹理坐标到可视像素的对应的位置.这个反畸变过程可以在顶点着色器中进行,它通过产生密集的网格,通过对纹理坐标调用一个函数或者使用一个纹理贴图来为每个颜色或像素着色器调整纹理坐标,为每个颜色提供新的纹理坐标映射到屏幕上的正确位置。
OSVR RenderManager中实现了初步的网格畸变的方法.

每色径向畸变

下图显示了经过径向畸变透镜后,所看到的矩形屏幕(黑色)的示例.特定的透镜对不同波长的光有不同的放大作用,图表中三种不同颜色的图像,每一种代表一种原色.透镜能够放大物理显示的范围,并且将虚拟图像放在离眼睛更远的位置,这样观察者就能够注视到它,但是透镜会产生一些期望以外的副作用.可以使透镜系统每种原色产生相同的畸变,也可以在渲染系统内矫正这种色散畸变.
在这里,畸变依赖于从观察者瞳孔到物理屏幕上的点的直线和从观察者瞳孔到透镜中心直线的夹角.虽然对于理想透镜而言,屏幕上的虚拟图像的位置不依赖于用户的眼睛的位置(只要眼睛在透镜的出瞳范围内),径向畸变依赖于观察者的眼睛的位置.这意味着完全矫正径向畸变需要考虑观察者的眼睛之间的距离以及了解透镜相对于屏幕的位置.
OSVR配置文件使用一组参数来描述畸变:

  • Center of projection:它提供了屏幕虚拟图像上的位置坐标,它是从观察者眼睛的中心到透镜的投影的中心射线的交点.这是一个每个轴上为0-1的小数的分数坐标(即X,Y分别从0.0~1.0),屏幕的左下角为(0,0),右上角为(1,1).这个参数的含义我画了一个第一人称面对屏幕视角的草图,帮助大家理解:
    这里写图片描述

  • distance scales:因为畸变矫正取决于透镜的几何和屏幕的几何形状,并且可能与视口大小或横纵比无关(透镜组方式可能更加复杂),我们需要允许指定不仅限于径向畸变多项式系数外(从投影中心到点的距离的比例尺),还需要测量这个空间.我们通过指定空间中大量的单位半径来指定参数在纹理坐标之间的位置,范围从0到1.X,Y是有可能不同的,因为视口可以是非方形的,或者透镜系统有不同的横纵比.D[0]组件描述宽度,D[1]组件描述高度.

  • Per-color coefficients:每个颜色都会提供一组多项式系数,这些系数用来指定从投影中心的新的径向位移作为缩放原始位移的系数.每个多项式中的第一个系数是一个常数因子,第二个是线性因子,第三个是二次方因子,等等.可以指定多个系数.
    这些参数听起来很不直观,大家可以在OSVR里找到,我这里放一个HDK1.3的配置文件:distance of projection相当于center_proj_x,distance scales相当于distance_scale_,Per-color cofficients相当于polynomial_coeffs_.
{  "meta": {    "schemaVersion": 1  },  "hmd": {    "device": {      "vendor": "OSVR",      "model": "HDK",      "num_displays": 1,      "Version": "1.3",      "Note": "Specific to the optics of 1.3, with Render Manager compatible distortion parameters"    },    "field_of_view": {      "monocular_horizontal": 90,      "monocular_vertical": 96.73,      "overlap_percent": 100,      "pitch_tilt": 0    },    "resolutions": [      {        "width": 1080,        "height": 1920,        "video_inputs": 1,        "display_mode": "horz_side_by_side",        "swap_eyes": 0      }    ],    "distortion": {        "distance_scale_x": 1,        "distance_scale_y": 1,        "polynomial_coeffs_red": [0, 1, -1.74, 5.15, -1.27, -2.23 ],        "polynomial_coeffs_green": [0, 1, -1.74, 5.15, -1.27, -2.23 ],        "polynomial_coeffs_blue": [0, 1, -1.74, 5.15, -1.27, -2.23 ]    },    "rendering": {      "right_roll": 0,      "left_roll": 0    },    "eyes": [      {        "center_proj_x": 0.471,        "center_proj_y": 0.5,        "rotate_180": 1      },      {        "center_proj_x": 0.529,        "center_proj_y": 0.5,        "rotate_180": 1      }    ]  }}

在特定期望的环境中,R,G,B的系数,X和Y的距离,投影中心 可以被指定下来(缩放所有这些因子不会对结果有影响),但是空间的左下角(屏幕的左下方)必须是(0,0),像素的远处必须是右上角被D-specified标定的地方.
每种颜色的参数指定了从投影中心的新的径向位移作为原始位移的一个函数.在D-scaled空间,如下:

Offset = Orig - COP;              // VectorOffsetMag = sqrt(Offset.length() * Offset.length());  // ScalarNormOffset = Offset / OffsetMag;  // VectorFinal = COP + (a0 + a1*OffsetMag + a2*OffsetMag*OffsetMag + ...)    * NormOffset; // Position

上面的COP是center of project的意思.
示例:(1)对于投影中心在图像中间的正方形像素的显示,显示的宽度是10像素,高度是8像素,我们将得到:D =(10,8); COP =(4.5,3.5); 参数被指定在pixel-unit的值中 (2)对于6单位宽×12单位高的显示,但是其光学水平方向展开视图以产生具有在X中被拉伸的像素的正方形观看图像,我们可以具有:D =(12,12); COP =(5.5,5.5);参数被指定在垂直pixel-sized单位中,或D =(6,6); COP =(2.5,2.5); 参数被指定在水平pixel-sized单位中.

填充(overfill)

上面例子中显示有一些物理了屏幕上的点与屏幕图像上的任何点不对应.这意味着没有图像被移动到那个位置上.为了避免这种情况,屏幕(绿色)的虚拟图像可以被调整,以便它包围整个真实屏幕,为看得见的和看不见的地方提供图像数据.在该例子中,屏幕从真实屏幕移回,这将导致畸变矫正更加依赖于眼睛的位置.更好的解决方案是将虚拟屏幕放到物理屏幕深度覆盖的范围之内.
这里写图片描述
这个填充(overfill)功能也会被其他畸变方式所需要,包括径向对称(radially-symmetric)畸变.这是因为任何非线性畸变都会将屏幕的矩形边界变成一系列的曲线.

与时间扭曲(time warp)的相互作用

@todo

屏幕的投影和视图

如简介里所描述的那样,畸变矫正的基本功能是将通过透镜已经发生畸变的视图像素移动到一个合理的位置上,这个位置看起来像是来自规则的矩形屏幕上,这些图像有可能是部分可见的.矩形平面屏幕的图像是投影和视图矩阵处理后的结果.
这就意味着畸变矫正可以自由的选择任何矩形作为要投影的屏幕,只要它能够将渲染到矩形上的图像进行反畸变处理.注意:OSVR RenderManager 生成一个填充(overfill)视图,以便从虚拟矩形屏幕外部拉出像素,最佳的规则的矩形应尽可能的覆盖物理显示器上的可见区域,并尽可能的减少所需的畸变(和填充(overfill)).并且它应处于包含屏幕的虚拟图像的凸起部分(以减少当眼睛在不同方向上移动时产生的偏移).
畸变矫正对物理显示的所有点进行预畸变来确定他们应该被画在什么位置上.在规则屏幕的填充(overfill)版本上的每一个点都需要被画出.最终的预畸变图像将会提供给负责扫描输出的电路,然后通过任意形状的屏幕和任意透镜来观看.最终观察者看到的图像是经过畸变和反畸变之后的正常的图像.

示例:使用屏幕角度映射表

假设通过摄像头直接测量或是在头戴显示器的光学设计中光线追踪(ray-tracing)的任何一种,在给定的IPD下,生成屏幕上的物理点与眼睛中心的角度之间的映射.该映射可以是任意的,只要它是一个函数,并且可以包含所有点的布局.对于这个例子,我们假设角度的单位是度(而不是弧度),从头部空间的方向是-Z轴方向为前方,并且显示上的位置以毫米为单位,眼睛正前方方向上对应的点为角度(0,0).此外,我们假设到可见屏幕的焦距为2米(其他部分可能更远,或更近)
(这里是一个理论处理,实现这操作的程序在AnglesToConfig程序文档中描述).

步骤1:确定规则屏幕和填充(overfill)

这个处理的实现在OSVR Distortionze项目的angles_to_config目录中(处理的方法和平面投影适用于单眼水平视野小于180度的情况).
我们通过在OSVR RenderManager中表达的方式来确定规则屏幕的四个边缘和所需的填充(overfill).屏幕的法线必须位于XZ平面中,屏幕位于Y = 0轴的上半部分以上.指定水平和垂直视野范围,眼睛的垂直线和屏幕相交的投影中心以及围绕Y轴的旋转.
配置文件中每个点的眼睛空间位置使用极坐标进行计算,使用2米焦点来估算每个点的半径/纵向角度(Y轴上0指向前方的方向为正方向)/横向角度(正向上)
X屏幕空间是由垂直于Y轴,并被Y轴穿过所定义的范围:

  • 左:重新投影到Y = 0平面中的点位置具有最大正角(注意,这可能不是具有最大纵坐标的点,因为纬度对XZ位置的变化的影响).
  • 右:重新投影到Y = 0平面中的点位置具有最大负角(注意,这可能不是具有最小纵坐标的点,因为XZ位置上的纬度变化的影响).

Y屏幕空间范围是对称的,对应于是X线在最大量值角指定轴边界向上或向下从水平的平面内平行于屏幕X轴的行。我们通过在X屏幕空间范围确定的投影到屏幕平面中时,找到具有最大值Y值的点。
因为集合中的所有点的投影将位于这些屏幕空间范围内,这意味着从该区域之外的任何点都不对应于物理屏幕上的任何点,所以屏幕的填充因子可以是1(没有需要额外的过度填充)。

步骤2:在配置文件中指定屏幕

我们将它们转换成当前OSVR配置文件格式中使用的规范.(将来,如投影和视图文档中说的那样,我们会用4个屏幕的边角中的三个来指定头部空间的位置来替代现在的做法).目前的描述包括这些规格:水平和垂直FOV,投影的中心(这是屏幕上通过眼睛垂直于屏幕的线穿过屏幕的标准化位置)和重叠的百分比.
这里写图片描述
display/hmd/field_of_view/monocular_horizontal:这个参数是指单眼朝向屏幕的中心的垂直线上的位置观察获得的.我们用半屏幕的宽度和从屏幕平面原始的垂直距离来确定它.它指定了水平FOV的一半的值.
display/hmd/field_of_view/monocular_vertical:这个参数是指单眼朝向屏幕的中心的垂直线上的位置观察获得的.我们用半屏幕的高度和从屏幕平面原始的垂直距离来确定它.它指定了垂直FOV的一半的值.
display/hmd/field_of_view/overlap_percent:这个参数是指单眼朝向屏幕的中心的垂直线上的位置观察获得的.并且假设两眼是重合的(IPD = 0).(注意:最终视图转换并不基于这个假设,仅仅是目前算法需要将overlap_percent映射到角度).
display/hmd/eyes[1]/center_proj_x:从屏幕的左侧到右侧的直线,这条直线穿过眼睛垂直于屏幕,并与屏幕相交的位置.

步骤3:从物理屏幕映射坐标

在物理屏幕上的给定的点,畸变映射提供了在规则矩形平面上对应点的坐标(或者在屏幕之外,在填充(overfill)区域或之外).这决定了在屏幕上该位置显示的适当的点.这通过两个步骤完成:
- 步骤3A:使用提供的表,从物理显示坐标映射到角度.
- 步骤3B:通过将来自眼睛的光线投影到规则屏幕的平面上,实现角度映射到虚拟屏幕坐标.然后确定屏幕空间X和Y坐标(左侧X为0,右侧X为1,底部Y为0,顶部Y为1).
对于除表中指定的点以外的点进行此映射,需要对指定外的显示点和外凸之外的点进行插值。

步骤4:在配置文件中生成畸变映射

配置文件格式允许指定由display/hmd/distortion/type变量来标识不同类型的畸变.我们假设RGB畸变分量都是相同的,所以使用类型”mono_point_samples”.这意味着我们需要仅仅指定一种畸变网格,它从物理显示屏标准化坐标(左下角为[0,0],右上角为[1,1])映射到规则矩形上(overfill值为1,即无须额外的填充).处理畸变时数值有可能被映射到[0,0]-[1,1]之外的其他值.他们最初将映射到填充(overfill)区域,然而如果没有足够的填充(overfill),最终将超过这个范围.对于我们上面的方法,没有一个点会被映射到范围之外,因为所有可见的显示点都被映射到规则屏幕的投影区域内,即使填充(overfill)系数为1.
我们通过标准化表的显示坐标来计算网格的输入的标准化的坐标,将它们从毫米转换为屏幕的分数,减去屏幕左下角的坐标,然后每个轴坐标除以屏幕的尺寸.我们计算输出坐标在步骤3中已经描述.
接着我们将未排序的点集合存储在display/hmd/distortion/moni_point_samples数组中,它包含元素的向量,每个元素含有2个子元素,第一个元素是标准化的物理屏幕的二维坐标,第二个是在规则屏幕坐标里的二维坐标.
一个输出例子,它是HMD配置文件的一部分.映射关系如下:

{  "display": {    "hmd": {      "distortion": {        "type": "mono_point_samples",        "mono_point_samples": [          [ [0,0], [0,0] ],          [ [1,0], [1,0] ],          [ [0,1], [0,1] ],          [ [1,1], [1,1] ]        ]      }    }  }}

RenderManager使用这组无序点的样本,使用对最近3个非共勉点的双线性拟合的方式来计算网格,以便确定空间中的每一个必须采样点的与坐标相对应,这些点用来
产生最终所需的网格.

Built-in 畸变网格

HDK1.3和HDK2.0的畸变网格已经内置到RenderManager中.它们可以通过显示配置的”distortion”字段来加载.例如,用加载HDK2.0的畸变网格:

"distortion": {                "type": "mono_point_samples",                "mono_point_samples_built_in": "OSVR_HDK_20_V1"            },

注意:OSVR_HDK_20_V1仅在RenderManagerv00_06_43-93-g3a45de8或更新版本中可用.对于HDK1.3,可以使用”OSVR_HDK_13_V2”和”OSVR_HDK_13_V1”.可以在”https://github.com/OSVR/OSVR-Core/blob/master/apps/displays/OSVR_HDK_1_3_with_mesh.json.”中找到局域HDK1.3内嵌网格的显示描述符的例子.

原创粉丝点击