Homography求解与基于eigen库的c++实现
前言
在使用标志物识别与跟踪技术中,利用homography来描述两个视图中标志物平面之间的映射关系。
一、理论推导
当两个视图中拥有相同的一个平面时,可以根据平面来估计视图之间的映射关系,并称之为单应性矩阵。
在世界中一个点
P
[
X
,
Y
,
Z
]
P[X,Y,Z]
P[X,Y,Z],在两张图像中的位置分别为
p
1
,
p
2
p_1,p_2
p1,p2,则有下列关系:
n
T
P
+
d
=
0
\boldsymbol n^T\boldsymbol P+d=0
nTP+d=0
− n T P d = 1 -\frac{\boldsymbol n^T\boldsymbol P}{d}=1 −dnTP=1
同一点在两张视图中的映射关系:
p
1
=
K
(
P
)
\boldsymbol p1 = K(\boldsymbol P)
p1=K(P)
p 2 = K ( R P + t ) \boldsymbol p_2=K(\boldsymbol R\boldsymbol P+\boldsymbol t) p2=K(RP+t)
p 2 = K ( R P − t n T P d ) \boldsymbol p_2 = K(\boldsymbol R \boldsymbol P - \boldsymbol t\frac{\boldsymbol n^T\boldsymbol P}{d}) p2=K(RP−tdnTP)
p 2 = K ( R − t n T d ) P \boldsymbol p_2 = \boldsymbol K(\boldsymbol R-\boldsymbol t\frac{\boldsymbol n^T}{d})\boldsymbol P p2=K(R−tdnT)P
p 2 ∼ K ( R − t n T d ) K − 1 p 1 \boldsymbol p_2 \sim \boldsymbol K(\boldsymbol R-\boldsymbol t\frac{\boldsymbol n^T}{d})\boldsymbol K^{-1}\boldsymbol p_1 p2∼K(R−tdnT)K−1p1
所以,化简得到点的映射关系:
p
2
∼
H
p
1
\boldsymbol p_2 \sim \boldsymbol H \boldsymbol p_1
p2∼Hp1
( u 2 v 2 1 ) ∼ ( h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 h 9 ) ( u 1 v 1 1 ) \begin{pmatrix} u_2 \\ v2 \\ 1\end{pmatrix} \sim \begin{pmatrix} h_1& h_2& h_3 \\h_4&h_5&h_6\\h_7&h_8&h_9 \end{pmatrix} \begin{pmatrix} u_1 \\ v_1 \\ 1 \end{pmatrix} ⎝⎛u2v21⎠⎞∼⎝⎛h1h4h7h2h5h8h3h6h9⎠⎞⎝⎛u1v11⎠⎞
u 2 ∼ h 1 u 1 + h 2 v 1 + h 3 u_2 \sim h_1u_1+h_2v_1+h_3 u2∼h1u1+h2v1+h3
v 2 ∼ h 4 u 1 + h 5 v 1 + h 6 v_2\sim h_4u_1+h_5v_1+h_6 v2∼h4u1+h5v1+h6
1 ∼ h 7 u 1 + h 8 v 1 + h 9 1\sim h_7u_1+h_8v_1+h_9 1∼h7u1+h8v1+h9
由于缩放因子的存在,添加一个约束,令
h
9
=
1
h_9=1
h9=1,将矩阵展开得:
h
1
u
1
+
h
2
v
1
+
h
3
−
h
7
u
1
u
2
−
h
8
v
1
u
2
=
u
2
h_1u_1+h_2v_1+h_3 - h_7u_1u_2 - h_8v_1u_2 = u_2
h1u1+h2v1+h3−h7u1u2−h8v1u2=u2
h 4 u 1 + h 5 v 1 + h 6 − h 7 u 1 v 2 − h 8 v 1 v 2 = v 2 h_4u_1+h_5v_1+h_6-h_7u_1v_2-h_8v_1v_2=v_2 h4u1+h5v1+h6−h7u1v2−h8v1v2=v2
( u 1 v 1 1 0 0 0 − u 1 u 2 − v 1 u 2 0 0 0 u 1 v 1 1 − u 1 v 2 − v 1 v 2 ) ( h 1 h 2 h 3 h 4 h 5 h 6 h 7 h 8 ) = ( u 2 v 2 ) \begin{pmatrix} u_1 & v_1&1&0&0&0&-u_1u_2&-v_1u_2\\0&0&0&u_1&v_1&1&-u_1v_2&-v_1v_2 \end{pmatrix} \begin{pmatrix} h_1\\h_2\\h_3\\h_4\\h_5\\h_6\\h_7\\h_8\end{pmatrix}=\begin{pmatrix} u_2\\v_2\\ \end{pmatrix} (u10v10100u10v101−u1u2−u1v2−v1u2−v1v2)⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛h1h2h3h4h5h6h7h8⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞=(u2v2)
所以,一对点可以引入两个约束,一共8个未知参数,故需要4对点即可求得映射关系:Ax=B
二、c++实现
1.CMakeLists.txt
cmake_minimum_required(VERSION 3.22.0)
project(homography)
# Eigen
include_directories("/usr/include/eigen3")
add_executable(homography homography.cpp)
2.homography.cpp
void calc_homography(int pts1[4][2], int pts2[4][2], int pts_in[][2], int pts_out[][2])
{
Eigen::Matrix<float,8,8> A;
Eigen::Matrix<float,8,1> B;
for(int i = 0; i<4;++i) {
A(i*2, 0) = pts1[i][0];
A(i*2, 1) = pts1[i][1];
A(i*2, 2) = 1.0;
A(i*2, 3) = 0.0;
A(i*2, 4) = 0.0;
A(i*2, 5) = 0.0;
A(i*2, 6) = -pts1[i][0] * pts2[i][0];
A(i*2, 7) = -pts1[i][1] * pts2[i][0];
A(i*2 + 1, 0) = 0.0;
A(i*2 + 1, 1) = 0.0;
A(i*2 + 1, 2) = 0.0;
A(i*2 + 1, 3) = pts1[i][0];
A(i*2 + 1, 4) = pts1[i][1];
A(i*2 + 1, 5) = 1.0;
A(i*2 + 1, 6) = -pts1[i][0] * pts2[i][1];
A(i*2 + 1, 7) = -pts1[i][1] * pts2[i][1];
B(i*2, 0) = pts2[i][0];
B(i*2 + 1, 0) = pts2[i][1];
}
std::cout << "求解 Ax=B " << std::endl;
std::cout << A << "\n" << std::endl;
std::cout << B.transpose() << "\n" << std::endl;
Eigen::MatrixXf H = A.colPivHouseholderQr().solve(B);
Eigen::Matrix<float, 3, 3> X;
X(0,0) = H(0,0);
X(0,1) = H(1,0);
X(0,2) = H(2,0);
X(1,0) = H(3,0);
X(1,1) = H(4,0);
X(1,2) = H(5,0);
X(2,0) = H(6,0);
X(2,1) = H(7,0);
X(2,2) = 1.0;
std::cout <<"单应性矩阵为" <<std::endl;
std::cout << X << std::endl;
Eigen::MatrixXf X_ = X.inverse();
for(int i=0; i<4; i++)
{
Eigen::Matrix<float,3,1> in;
in << static_cast<float>(pts_in[i][0]), static_cast<float>(pts_in[i][1]), 1;
Eigen::Matrix<float,3,1> out;
out = X_ * in;
pts_out[i][0] = static_cast<int>(out[0]/out[2]);
pts_out[i][1] = static_cast<int>(out[1]/out[2]);
}
std::cout <<"\n" << "N个相机1图像中坐标的点: " << std::endl;
std::cout << pts_out[0][0] << " "<< pts_out[0][1] << std::endl;
std::cout << pts_out[1][0] << " "<< pts_out[1][1] << std::endl;
std::cout << pts_out[2][0] << " "<< pts_out[2][1] << std::endl;
std::cout << pts_out[3][0] << " "<< pts_out[3][1] << std::endl;
}
总结
注意由于缩放因子引入的约束。