OpenCV相机标定与3D重建(37)计算两幅图像之间单应性矩阵(Homography Matrix)的函数findHomography()的使用
- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
找到两个平面之间的透视变换。
cv::findHomography 是 OpenCV 库中用于计算两幅图像之间单应性矩阵(Homography Matrix)的函数。单应性矩阵描述了两个平面之间的投影变换关系,它在计算机视觉中用于图像校正、拼接和增强现实等任务。
函数原型
Mat cv::findHomography
(
InputArray srcPoints,
InputArray dstPoints,
int method = 0,
double ransacReprojThreshold = 3,
OutputArray mask = noArray(),
const int maxIters = 2000,
const double confidence = 0.995
)
参数
- 参数srcPoints:原平面中点的坐标,可以是类型为 CV_32FC2 的矩阵或 vector。
- 参数dstPoints:目标平面中点的坐标,可以是类型为 CV_32FC2 的矩阵或 vector。
- 参数method:用于计算单应性矩阵的方法。可能的方法包括:
- 0:常规方法,使用所有点,即最小二乘法。
- RANSAC:基于RANSAC的稳健方法。
- LMEDS:最小中值(Least-Median)稳健方法。
- RHO:基于PROSAC的稳健方法。
- ransacReprojThreshold:仅用于 RANSAC 和 RHO 方法。这是允许的最大重投影误差,用于将一对点视为内点。也就是说,如果
∥ dstPoints i − convertPointsHomogeneous ( H ⋅ srcPoints i ) ∥ 2 > ransacReprojThreshold \| \texttt{dstPoints} _i - \texttt{convertPointsHomogeneous} ( \texttt{H} \cdot \texttt{srcPoints} _i) \|_2 > \texttt{ransacReprojThreshold} ∥dstPointsi−convertPointsHomogeneous(H⋅srcPointsi)∥2>ransacReprojThreshold
则认为点 i 是离群点。如果 srcPoints 和 dstPoints 以像素为单位测量,则通常将此参数设置在1到10之间是有意义的。 - 参数mask:由稳健方法(如 RANSAC 或 LMEDS)设置的可选输出掩码。注意输入掩码值被忽略。
- 参数maxIters:RANSAC的最大迭代次数。
- 参数confidence:置信水平,介于0和1之间。
该函数找到并返回源平面和目标平面之间的透视变换矩阵 H H H
s
i
[
x
i
′
y
i
′
1
]
∼
H
[
x
i
y
i
1
]
s_i \begin{bmatrix} x'_i \\ y'_i \\ 1 \end{bmatrix} \sim H \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}
si
xi′yi′1
∼H
xiyi1
从而最小化反投影误差:
∑
i
(
x
i
′
−
(
h
11
x
i
+
h
12
y
i
+
h
13
)
h
31
x
i
+
h
32
y
i
+
h
33
)
2
+
(
y
i
′
−
(
h
21
x
i
+
h
22
y
i
+
h
23
)
h
31
x
i
+
h
32
y
i
+
h
33
)
2
\sum_i \left( \frac{x'_i - (h_{11}x_i + h_{12}y_i + h_{13})}{h_{31}x_i + h_{32}y_i + h_{33}} \right)^2 + \left( \frac{y'_i - (h_{21}x_i + h_{22}y_i + h_{23})}{h_{31}x_i + h_{32}y_i + h_{33}} \right)^2
i∑(h31xi+h32yi+h33xi′−(h11xi+h12yi+h13))2+(h31xi+h32yi+h33yi′−(h21xi+h22yi+h23))2
如果 method 参数设置为默认值 0,则函数使用所有点对通过简单的最小二乘方案计算初始单应性估计。
然而,如果并非所有的点对(srcPoints_i, dstPoints_i)都符合刚性的透视变换(即存在一些离群点),这个初始估计将会较差。在这种情况下,你可以使用三种稳健方法之一。RANSAC、LMEDS 和 RHO 方法尝试许多不同的随机子集(每次四个点对,共线点对被丢弃),使用这个子集和简单的最小二乘算法估计单应性矩阵,然后计算所估计单应性的质量/优度(对于RANSAC来说是内点的数量,对于LMEDS来说是最小中值重投影误差)。最佳子集随后用于生成单应性矩阵的初始估计和内点/离群点的掩码。
无论是否使用稳健方法,计算出的单应性矩阵都会进一步优化(在稳健方法的情况下仅使用内点),以Levenberg-Marquardt方法减少重投影误差。
RANSAC 和 RHO 方法可以处理几乎任何比例的离群点,但需要一个阈值来区分内点和离群点。LMEDS 方法不需要任何阈值,但只有当内点超过50%时才能正确工作。最后,如果没有离群点且噪声较小,使用默认方法(method=0)。
该函数用于找到初始的内部和外部矩阵。单应性矩阵确定至一个尺度。因此,它被标准化以使 h 33 = 1 h_{33}=1 h33=1。需要注意的是,每当无法估计 H 矩阵时,将返回一个空矩阵。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
// 创建虚拟的匹配点数据(假设我们有4对匹配点)
vector< Point2f > srcPoints = { Point2f( 56.0f, 65.0f ), Point2f( 368.0f, 52.0f ), Point2f( 28.0f, 387.0f ), Point2f( 389.0f, 390.0f ) };
vector< Point2f > dstPoints = { Point2f( 0.0f, 0.0f ), Point2f( 300.0f, 0.0f ), Point2f( 0.0f, 300.0f ), Point2f( 300.0f, 300.0f ) };
// 定义输出的单应性矩阵和掩码
Mat homographyMatrix, mask;
// 使用 RANSAC 方法计算单应性矩阵
homographyMatrix = findHomography( srcPoints, dstPoints,
RANSAC, // 使用RANSAC方法
3.0, // 点到投影模型的最大重投影误差
mask, // 输出掩码
2000, // 最大迭代次数
0.995 ); // 置信水平
// 打印结果
cout << "Homography Matrix:\n" << homographyMatrix << endl;
// 打印哪些点被认为是内点
cout << "Inliers mask:\n";
for ( size_t i = 0; i < mask.total(); ++i )
{
if ( mask.at< uchar >( i ) )
{
cout << "Point " << i + 1 << " is an inlier." << endl;
}
else
{
cout << "Point " << i + 1 << " is an outlier." << endl;
}
}
return 0;
}
运行结果
Homography Matrix:
[1.055873761296419, 0.09181510967794945, -65.09691276166618;
0.04690100493754324, 1.125624118501043, -75.79202397907012;
0.0001832514481695185, 0.0005133370013304123, 0.9999999999999999]
Inliers mask:
Point 1 is an inlier.
Point 2 is an inlier.
Point 3 is an inlier.
Point 4 is an inlier.