高斯牛顿法去畸变(C++实现)

1. 背景

在实际三维重建时,通常会遇到如下问题:
已知有畸变的点坐标 x ′ , y ′ x',y' x,y,已知畸变模型 f ( x , y ) , g ( x , y ) f(x,y),g(x,y) f(x,y),g(x,y), 求无畸变的点坐标 x , y x,y x,y。畸变模型为
x ′ = x + f ( x , y ) y ′ = y + g ( x , y ) \begin{matrix} x'=x+f(x,y) \\ y'=y+g(x,y) \end{matrix} x=x+f(x,y)y=y+g(x,y)
对于畸变模型通常是非线性函数来说,通过左边的有畸变坐标解算无畸变坐标,解析方法难度很大。因此,数值求解的方法更适用于去畸变的情况。

2. 一维高斯牛顿法

先来看该问题的一维表示
y = x + f ( x ) y=x+f(x) y=x+f(x)
相当于已知 y y y f ( x ) f(x) f(x),求解x。令
F ( x ) = x + f ( x ) − y F(x) =x+f(x)-y F(x)=x+f(x)y
该问题等价求 F ( x ) = 0 F(x)=0 F(x)=0时的x, 在数值分析上就是求最小化时的x,即
x = a r g m i n ∣ ∣ F ( x ) ∣ ∣ x=argmin{||F(x)||} x=argminF(x)
为了求导的方便,通常等价为如下形式
x = a r g m i n 1 2 ∣ ∣ F ( x ) ∣ ∣ 2 x=arg\rm{min}{\frac{1}{2}||F(x)||^2} x=argmin21F(x)2
F ( x ) F(x) F(x) x x x处进行泰勒展开
F ( x + Δ x ) = F ( x ) + F ′ ( x ) Δ x + F ′ ′ ( x ) 2 Δ x 2 + . . . F(x+\Delta{x})=F(x)+F'(x)\Delta{x}+\frac{F''(x)}{2}\Delta{x}^2+... F(x+Δx)=F(x)+F(x)Δx+2F(x)Δx2+...
高斯牛顿法的思想是取其一阶线性近似为
F ( x + Δ x ) ≈   F ( x ) + F ′ ( x ) Δ x F(x+\Delta{x})\approx\,F(x)+F'(x)\Delta{x} F(x+Δx)F(x)+F(x)Δx
用雅可比矩阵 J ( x ) J(x) J(x)来表示一阶导数
代入式(5)中,求最小化的问题需要对目标求导,求一阶导数为0时的x
1 2 ∣ ∣ F ( x + Δ x ) ∣ ∣ 2 = 1 2 [ F ( x ) T F ( x ) + 2 F ( x ) T J ( x ) Δ x + Δ x T J ( x ) T J ( x ) Δ x ] \frac{1}{2}||F(x+\Delta{x})||^2=\frac{1}{2}[{F(x)^TF(x)+2F(x)^TJ(x)\Delta{x}+\Delta{x}^TJ(x)^TJ(x)\Delta{x}}] 21F(x+Δx)2=21[F(x)TF(x)+2F(x)TJ(x)Δx+ΔxTJ(x)TJ(x)Δx]
对上式求导,并令导数为0,得到
J ( x ) T J ( x ) Δ x = − J ( x ) T F ( x ) J(x)^TJ(x)\Delta{x}=-J(x)^TF(x) J(x)TJ(x)Δx=J(x)TF(x)
那么
Δ x = ( J ( x ) T J ( x ) ) − 1 ( − J ( x ) T F ( x ) ) \Delta{x}=(J(x)^TJ(x))^{-1}(-J(x)^TF(x)) Δx=(J(x)TJ(x))1(J(x)TF(x))

这样获得了增量 Δ x \Delta{x} Δx的大小,则x的迭代为
x k + 1 = x k + Δ x = x k + ( J ( x k ) T J ( x k ) ) − 1 ( − J ( x k ) T F ( x k ) ) x_{k+1}=x_{k}+\Delta{x}=x_{k}+(J(x_{k})^TJ(x_{k}))^{-1}(-J(x_{k})^TF(x_{k})) xk+1=xk+Δx=xk+(J(xk)TJ(xk))1(J(xk)TF(xk))
对该迭代过程限制最大迭代次数或者增量小于最小改变量来求解得到最终的x。

3. 高斯牛顿去畸变求解过程

