真实场景的虚拟视点合成(View Synthsis)详解
上一篇博客中介绍了从拍摄图像到获取视差图以及深度图的过程,现在开始介绍利用视差图或者深度图进行虚拟视点的合成。虚拟视点合成是指利用已知的参考相机拍摄的图像合成出参考相机之间的虚拟相机位置拍摄的图像,能够获取更多视角下的图片,在VR中应用前景很大。
视差图可以转换为深度图,深度图也可以转换为视差图。视差图反映的是同一个三维空间点在左、右两个相机上成像的差异,而深度图能够直接反映出三维空间点距离摄像机的距离,所以深度图相较于视差图在三维测量上更加直观和方便。
- 利用视差图合成虚拟视点
- 利用深度图合成虚拟视点
一、利用视差图合成虚拟视点
由于视差图反映的是三维空间点在左、右两个相机上成像的差异,并且由于提前进行了立体校正,这种差异就反映在图像的同一行上。在立体匹配的SGBM算法中就是以其中某一副为参考图像,在另一幅图像的同一行上搜索匹配点。因此在合成虚拟视点时也只需在同一行上平移虚拟摄像机位置即可。流程如下:
(1). 假设视差图中某一个像素点的视差值为dmax,也就是说从左摄像机camL到右摄像机camR之间,该像素点的视差值变化范围为0~dmax。为了方便介绍我将其归一化到alpha取值范围为0~1。
(2).那么如果我要获取alpha=0.5位置时候虚拟相机位置的图像呢,那么此时该像素点和左图像匹配点的坐标差异就是0.5 * dmax,和右图像匹配点的坐标差异也是0.5 * dmax;如果alpha=0.3,那么此时该像素点和左图像匹配点的坐标差异就是0.3 * dmax,和右图像匹配点的坐标差异也是0.7 * dmax;
(3).根据上面提到的原理,显而易见,合成新视点时候只需要将左图像的像素点位置向右移动alpha *dmax,或者右图像的像素点向左移动(1-alpha)*dmax,就可以得到alpha位置处虚拟相机拍摄的虚拟视点图像。
合成虚拟视图即可以利用左参考图像和对应的左视差图,也可以利用右参考图像和对应的右视差图,更好的是都利用上得到两幅虚拟视点图像,然后做图像融合,比如基于距离的线性融合等。
在算法的实现中,又有两种选择,一个是正向映射,一个是反向映射(逆向映射)。
1.正向映射
① 将原参考图像中整数像素点根据其对应上视差值平移到新视图上。
② 平移后像素点坐标可能不是整数,为了获取整数坐标,采用最近邻插值,将原图像像素值赋值到新坐标位置。
③ 由于最近邻插值会损失精度,因此在物体的边缘会出现锯齿效应。
2.反向映射
① 利用参考视点的视差图dispL,算出虚拟视点位置的视差图dispV;
② 由于遮挡等因素影响,dispV肯定会出现空洞和裂纹,所以需要做一个空洞填充holeFilling;
③ 利用dispV将虚拟视点图像中的整数坐标平移到参考视点位置下的坐标,此时也可能不是整数,而是浮点数坐标。这就面临一个从哪里取值的问题。
④ 取参考视点图像浮点数坐标附近4个点的像素值,用双线性插值算出对应虚拟视点图像位置处的像素值即可。
⑤ 由于双线性插值属于一种加权平均的思想,能够有效避免精度损失造成的锯齿现象,但是算法复杂度要高。
下面给出利用左视点图像和对应视差图获取虚拟视点位置图像的效果(反向映射+双线性插值):
不进行空洞填充

空洞填充

