180度vr直播用,鱼眼视频展开到全景

对于180度VR的直播,一般来说低成本的做法是采用鱼眼镜头拍摄整个180度的视频。

鱼眼镜头成像过程:


不同的鱼眼镜头有不同的成像方式,我们采用的也是比较常用的等距投影模型。


对于输入来说,由于镜头的成像范围,我们的输入是一部分的鱼眼图。输出我们希望是一个全景图,该图可以在带上vr头盔后以180度范围观看全景。也就是鱼眼到全景的转换。

关于鱼眼展开到全景图的算法,采用了经纬度展开的方法。其实主要工作就是找到输出图像上各个像素到输入图像上的映射位置,而这个位置可以通过等距投影模型推算。

鱼眼镜头采样范围一般不止180度,大约183度,但是我们没有找到合适的方法来检查鱼眼镜头的实际成像范围,所以目前按180度进行映射。实际效果看不出什么问题。

首先看我们的输入图像,是不完全的鱼眼图(上下有缺失):




第一步,找到鱼眼成像中,圆的半径:

1.对图像从左边往右边平移一条直线,计算这条直线上的max和min的差值,如果超过阈值,则判断为圆的左边界。同理可以找到圆的右边界。

2.对图像从左到右平移一条带有角度的直线,找到这个角度上,圆的直径。

3.根据上面两个值可以找到圆心和半径,圆心就是两条直径的交点。


第二步,就是最主要的映射了。

首先,明确我们的输入和输出。先说输出,对于一个180度全景图,相当于一个半球,它的宽高比应该是1:1。找一个书本上的球,如下图,我们直观理解:这个半球在三维坐标系中,水平范围是180度[0,180°],垂直范围也是180度[-90°,90°],水平垂直的范围相同。将水平垂直坐标归一化到[0,1],假设输出图像的宽高为out_len,我们要找的映射关系则是以 (1/ out_len) 为间隔,将整个球面投影到一个正方形上。这其实就是目前常见vr视频的物理意义。


所以,我们要做的映射过程如下:

[cpp]  view plain  copy
  1. // out yuv height/width is out_len * out_len  
[cpp]  view plain  copy
  1. float x, y;  
  2. for (x=0.0;x<=1.0;x+=(1/out_len))  
  3. {  
  4.     for (y=0.0;y<=1.0;y+=(1/out_len))  
  5.     {  
  6.         //map each pixel   
  7.     }  
  8. }  

接下来就是如何将每一个输出图像对应坐标的像素,映射到输入图像上。

首先我们先理解如何从鱼眼成像后的图得到其三维球面上的点。可以根据等距投影模型了解到,鱼眼成像的圆形中,任意一个像素到原点的距离,都可以推出其在球面坐标上距离y轴和x轴的角度,也就是纬度和精度(因为弧度和角度是线性关系)。


以一个任意值输出图像点(x, y) 为例, 0<x<1,0<y<1.

1. 首先,对x做一个颠倒,类似坐标系颠倒的变换, x = 1-x;

2. 得到当前点在球面上的经纬度:(请参考上面提到的全景图和球面坐标的对应关系,直观理解就是角度关系) 

  latitude = y*π; 

  longitude = x*π;

3. 我们在三维空间建立一个半径1的球方程,然后找到(x, y)点对应三维坐标中的投影点P(xt, yt, zt)。这个三维坐标如下图(我怎么画的这么渣啊,下次还是找书上的图拍照好了 T_T)


描述一下,这个图,建立在右手坐标系,纬度latitude对应的几何意义是P点投影到xoy面后,和Y轴正方向的夹角。经度latitude对应的几何意义是P点到XZ平面的投影点P' 与X轴负方向的夹角。

球坐标方程如下:

[cpp]  view plain  copy
  1. xt = -sin(latitude)*cos(longitude);  
  2. yt = cos(latitude);  
  3. zt = sin(latitude)*sin(longitude);  

可以直观理解这个球面上的点是鱼眼成像前的点,然后通过镜头以等距投影方式投影到XY坐标上了。


4. 接下来,就是求P点到鱼眼上那个圆中的映射。这里理解请尽量按几何的方式理解公式,会很直观

首先,等距投影其实可以理解为将球面上一圈一圈的像素像同心圆一样展开。那么,我们就需要先找到P点所在同心圆的半径。这里的π是我们假定的输入角度π,cirR是鱼眼圆的半径

[cpp]  view plain  copy
  1. r = cirR * acos(zt)/ (π/2)  
其实就是由P点与Z轴正方向的夹角,得到同心圆的半径r。

接下来计算该点投影在XY平面后,与X轴正方向的夹角,

[cpp]  view plain  copy
  1. theta = atan2f(yt, xt);  
在二维平面上,根据角度和半径,我们可以得到这个圆的坐标了

[cpp]  view plain  copy
  1. x_fish = r*cos(theta);  
  2. y_fish = r*sin(theta);  

根据圆心的坐标(cx, cy)做平移

[cpp]  view plain  copy
  1. u = x_fish + cx;  
  2. v = -y_fish + cy;  
于是,我们得到了输入点(x, y) 对应鱼眼输入图像的位置 (u, v)。 注意,这是个非整数点。为了得到可以接受的效果,需要对其做3x3的二维滤波。否则边缘没法看。滤波过程这里就不赘述了。


最后的输出图像如下,做成码流在vr头盔上旋转观看180度,完全没有问题。超出拍摄范围的一概填黑




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值