参照一维高斯牛顿法的过程,对于式(1)的畸变模型,令残差
r ( x , y ) = [ F ( x , y ) G ( x , y ) ] r(x,y)=\begin{bmatrix} F(x,y) \\ G(x,y) \end{bmatrix} r(x,y)=[F(x,y)G(x,y)]
其中,
F ( x , y ) = x + f ( x , y ) − x ′ G ( x , y ) = y + g ( x , y ) − y ′ F(x,y)=x+f(x,y)-x'\\ G(x,y)=y+g(x,y)-y' F(x,y)=x+f(x,y)xG(x,y)=y+g(x,y)y

那么去畸变的结果就是
( x , y ) = a r g m i n 1 2 ∣ ∣ r ( x , y ) ∣ ∣ 2 (x,y)=arg\rm{min}{\frac{1}{2}||\it{r}(x,y)||^2} (x,y)=argmin21r(x,y)2
根据式(10),
[ Δ x Δ y ] = ( J ( x , y ) T J ( x , y ) ) − 1 ( − J ( x , y ) T r ( x , y ) ) \begin{bmatrix} \Delta{x} \\ \Delta{y} \end{bmatrix} =(J(x,y)^TJ(x,y))^{-1}(-J(x,y)^Tr(x,y)) [ΔxΔy]=(J(x,y)TJ(x,y))1(J(x,y)Tr(x,y))
其中,
J ( x ) = [ ∂ F ( x , y ) ∂ x ∂ F ( x , y ) ∂ y ∂ G ( x , y ) ∂ x ∂ G ( x , y ) ∂ y ] J(x)=\begin{bmatrix} \frac{\partial{F(x,y)}}{\partial{x}} & \frac{\partial{F(x,y)}}{\partial{y}} \\ \frac{\partial{G(x,y)}}{\partial{x}} & \frac{\partial{G(x,y)}}{\partial{y}} \end{bmatrix} J(x)=[xF(x,y)xG(x,y)yF(x,y)yG(x,y)]
同样地,根据式(11)迭代计算最后去畸变的结果。

为了更好说明计算过程,用具体的数据来进行推导。通常畸变模型是高阶非线性的,但为了说明方便,这里假设畸变模型为双线性,如下:

f ( x , y ) = ( 1 − x − y + x y ) ∗ k 1 + ( x − x y ) ∗ k 2 + ( y − x y ) ∗ k 3 + x y ∗ k 4 g ( x , y ) = ( 1 − x − y + x y ) ∗ k 5 + ( x − x y ) ∗ k 6 + ( y − x y ) ∗ k 7 + x y ∗ k 8 f(x,y)=(1-x-y+xy)*k_1+(x-xy)*k_2+(y-xy)*k_3+xy*k_4 \\ g(x,y)=(1-x-y+xy)*k_5+(x-xy)*k_6+(y-xy)*k_7+xy*k_8 f(x,y)=1xy+xy)k1+(xxy)k2+(yxy)k3+xyk4g(x,y)=1xy+xy)k5+(xxy)k6+(yxy)k7+xyk8