二、利用深度图合成虚拟视点
和利用视差图合成新视点一样,利用深度图合成新视点也有两种选择:1. 正向映射 2. 反向映射。由于正向映射比较简单并且效果不理想,因此目前大多采用反向映射方法。
已知内参矩阵K,下面仍然以左图像imgL和depthL为参考图像,获取alpha=0.5位置处的虚拟视点图像,简要步骤如下:
(1). 利用内参矩阵K, 将depthL映射到三维空间点,平移到虚拟相机坐标下后,重投影到虚拟视点图像平面,得到虚拟视点位置处的深度图depthV;
(2). 对depthV进行空洞填充;
(3). 利用内参矩阵K和深度图depthV,将虚拟视点图像imgV上的坐标点反向投影到三维空间点,平移后再重投影到参考图像imgL上, 在imgL上利用双线性插值获取imgV上的像素值。
具体流程如下:
(1). 利用内参矩阵K,以及参考深度图depthL,如下图,将参考图像坐标点(u, v)投影到参考相机的摄像机坐标系下,得到对应的三维空间点(X, Y, Z),计算方法如下:
d * u = fx * X + cx *Z
d * v = fy * Y + cy * Z
d = Z
其中d是深度值,fx, fy, cx, cy均从内参矩阵中得到,那么(X, Y, Z)可以表示如下:
X = ( u - cx ) * d / fx
Y = ( v - cy ) * d / fy
Z = d
参考深度图depthL 参考左图像imgL

(2). 将三维点(X, Y, Z)平移到虚拟摄像机坐标系下,得到虚拟摄像机坐标系下的三维点(X1, Y1, Z1), 计算如下:
X1 = X - alpha * baseline
Y1 = Y
Z1 = Z
(3). 将(X1, Y1, Z1)重投影到虚拟视点图上,得到坐标(u1, v1):
u1 = ( fx * X1 + cx * d ) / d
v1 = ( fy * Y1 + cy * d ) / d
d = Z1
//注意此处d != 0
利用最近邻插值得到虚拟视点深度图depthV。如下图所示,alpha=0.5位置虚拟视点深度图:

(4). 对depthV进行空洞填充。填充后depthV如下图:

(5). 利用depthV和内参矩阵K,反向重复上述操作。将虚拟视点图像imgV坐标点反向投影到三维点,平移,再重投影到参考视图imgL上,在imgL上利用双线性插值得到imgV上的像素值。从而获取虚拟视点图像imgV,如下图:

(6). 综合上述步骤,可以获取alpha从0 到 1, 也就是从左相机位置到右相机位置的一系列虚拟视点图像,gif动图展示如下:

