镜头畸变模型
参考opencv文档 https://docs.opencv.org/3.1.0/d4/d94/tutorial_camera_calibration.html,opencv在相机标定时的采用的是带有三个参数的径向畸变模型和两个参数的切向畸变模型,如下:
去畸变undistortPoints
opencv对特征点去畸变的原理是迭代法的一种:不动点迭代求解非线性方程的根,当然也可以用高斯牛顿法去畸变(参见另一篇博文)。不动点迭代法是将 f ( x ) = 0 f(x)=0 f(x)=0等价转换为 x = ψ ( x ) x=\psi(x) x=ψ(x)的形式,进而构造出迭代式的方法,但并不是所有的迭代形式都可以,需要满足迭代收敛的条件 ∣ ψ ′ ( x ) ∣ < 1 |\psi'(x)|<1 ∣ψ′(x)∣<1。
将opencv从无畸变点到有畸变点的过程表达为如下
{
x
′
=
x
(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
)
+
[
2
p
1
x
y
+
p
2
(
r
2
+
2
x
2
)
]
y
′
=
y
(
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
)
+
[
p
1
(
r
2
+
2
y
2
)
+
2
p
2
x
y
]
(1)
\left\{ \begin{array}{c} x'=x(1+k_1r^2+k_2r^4+k_3r^6)+[2p_1xy+p_2(r^2+2x^2)] \\ y'=y(1+k_1r^2+k_2r^4+k_3r^6)+[p_1(r^2+2y^2)+2p_2xy] \\ \end{array}\tag{1} \right.
{x′=x(1+k1r2+k2r4+k3r6)+[2p1xy+p2(r2+2x2)]y′=y(1+k1r2+k2r4+k3r6)+[p1(r2+2y2)+2p2xy](1)
其中
x
′
,
y
′
x', y'
x′,y′为有畸变点,
x
,
y
x, y
x,y为无畸变点,
r
2
=
x
2
+
y
2
r^2=x^2+y^2
r2=x2+y2。写成矩阵的形式为
[
x
′
y
′
]
=
[
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
]
[
x
y
]
+
[
2
p
1
x
y
+
p
2
(
r
2
+
2
x
2
)
p
1
(
r
2
+
2
y
2
)
+
2
p
2
x
y
]
(2)
\begin{bmatrix} x' \\ y' \\ \end{bmatrix}=[1+k_1r^2+k_2r^4+k_3r^6]\begin{bmatrix} x \\ y \\ \end{bmatrix}+\begin{bmatrix} 2p_1xy+p_2(r^2+2x^2) \\ p_1(r^2+2y^2)+2p_2xy \\ \end{bmatrix}\tag{2}
[x′y′]=[1+k1r2+k2r4+k3r6][xy]+[2p1xy+p2(r2+2x2)p1(r2+2y2)+2p2xy](2)
简化形式为
F
(
X
)
⋅
X
+
G
(
X
)
−
X
′
=
0
(3)
F(X) \cdot X+G(X)-X'=0\tag{3}
F(X)⋅X+G(X)−X′=0(3)
其中,
X
=
[
x
y
]
,
X
′
=
[
x
′
y
′
]
(4)
X=\begin{bmatrix} x \\ y \\ \end{bmatrix}, X'=\begin{bmatrix} x' \\ y' \\ \end{bmatrix}\tag{4}
X=[xy],X′=[x′y′](4),
F
(
X
)
=
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
(5)
F(X) = 1+k_1r^2+k_2r^4+k_3r^6\tag{5}
F(X)=1+k1r2+k2r4+k3r6(5),
G
(
X
)
=
[
2
p
1
x
y
+
p
2
(
r
2
+
2
x
2
)
p
1
(
r
2
+
2
y
2
)
+
2
p
2
x
y
]
(6)
G(X) = \begin{bmatrix} 2p_1xy+p_2(r^2+2x^2) \\ p_1(r^2+2y^2)+2p_2xy \\ \end{bmatrix}\tag{6}
G(X)=[2p1xy+p2(r2+2x2)p1(r2+2y2)+2p2xy](6)
根据式(3)构造不动点等价形式为
X
=
X
′
−
G
(
X
)
F
(
X
)
(7)
X=\frac{X'-G(X)}{F(X)}\tag{7}
X=F(X)X′−G(X)(7)
构造迭代形式为
X
k
+
1
=
X
′
−
G
(
X
k
)
F
(
X
k
)
(8)
X_{k+1}=\frac{X'-G(X_k)}{F(X_k)}\tag{8}
Xk+1=F(Xk)X′−G(Xk)(8)
该迭代式子是否满足收敛条件我还没有进行验证,在去畸变迭代计算时,将当前已知的有畸变点
X
’
X’
X’作为初始值,即
X
0
=
X
′
X_0=X'
X0=X′,然后根据式(8)迭代计算无畸变的点,直到满足迭代误差或者最大迭代次数条件。
附上opencv中相关的一段代码,标注上主要的注释
if (_distCoeffs)
{
// compensate tilt distortion
cv::Vec3d vecUntilt = invMatTilt * cv::Vec3d(x, y, 1);
double invProj = vecUntilt(2) ? 1. / vecUntilt(2) : 1;
x0 = x = invProj * vecUntilt(0);
y0 = y = invProj * vecUntilt(1);
double error = 1.7976931348623158e+308;
// compensate distortion iteratively
//注1: 开始循环迭代去除畸变
for (int j = 0;; j++)
{
//注2:一个是最大迭代次数条件
if ((criteria.type & cv::TermCriteria::COUNT) && j >= criteria.maxCount)
break;
//注3:一个是迭代最大误差条件
if ((criteria.type & cv::TermCriteria::EPS) && error < criteria.epsilon)
break;
//注4:在只有k1,k2,k3,p1,p2共5个畸变参数时,对应的k数组中只有k[0]~k[4]有值,其他都为0
double r2 = x*x + y*y;
double icdist = (1 + ((k[7] * r2 + k[6])*r2 + k[5])*r2) / (1 + ((k[4] * r2 + k[1])*r2 + k[0])*r2);
double deltaX = 2 * k[2] * x*y + k[3] * (r2 + 2 * x*x) + k[8] * r2 + k[9] * r2*r2;
double deltaY = k[2] * (r2 + 2 * y*y) + 2 * k[3] * x*y + k[10] * r2 + k[11] * r2*r2;
//注5:形如式(8)的迭代
x = (x0 - deltaX)*icdist;
y = (y0 - deltaY)*icdist;
if (criteria.type & cv::TermCriteria::EPS)
{
double r4, r6, a1, a2, a3, cdist, icdist2;
double xd, yd, xd0, yd0;
cv::Vec3d vecTilt;
//注6:将第k次计算的无畸变点代入畸变模型,得到于当前有畸变的偏差
r2 = x*x + y*y;
r4 = r2*r2;
r6 = r4*r2;
a1 = 2 * x*y;
a2 = r2 + 2 * x*x;
a3 = r2 + 2 * y*y;
cdist = 1 + k[0] * r2 + k[1] * r4 + k[4] * r6;
icdist2 = 1. / (1 + k[5] * r2 + k[6] * r4 + k[7] * r6);
xd0 = x*cdist*icdist2 + k[2] * a1 + k[3] * a2 + k[8] * r2 + k[9] * r4;
yd0 = y*cdist*icdist2 + k[2] * a3 + k[3] * a1 + k[10] * r2 + k[11] * r4;
vecTilt = matTilt*cv::Vec3d(xd0, yd0, 1);
invProj = vecTilt(2) ? 1. / vecTilt(2) : 1;
xd = invProj * vecTilt(0);
yd = invProj * vecTilt(1);
double x_proj = xd*fx + cx;
double y_proj = yd*fy + cy;
error = sqrt(pow(x_proj - u, 2) + pow(y_proj - v, 2));
}
}
}