概述
市面上许多应用屈光方法构造的全景相机由于存在非零视差不允许离相机很近的对象进行无缝拼接。即使目前有许多主流的视频拼接算法来解决这类问题,但也会带来额外的运算开销,因此尝试以设计反射式金字塔结构的方式来消除或是减小视差以得到更好的拼接效果。3D 全景双目立体相当于两个 2D 全景摄像机,这两个 2D 全景摄像机分别用于拍摄左眼和右眼的全景画面,从而能够还原出有深度信息的 3D 全景视频。 3D双目立体全景摄像机可以营造出令人震撼的沉浸感,它是拍摄 VR 电影的基本要求。为了降低算法复杂度的代价,采用 3D 全景双目立体的方式来开展后续的工作。
效果
- 3D全景视频通过对同一场景分别投影进行浏览
- 3D全景渐变式展示
通过镜面反射装置设计使得拍摄得到场景模拟左右眼真实所见
- 蓝色为右眼全景图,红色为左眼全景图。可以看到距离越近的物体存在的视差越大,距离越远的物体视差越小
工作原理

硬件设计
对镜面反射式立体成像的硬件结构进行阐述。核心是通过虚拟光心模拟人眼视差实现模拟立体成像
光心
图1中12的点表示12个虚拟光心,红色和蓝色射线分别代表左右眼能捕捉的场景范围,场景重叠区域则为立体成像区。A,A’为一对左右眼的虚拟视点,六棱形的边框代表6面镜子。其中A,A’的距离模拟人眼设计,这里设为70mm,后续的设计均以这个约束为基础。6对左右眼的图像对分别进行拼接,模拟真实人眼所观察到世界,以得到真实的立体全景图像。
偏移角度
由于相机水平视场角需要覆盖单个镜面所反射的真实场景,因此相机需要有所偏移。这里截取一对左右眼虚拟视点和一面镜子作分析。A,A’为一对左右眼虚拟视点,M’N’为镜子,h为虚拟视点到镜子的距离,r为对称中心到镜子的距离,R为M’N’的距离,
φ
\varphi
φ 为需偏移的角度,d为AA’的距离,h为虚拟光心到镜面的距离。由于虚拟视点连线为12等边形,易得角OAA’为15度。则
φ
\varphi
φ
关系式如下:
h = ∣ A E ∣ = ∣ C F ∣ h = \left| {AE} \right| = \left| {CF} \right| h=∣AE∣=∣CF∣
h
=
r
−
d
2
tan
∠
O
A
A
′
=
r
−
6
−
2
8
d
h = r - \frac{d}{2}\tan \angle OAA' = r - \frac{{\sqrt 6 - \sqrt 2 }}{8}d
h=r−2dtan∠OAA′=r−86−2d
φ
=
1
2
(
∠
N
′
A
E
−
∠
M
′
A
E
)
=
1
2
(
arctan
R
+
d
2
h
−
arctan
R
−
d
2
h
)
\varphi = \frac{1}{2}(\angle N'AE - \angle M'AE) = \frac{1}{2}(\arctan \frac{{R + d}}{{2h}} - \arctan \frac{{R - d}}{{2h}})
φ=21(∠N′AE−∠M′AE)=21(arctan2hR+d−arctan2hR−d)
当邻光心距离固定为人眼瞳距70mm时,虚拟光心到镜面的距离h为14.8cm,相机偏移角度 φ \varphi φ 为 9. 8 ∘ {9.8^ \circ } 9.8∘。也就是说拼接完的右路全景图需要向反方向偏移2 φ \varphi φ 即 19. 6 ∘ {19.6^ \circ } 19.6∘
大小关系
我们很关注的一点是镜面框架的大小,下面进行具体的理论分析。如图3所示,H为框架顶部距离底部的距离,h为虚拟光心到镜面的距离,
ν
\nu
ν为垂直视场角,
δ
\delta
δ为由于相机遮挡损失的视场角,
θ
\theta
θ为框架倾斜的角度。a为相机的直径。
2 h sin θ sin δ = a 2 sin ( θ − δ ) 2h\sin \theta \sin \delta = \frac{a}{2}\sin (\theta - \delta )\ 2hsinθsinδ=2asin(θ−δ)
sin 2 δ = a 2 4 sin 2 θ 4 h 2 sin 2 θ + 2 h a sin θ + a 2 4 = a 2 8 ( 1 − cos 2 θ ) 2 h 2 ( 1 − cos 2 θ ) + h a sin 2 θ + a 2 4 {\sin ^2}\delta = \frac{{\frac{{{a^2}}}{4}{{\sin }^2}\theta }}{{4{h^2}{{\sin }^2}\theta + 2ha\sin \theta + \frac{{{a^2}}}{4}}} = \frac{{\frac{{{a^2}}}{8}(1 - \cos 2\theta )}}{{2{h^2}(1 - \cos 2\theta ) + ha\sin 2\theta + \frac{{{a^2}}}{4}}}\ sin2δ=4h2sin2θ+2hasinθ+4a24a2sin2θ=2h2(1−cos2θ)+hasin2θ+4a28a2(1−cos2θ)
当相邻光心距离固定为人眼瞳距70mm时,通过关系式(2)可以得出h,因为相机直径a是已知量,由几何关系式(4)与(5)可以在控制框架倾斜的角度
θ
\theta
θ的同时得出损失视场角
δ
\delta
δ的值。
则垂直视场角与相机高度如式(6)与(7)所示:
ν
=
2
(
π
2
−
θ
−
δ
)
\nu = 2(\frac{\pi }{2} - \theta - \delta )\
ν=2(2π−θ−δ)
H
=
r
tan
θ
+
h
cot
(
ν
2
)
−
cot
(
θ
)
H = r\tan \theta + \frac{h}{{\cot (\frac{\nu }{2}) - \cot (\theta )}}\
H=rtanθ+cot(2ν)−cot(θ)h
H代表反射镜面装置的高度,W代表装置的最大横截面积, H 0 {H_0} H0代表镜面有效区域的高度。由图4可以看出,相机整体大小随框架倾斜的角度 θ \theta θ增大而减小,然而垂直视场角也相对减小。试验中,为权衡两者关系,将 θ \theta θ设为52.8,则垂直视场角为 6 0 ∘ {60^ \circ } 60∘,相机有效高度为22.4cm,最大横截面为54.7cm
全景相机由13个相机组成,其中12个相机拍摄水平方向的场景,1个相机拍摄上方的场景。每6个相机向上的相机通过拼接流程形成一幅全景图。相邻6个相机与共享的向上的相机形成另一幅全景图,两幅全景图通过虚拟现实头戴装置分别投向左右眼即可得到立体效果。
实验设备包括上节所得参数的铝合金框架,6块上底120mm下底476mm高457mm厚2mm的正面镜,一个10cm英制3/8云台螺丝,12个m6相机螺丝,41个m8玻璃固定螺丝,12个mindvision公司 像素:500w usb3.0接口 镜头 约90度鱼眼镜头和1个向上拍摄的220度鱼眼镜头,服务器:IPC-1497M 17存便携式工控机箱。
具体可参见我的ICVRV2017论文 A virtual reality video stitching system based on mirror pyramids
附github地址,包含论文和硬件设计的代码:https://github.com/1993zlorange/A-virtual-reality-video-stitching-system-based-on-mirror-pyramids
标定
全景成像关键步骤在与相机的标定。本文提出了基于刚性约束的标定方法,利用多块标定板,通过多次旋转相机,可以同时标定出相机的内外参数。
我的标定方法发在CISP-BMEI2017名为A-video-stitching-system-based-on-mirror-pyramids-and-non-overlapping-calibration-method。具体文章+代码附github地址: https://github.com/1993zlorange/A-video-stitching-system-based-on-mirror-pyramids-and-non-overlapping-calibration-method
CSDN中文代码地址,有兴趣同学可以友情赞助一下: https://download.csdn.net/download/weixin_42641395/10648243
需要
- matlab 标定工具包
入口 :entrance.m(包括误差分析)
- 解压TRC_left,TRC_right到TRC_1_x
- 依次做pose_estimating得出相对旋转矩阵
- 生成投影随机点放入m1
- 通过T_12(欧拉角转换为旋转矩阵),TRC_1_x,TRC_2_x进行重投影
- 计算误差
*计算重投影后x,y坐标的偏移aver_errorx12, aver_errory12
*计算距离 n_picture_x,aver_error_norm12_y,aver_error_norm13_y, aver_error_norm14_y
计算相对运动参数(pose_estimating.m)
- 首先求解内参矩阵
- 取出标定板相对每次姿态的运动参数与相机的内参数Rc_left,Tc_left
- 计算相对姿态的运动参数放入TRc_left,TTc_left
- 求解相对旋转矩阵R_relative(欧拉角存储)
- 存储TRC_left, TRC_right(放入一个矩阵)
拼接
本文全景成像采用多个相机同时拍摄,它们最终获得的场景的序列图像由于是在不同方向上拍摄得到的,其投影平面存在一定的夹角。因此,需要首先将它们投影到同一坐标系中。再通过融合算法对重叠的部分进行优化。
下面是一些具体拼接方面的工作,并附有程序的一些说明,是原工程,有兴趣的同学可以到CSDN下载看一下。
CSDN调用投影变换库进行图像拼接代码地址:https://download.csdn.net/download/weixin_42641395/10649320
CSDN全景立体视频拼接(分别拼接形成左右眼全景视频)代码地址:https://download.csdn.net/download/weixin_42641395/10649227
依赖项
- opencv 3
使用opencv主要使用了其MAT类 以及权重,缝合线算法
opencv 3 编译细节
-
panomath 库(投影变换库)
使用panomath库主要使用了其鱼眼到经纬线坐标系的投影变换 -
tinyxml(xml读取的基础库)
-
mindvision 相机
本程序是基于mindvision相机sdk编写的,因此硬件上相机选择mindvision相机 -
panostitcher
-
读取xml
-
读取参数
-
投影变换
-
VFW32 (opencv视频输出)
#pragma comment( lib, "VFW32.LIB")
程序搭建
- 配置opencv 3
属性>配置属性>VC++目录>包含目录
F:\opencv3.0\opencv3\install\include\opencv2
F:\opencv3.0\opencv3\install\include\opencv
F:\opencv3.0\opencv3\install\include
属性>配置属性>VC++目录>库目录
F:\opencv3.0\opencv3\install\x64\vc12\lib
属性>链接器>输入
opencv_calib3d320d.lib
opencv_core320d.lib
opencv_cudaarithm320d.lib
opencv_cudabgsegm320d.lib
opencv_cudacodec320d.lib
opencv_cudafeatures2d320d.lib
opencv_cudafilters320d.lib
opencv_cudaimgproc320d.lib
opencv_cudalegacy320d.lib
opencv_cudaobjdetect320d.lib
opencv_cudaoptflow320d.lib
opencv_cudastereo320d.lib
opencv_cudawarping320d.lib
opencv_cudev320d.lib
opencv_features2d320d.lib
opencv_flann320d.lib
opencv_highgui320d.lib
opencv_imgcodecs320d.lib
opencv_imgproc320d.lib
opencv_ml320d.lib
opencv_objdetect320d.lib
opencv_photo320d.lib
opencv_shape320d.lib
opencv_stitching320d.lib
opencv_superres320d.lib
opencv_video320d.lib
opencv_videoio320d.lib
opencv_videostab320d.lib
- panomath 库以及 xml
属性>配置属性>VC++目录>包含目录
C:\Users\Administrator\Desktop\OpenCV\transform1.5\include
C:\Users\Administrator\Desktop\OpenCV\tinyxml
解决方案资源管理器中右键资源文件->添加->现有项
transform1.5->cpp中所有文件
tinyxml中所有cpp文件
- VFW32
下载并在Opencv.cpp总添加
#pragma comment( lib, "VFW32.LIB")
操作流程
- kolor 拼接左右全景图获取.pano文件,将.pano后缀改成.xml
- 找到OpenCV.cpp中 uiDisplayThread_firstframe_left函数 与uiDisplayThread_firstframe_right函数,修改文件名
string filename = "your name.xml";
流程
相机采用的是12个mindvision公司 像素:500w usb3.0接口 镜头 约90度鱼眼镜头和1个向上拍摄的220度鱼眼镜头,服务器:IPC-1497M 17存便携式工控机箱。因此对相机操作是研究调用了mindvision公司的开源接口。以下是实现视频拼接的具体流程。
_tmain
- 枚举设备
- 相机初始化 (赋予相机句柄)句柄包含着相机参数信息
- 从句柄中提取相机参数
- 分配缓存(根据相机参数中的图像大小分配一个缓存的大小)
- 通过相机句柄建立属性页面
- 打开相机开始传送视频信号
- 线程
- 第一帧:投影变换计算左边对应模板
uiDisplayThread_firstframe_left
uiDisplayThread_firstframe_right
- 开始线程函数(功能函数)
- 从相机中提取帧到pdybuffer中(pdybuffer是mindvision公司定义的一个内存缓存变量)
- pdybuffer中的数据通过图像处理到m_pFrameBuffer中(m_pFrameBuffer是mindvision公司定义的一个临时放帧的缓存变量)
- 提取缓存到 opencv 格式iplImage中并转换成mat(1帧1个mat)
- 释放pdybuffer iplImage
uiDisplayThread_left
uiDisplayThread_right
- 缓存帧通过模板投影变换到经纬线坐标系
- 从相机中提取帧到pdybuffer中
- pdybuffer中的数据通过图像处理到m_pFrameBuffer中
- 提取缓存到 opencv 格式iplImage中并转换成mat(1帧1个mat)
- 释放pdybuffer iplImage
- 相机反初始化
变量
- m_hCamera:相机句柄(全局变量)
- m_pFrameBuffer:缓存(全局变量)
- sCameraInfo:相机参数
- CameraCreateSettingPage:属性界面接口函数
首帧线程流程
- 读入信号,上下旋转后到MAT缓存
- 新建(2:1)的pano缓存
- 读入相机参数loadParas
- 每个缓存MAT投影变换到pano大小的全景坐标系缓存
- 各个全景坐标系缓存叠加进行融合操作
- 寻找缝合线
- 计算权重
- 需要在外部访问(线程不再只处理图像,因此需要外部调用),因此设成public
vector m_maps_x;
vector m_maps_y;
vector m_maps_weight;
浏览
作为观众进行观看时需要将全景视频通过透视投影变换投影到人眼中。左右眼全景视频进行投影实现模拟人眼立体效果。
如上图所示,上图为左眼全景图,下图为右眼全景图。方框部分为分别投射到左右眼的透视图像。可以看到,即使
离相机十分近的物体也能够很好地达到拼接效果,同时,左右眼所看到的场景并不一样,真实地模拟了人眼所见到的场景,不同于单纯地将全景图做平移而生成的伪 3D。
镜面反射所产生的立体视频垂直视场角为 60 度,然而我们对于向上的场景没有特别高的需求因为向上的场景一般为天空等没有特征的信息。因此我们向上设置一个摄像机,两幅立体视频对共享向上的场景信息,从而增大了垂直视场角,
投影变换原理
投影变换逆映像的流程如图下所示:
最终的目的是遍历 image_output 的坐标,再通过对应(x_in,y_in)=f(x_out,y_out).图像到世界坐标系关键的是确定 f即比例。世界到图像坐标系关键的是确定世界点的欧拉角 yaw,pitch 与坐标( x,y,z)的关系。注意这里的世界坐标系是归一化的,即
x
2
+
y
2
+
z
2
=
1
{x^2}{\rm{ + }}{{\rm{y}}^2} + {z^2} = 1
x2+y2+z2=1,所以这个变换是光心不变的。
因为投影变换算法较多,并且需要经常使用,因此自己写了一个投影变换库,附gitbub地址:https://github.com/1993zlorange/projection-transforming
以下是原工程以及库的框架的说明,上传至CSDN
CSDN投影变换库代码,附的是原工程:https://download.csdn.net/download/weixin_42641395/10648248
CSDN浏览DEMO代码地址:https://download.csdn.net/download/weixin_42641395/10649199
底层算法
- 头文件
- img2wrd.h 图像到世界坐标系
- wrd2img.h 世界坐标系到图像
- imgout2imgin_cor.h 输出图像到输入图像的坐标对应
- matrix.h 矩阵运算
- 源文件
- img2wrd_func.cpp 图像到世界坐标系
- wrd2img_func.cpp 世界坐标系到图像
- imgout2imgin_cor_func.cpp 输出图像到输入图像的坐标对应
- matrix.cpp 矩阵运算
上层应用库(需要opencv支持)
- imgin2imgout_img.h
- imgin2imgout_img_func.cpp
Sample
- test_img.cpp
投影变换库中的类说明:
- 基础数学库:
- 图像坐标系到世界坐标系:img2wrd
- 世界坐标系到图像坐标系:wrd2img
- 输出图像到输入图像坐标的变换:imgout2imgin_cor
- 上层应用库:
- 输入图像到输出图像图像的变换:imgin2imgout_img
总结
以上是硕士阶段做的一个比较有意思的工作,因为那年是虚拟现实元年,因此与老师商讨做的这个全景立体成像的课题,得到的视频可以实时在虚拟现实眼镜中观看。再次感谢实验室的老师和师兄弟们。现与大家分享。
干货
硬件设计
具体可参见我的ICVRV2017论文 A virtual reality video stitching system based on mirror pyramids
附github地址,包含论文和硬件设计的代码:https://github.com/1993zlorange/A-virtual-reality-video-stitching-system-based-on-mirror-pyramids
##标定
我的标定方法发在CISP-BMEI2017名为A-video-stitching-system-based-on-mirror-pyramids-and-non-overlapping-calibration-method。具体文章+代码附github地址:https://github.com/1993zlorange/A-video-stitching-system-based-on-mirror-pyramids-and-non-overlapping-calibration-method
CSDN中文代码地址,有兴趣同学可以友情赞助一下:https://download.csdn.net/download/weixin_42641395/10648243
##拼接
下面是一些具体拼接方面的工作,是原工程,有兴趣的同学可以到CSDN下载看一下。
CSDN调用投影变换库进行图像拼接代码地址:https://download.csdn.net/download/weixin_42641395/10649320
CSDN全景立体视频拼接(分别拼接形成左右眼全景视频)代码地址:https://download.csdn.net/download/weixin_42641395/10649227
投影浏览
因为投影变换算法较多,并且需要经常使用,因此自己写了一个投影变换库,附gitbub地址:https://github.com/1993zlorange/projection-transforming
以下是原工程,上传至CSDN
CSDN投影变换库代码,附的是原工程:https://download.csdn.net/download/weixin_42641395/10648248
CSDN浏览DEMO代码地址:https://download.csdn.net/download/weixin_42641395/10649199