上面效果中在深度不连续区域有较为明显的失真,这是由于该区域为遮挡区域,无法计算出准确的视差值,可以通过观察前面立体匹配博客的视差图或者深度图看出。而采用的空洞填充将附近的视差值填充过去,所以会造成前景出现在背景部分,出现较为明显的伪影。去除伪影也有很多种方法,比如结合右参考相机合成同样位置的虚拟视点,然后将两个视点融合等方法。
其实3D Warping技术的核心就是下面几条语句,二维图像点到三维空间点,平移,旋转,再重投影到新的二维图像点。上面没有加入旋转,旋转的话就是直接用三维点乘以3x3的旋转矩阵后平移,然后再重投影。
float dep = pDepthV[i*imgW + j];
if (dep < 0.001) continue;
//反投影
float X = (j - cx)*dep / fx;
float Y = (i - cy)*dep / fy;
float Z = dep;
//平移
float X2 = X + offsetX;
float Y2 = Y;
float Z2 = Z;
//重投影
float uf = (fx*X2 + cx* Z2) / dep;
float vf = (fy*Y2 + cy * Z2) / dep;
真实场景的虚拟视点合成(View Synthsis)详解的更多相关文章
- 真实场景的双目立体匹配(stereo matching)以及虚拟视点合成(virtual view synthsis)示例
双目立体匹配一直是双目视觉的研究热点,双目相机拍摄同一场景的左.右两幅视点图像,运用立体匹配匹配算法获取视差图,进而获取深度图.而深度图的应用范围非常广泛,由于其能够记录场景中物体距离摄像机的距离,可 ...
- 使用 /proc 文件系统来访问 linux操作系统 内核的内容 && 虚拟文件系统vfs及proc详解
http://blog.163.com/he_junwei/blog/static/19793764620152743325659/ http://www.01yun.com/other/201304 ...
- SurfaceView 与view区别详解
SurfaceView 与view区别详解 https://blog.csdn.net/u011339364/article/details/83347109 2018年10月24日 17:20:08 ...
- View绘制详解(五),draw方法细节详解之View的滚动/滑动问题
关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架.View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程.View的绘制涉及 ...
- View绘制详解(四),谝一谝layout过程
上篇博客我们介绍了View的测量过程,这只是View显示过程的第一步,第二步就是layout了,这个我们一般译作布局,其实就是在View测量完成之后根据View的大小,将其一个一个摆放在ViewGro ...
- View绘制详解(三),扒一扒View的测量过程
所有东西都是难者不会,会者不难,Android开发中有很多小伙伴觉得自定义View和事件分发或者Binder机制等是难点,其实不然,如果静下心来花点时间把这几个技术点都研究一遍,你会发现其实这些东西都 ...
- View绘制详解(二),从setContentView谈起
掐指一算,本来今天该介绍View的测量了,可是要说View的测量,那就要从setContentView谈起了,setContentView本身涉及到的东西也是挺多的,所以今天我们就先来看看这个setC ...
- View绘制详解,从LayoutInflater谈起
自定义View算是Android开发中的重中之重了,很多小伙伴可能或多或少都玩过自定义View,对View的绘制流程也有一定的理解.那么现在我想通过几篇博客来详细介绍View的绘制流程,以便使我们更加 ...
- Android游戏开发之旅 View类详解
Android游戏开发之旅 View类详解 自定义 View的常用方法: onFinishInflate() 当View中所有的子控件 均被映射成xml后触发 onMeasure(int, int) ...
随机推荐
- Log4Net记录到MySql
1.新建控制台程序. 2.添加Log4Net nuget 3.添加MySql 引用 4.添加配置文件如下: <?xml version="1.0"?> <conf ...
- ES6与canvas实现鼠标小球跟随效果
最近闲来无聊,看了下ES6的语法,结合canvas实现了动画特效--随着鼠标的移动,会有小球跟随且自动消失的动画. 首先,html部分,目前就一个canvas标签. <canvas id=&qu ...
- .net 远程调试
1.我们在调试支付接口的时候很多情况下需要请求网关等,这时候需要在服务器上进行调试 首先找到服务器上的msvsmon文件,打开运行,得到对应的端口 然后打开vs,附加进程到远程地址加上端口,然后附加w ...
- Netty ByteBuf梳理
我们知道,网络数据的基本单位总是字节.Java NIO提供了ByteBuffer作为它的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐. Netty的ByteBuffer替代品是ByteBuf, ...
- MarkDown 编辑数学公式
1. 参考博客:http://blog.csdn.net/smstong/article/details/44340637 1 数学公式的web解决方案 在网页上显示漂亮的数学公式,是多年来数学工作者 ...
- vagrant系列教程(二):vagrant的配置文件vagrantfile详解(转)
原文:http://blog.csdn.net/hel12he/article/details/51089774 上一篇文章完整的讲叙了如何安装一个vagrant的环境.这里主要说一说vagrant的 ...
- 2、flask之基础知识点
本篇导航: 路由系统 视图函数 请求与响应 模版语法 session 蓝图(blueprint).闪现 (flash) 扩展 一.路由系统 1.可传入参数: @app.route('/user/< ...
- Socket网络通信之数据传递
Socket通信的步骤: 1.创建ServerSocket和Socket 2.打开连接到Socket的输入/输出流 3.按照协议对Socket进行读/写操作 4.关闭输入输出流.关闭Socket 服务 ...
- python函数的面向对象——面向对象设计
通过几个函数式编号演进,理解面向对象设计 def01.py dog1 = { 'name':'元昊', 'gender':'母', 'type':'藏獒' } dog2 = { 'name':'李李' ...
- PHP调用外部命令
------------------------------------------------------------------ 一.PHP调用外部命令总结 ...