求上式的一阶偏导数
∂ F ( x , y ) ∂ x = 1 + ∂ f ( x , y ) ∂ x = 1 + ( − 1 + y ) ∗ k 1 + ( 1 − y ) ∗ k 2 + ( − y ) ∗ k 3 + y ∗ k 4 ∂ F ( x , y ) ∂ y = ∂ f ( x , y ) ∂ y = ( − 1 + x ) ∗ k 1 + ( − x ) ∗ k 2 + ( 1 − x ) ∗ k 3 + x ∗ k 4 ∂ G ( x , y ) ∂ x = ∂ g ( x , y ) ∂ x = ( − 1 + y ) ∗ k 5 + ( 1 − y ) ∗ k 6 + ( − y ) ∗ k 7 + y ∗ k 8 ∂ G ( x , y ) ∂ y = 1 + ∂ g ( x , y ) ∂ y = 1 + ( − 1 + x ) ∗ k 5 + ( − x ) ∗ k 6 + ( 1 − x ) ∗ k 7 + x ∗ k 8 \begin{matrix} \frac{\partial{F(x,y)}}{\partial{x}}=1+\frac{\partial{f(x,y)}}{\partial{x}}=1+(-1+y)*k_1+(1-y)*k_2+(-y)*k_3+y*k_4 \\ \frac{\partial{F(x,y)}}{\partial{y}}=\frac{\partial{f(x,y)}}{\partial{y}}=(-1+x)*k_1+(-x)*k_2+(1-x)*k_3+x*k_4 \\ \frac{\partial{G(x,y)}}{\partial{x}}=\frac{\partial{g(x,y)}}{\partial{x}}=(-1+y)*k_5+(1-y)*k_6+(-y)*k_7+y*k_8 \\ \frac{\partial{G(x,y)}}{\partial{y}}=1+\frac{\partial{g(x,y)}}{\partial{y}}=1+(-1+x)*k_5+(-x)*k_6+(1-x)*k_7+x*k_8 \end{matrix} xF(x,y)=1+xf(x,y)=1+(1+y)k1+(1y)k2+(y)k3+yk4yF(x,y)=yf(x,y)=(1+x)k1+(x)k2+(1x)k3+xk4xG(x,y)=xg(x,y)=(1+y)k5+(1y)k6+(y)k7+yk8yG(x,y)=1+yg(x,y)=1+(1+x)k5+(x)k6+(1x)k7+xk8
带入式(16)得到雅可比矩阵,而残差为
r ( x , y ) = [ F ( x , y ) G ( x , y ) ] = [ x + ( 1 − x − y + x y ) ∗ k 1 + ( x − x y ) ∗ k 2 + ( y − x y ) ∗ k 3 + x y ∗ k 4 − x ′ y + ( 1 − x − y + x y ) ∗ k 5 + ( x − x y ) ∗ k 6 + ( y − x y ) ∗ k 7 + x y ∗ k 8 − y ′ ] r(x,y)=\begin{bmatrix} F(x,y) \\ G(x,y) \end{bmatrix} = \begin{bmatrix} x+(1-x-y+xy)*k_1+(x-xy)*k_2+(y-xy)*k_3+xy*k_4-x' \\ y+(1-x-y+xy)*k_5+(x-xy)*k_6+(y-xy)*k_7+xy*k_8-y' \end{bmatrix} r(x,y)=[F(x,y)G(x,y)]=[x+1xy+xy)k1+(xxy)k2+(yxy)k3+xyk4xy+1xy+xy)k5+(xxy)k6+(yxy)k7+xyk8y]
假设待定参数 x x x, y y y的初始值就是有畸变的值,即初始值为 x = x ′ x=x' x=x, y = y ′ y=y' y=y。将 x x x, y y y的初始值带入雅可比矩阵,以及残差矩阵中,得到式(15)的增量值,然后利用如式(11)迭代去畸变。

4. c++实现

利用Eigen矩阵库辅助完成去畸变的c++实现

完整工程:
csdn: 三维重建中高斯牛顿法求解去畸变坐标
github:DistortionRemoval

int RemoveDistortion(double &distX, double &distY, double srcX, double srcY, double *Kx, double *Ky)
{
	// 去畸变坐标的初始值设置为有畸变的坐标
	distX = srcX;
	distY = srcY;

	int iMaxItetNum = 5;
	int iIterCnt = 0;
	Eigen::MatrixXd delta;
	delta.setOnes(2, 1);

	// 迭代去畸变
	do{
		// 雅可比矩阵
		Eigen::Matrix2d Jacobi;
		Jacobi(0, 0) = 1 + (-1 + distY)*Kx[0]
			+ (1 - distY)*Kx[1]
			+ (-distY)*Kx[2]
			+ distY*Kx[3];
		Jacobi(0, 1) = (-1 + distX)*Kx[0]
			+ (-distX)*Kx[1]
			+ (1 - distX)*Kx[2]
			+ distX*Kx[3];
		Jacobi(1, 0) = (-1+distY)*Ky[0]
			+ (1 - distY)*Ky[1]
			+ (-distY)*Ky[2]
			+ distY*Ky[3];
		Jacobi(1, 1) = 1 + (-1 + distX)*Ky[0]
			+ (-distX)*Ky[1]
			+ (1 - distX)*Ky[2]
			+ distX*Ky[3];

		// 残差矩阵
		Eigen::MatrixXd R;
		R.setZero(2, 1);
		R(0, 0) = distX + (1 - distX - distY + distX*distY)*Kx[0]
			+ (distX-distX*distY)*Kx[1]
			+ (distY-distX*distY)*Kx[2]
			+ distX*distY*Kx[3]
			- srcX;
		R(1, 0) = distY + (1 - distX - distY + distX*distY)*Ky[0]
			+ (distX - distX*distY)*Ky[1]
			+ (distY - distX*distY)*Ky[2]
			+ distX*distY*Ky[3]
			- srcY;

		// 求解增量
		delta = (Jacobi.transpose() * Jacobi).inverse();
		delta *= Jacobi.transpose() * R *(-1);

		// 下一次迭代
		iIterCnt++;
		distX = distX + delta(0, 0);
		distY = distY + delta(1, 0);
	} while (iIterCnt < iMaxItetNum && (abs(delta(0, 0)) > 1e-3 || abs(delta(1, 0)) > 1e-3));

	return 0;
}

你可能感兴趣的:(结构光三维重建,计算机视觉,c++,算法)