畸变
In geometric optics, distortion is a deviation from rectilinear projection; a projection in which straight lines in a scene remain straight in an image. It is a form of optical aberration.
畸变是像差的一种,它在感官上给人一种错觉(即原本直线变成了曲线),但图像的信息不会丢失。
本文主要介绍畸变的两种处理方式,一种是去畸变,一种是预畸变。
去畸变
根据相机的畸变系数,利用opencv中的undistort函数或者initUndistortRectifyMap和remap相结合即可完成畸变的去除,非常的简单。但是大多数情况下,对于手里的相机,我们是不知道相机的畸变系数,这时候就需要进行畸变标定。
畸变标定
获取畸变系数,通常有以下几种方法:
- 通过相机的参数规格中得到(几乎是废话)
- 基于3D/2D点的映射关系(如,张正友标定大法)
- 基于3D世界中直线在图像中仍是直线(如,铅锤法)
- 其他行业内的高精度标定方法
opencv中采用的是张正友标定法,通过拍摄一系列棋盘格图像,通过多视图几何推导出内参和外参的解析解作为优化的初值,畸变系数初值为0,然后利用LM优化算法,以重投影误差为目标进行优化。
opencv可以对14个畸变系数进行标定,分别为:
k1 k2 k3 k4 k5 k6 径向畸变系数
p1 p2 切向畸变系数
s1 s2 s3 s4 薄棱镜畸变系数
t1 t2 倾斜图像传感器的梯形畸变(Matrix for trapezoidal distortion of tilted image sensor)
其畸变公式,从形式上看布朗模型和除法模型( Fitzgibbon)的结合
x
d
i
s
t
=
x
∗
1
+
k
1
∗
r
2
+
k
2
∗
r
4
+
k
3
∗
r
6
1
+
k
4
∗
r
2
+
k
5
∗
r
4
+
k
6
∗
r
6
+
p
1
∗
(
2
∗
x
∗
y
)
+
p
2
∗
(
r
2
+
2
∗
x
2
)
+
s
1
∗
r
2
+
s
2
∗
r
4
x_{dist} = x * \frac{1 + k1 * r^2 + k2 * r^4 + k3 * r^6}{1 + k4* r^2 + k5 * r^4 + k6 * r^6} + p1 * (2*x*y)+p2*(r^2+2*x^2)+s1*r^2+s2*r^4
xdist=x∗1+k4∗r2+k5∗r4+k6∗r61+k1∗r2+k2∗r4+k3∗r6+p1∗(2∗x∗y)+p2∗(r2+2∗x2)+s1∗r2+s2∗r4
y
d
i
s
t
=
y
∗
1
+
k
1
∗
r
2
+
k
2
∗
r
4
+
k
3
∗
r
6
1
+
k
4
∗
r
2
+
k
5
∗
r
4
+
k
6
∗
r
6
+
p
1
∗
(
r
2
+
2
∗
y
2
)
+
p
2
∗
(
2
∗
x
∗
y
)
+
s
3
∗
r
2
+
s
4
∗
r
4
y_{dist} = y*\frac{1 + k1 * r^2 + k2 * r^4 + k3 * r^6}{1 + k4* r^2 + k5 * r^4 + k6 * r^6} + p1 * (r^2+2*y^2)+p2*(2*x*y)+s3*r^2+s4*r^4
ydist=y∗1+k4∗r2+k5∗r4+k6∗r61+k1∗r2+k2∗r4+k3∗r6+p1∗(r2+2∗y2)+p2∗(2∗x∗y)+s3∗r2+s4∗r4
r
=
x
2
+
y
2
r = \sqrt{x^2+y^2}
r=x2+y2
梯形畸变系数实际上就是两个角度值,即相机sensor绕x轴和y轴旋转的角度。根据这两个旋转角度,计算出变换矩阵。具体实现在opencv中computeTiltProjectionMatrix函数。在这个函数内,通过构造出matRotXY(旋转矩阵)和matProjZ两个矩阵,得到修正梯形畸变的变换矩阵。这一点我不是很明白,希望有懂的朋友留言告知。
布朗模型
其实布朗模型是一个非常复杂的畸变模型。我们平时看到的以及opencv中的畸变模型,都是对布朗模型的一种简化,因为对于现在的相机而言,其中的有些畸变项已经不用再考虑了,如底片弯曲系数。
注:上述来自文献:一种适合摄像机非线性畸变校正的畸变模型
反向映射
在我们看到的大多数文献以及书籍中,给出的畸变模型都是如下形式:
x
u
n
d
i
s
t
=
f
(
x
d
i
s
t
,
y
d
i
s
t
)
x_{undist} = f(x_{dist},y_{dist})
xundist=f(xdist,ydist)
其中
x
u
n
d
i
s
t
x_{undist}
xundist 为理想坐标位置,
x
d
i
s
t
x_{dist}
xdist 为畸变后的坐标位置
但是,opencv中用到的确是如下形式:
x
d
i
s
t
=
f
(
x
u
n
d
i
s
t
,
y
u
n
d
i
s
t
)
x_{dist} = f(x_{undist},y_{undist})
xdist=f(xundist,yundist)
其实两者之间是一致的,都是在描述畸变前后坐标点之间的一种特性(如径向畸变与坐标到中心半径的偶次幂有关,也可看做一种多项式模型,只不过多项式模型参数太多),这一点在《计算机视觉-算法和应用》中P256页有提到,只不过两种公式得到的畸变系数会截然不同。比如:我们常看到的对于桶形畸变,k1为负值,这对应的就是大多数文献及书籍提到的畸变模型,对于opencv中的畸变模型,桶形畸变的k值应为正值。
当然,opencv中这样做的目的就是方便利用反向映射的方法,避免去畸变后的图像出现奶酪图(有些像素点像素值变为0)。
预畸变
对于VR/AR、HMD等头显设备,为了让人眼能够聚焦到前方指甲大小的屏幕上,在人眼和屏幕之间增加了透镜。但带来的问题就是屏幕上原本的直线经过透镜汇聚到人眼后,变成了曲线。
预畸变就是根据透镜的畸变特性,提前对屏幕上要显示的画面进行反向畸变(如添加桶形畸变),来抵消透镜带来的畸变(如枕型畸变),这样人眼看到画面就会变的正常。预畸变原理如下图:
畸变特性
其实预畸变算法的开发,主要就是畸变特性的获取。畸变特性的获取主要方法有:
- 通过光学仿真软件进行仿真,如Zemax、CODEV等。这些光学软件首先构建一个畸变网格(Distortion Grid),针对每一个网格点通过追踪近轴光线和实际光线,获得理想坐标和畸变坐标。这一对对理想坐标和畸变坐标之间的关系,就是我们需要的畸变特性。
- 板靶实验。通过实际实验,获取透镜前目标的位置和透镜后的成像位置之间的坐标对应关系。
- 基于3D/2D点的映射关系。利用已标定内参的相机,拍摄固定位置的标定板(外参固定),获得一系列对应的3D和2D点,通过非线性优化的方式获得畸变特性。
畸变模型
利用上述获得的畸变特性数据进行建模,建模的好处是:
- 畸变网格可能过疏或过密,通过建立畸变模型,可以计算出任意网格点反畸变后的坐标位置。当然,光学仿真软件中的畸变网格大小是可以调节的。
利用matlab,选择如下畸变模型进行拟合,获得畸变系数。
x
d
i
s
t
=
f
(
x
u
n
d
i
s
t
,
y
u
n
d
i
s
t
)
x_{dist} = f(x_{undist},y_{undist})
xdist=f(xundist,yundist)
需要注意的是,用于拟合畸变模型的坐标数据和最终处理的像素位置之间的单位转化。
实现思路
预畸变的主要步骤:
1、对图像进行网格划分(对于1080p,32×32网格已经足够)
2、对每个网格点应用畸变模型,得到其反畸变后的坐标位置
3、进行双线性插值得到反畸变后图像
从整个流程可以看出,对于每一个网格点,后续的所有操作都是一致的。因此,利用GPU的并行能力配合OpenGL出色的插值算法,进行预畸变是一个更好的选择。
因此,即使我们选择的畸变模型是从理想坐标到畸变后的坐标,预畸变结果也不会出现奶酪图,因为OpenGL的片段着色器帮助我们进行了插值采样。
总结一句就是:我们在顶点着色器中计算每个网格点畸变后的位置坐标,在片段着色器中根据理想坐标位置进行采样。
最后
这篇blog只是简单介绍了一些概念及本人在反畸变开发中的最初的一些疑惑,在这里分享出来。如有不严谨或错误的地方,还望在评论中指出。后续,我会对opencv中一些畸变计算的源代码进行分享(如,opencv中如何去畸变,相机标定是如何获得的畸变系数,如何利用迭代的思路求解畸变方程等等)。
一些参考
- https://www.imaginationtech.com/blog/speeding-up-gpu-barrel-distortion-correction-in-mobile-vr/
- https://www.gamasutra.com/blogs/BrianKehrer/20160125/264161/VR_Distortion_Correction_using_Vertex_Displacement.php
- https://smus.com/vr-lens-distortion/
- https://www.shadertoy.com/view/4sXcDN
- https://blog.csdn.net/dabenxiong666/article/details/78713357
- https://www.docin.com/p-769165073.html