OpenCV相机标定与3D重建(26)计算两个二维点集之间的部分仿射变换矩阵(2x3)函数 estimateAffinePartial2D()的使用
- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
计算两个2D点集之间的具有4个自由度的最优有限仿射变换。
cv::estimateAffinePartial2D 是 OpenCV 库中的一个函数,用于计算两个二维点集之间的部分仿射变换矩阵(2x3)。与完整的仿射变换不同,部分仿射变换仅估计旋转、平移和均匀缩放,而不包括剪切变形。这使得它适用于保持平行线和平行性的情况,例如处理相机的平移和旋转运动。
函数原型
cv::Mat cv::estimateAffinePartial2D
(
InputArray from,
InputArray to,
OutputArray inliers = noArray(),
int method = RANSAC,
double ransacReprojThreshold = 3,
size_t maxIters = 2000,
double confidence = 0.99,
size_t refineIters = 10
)
参数
- 参数from 第一个输入的2D点集。
- 参数to 第二个输入的2D点集。
- 参数inliers 输出向量,指示哪些点是内点(1-内点,0-外点)。
- 参数method 用于计算变换的鲁棒方法。可能的方法包括:
- RANSAC - 基于RANSAC的鲁棒方法
- LMEDS - 最小中位数鲁棒方法
- 默认方法为 RANSAC。
- 参数ransacReprojThreshold 在RANSAC算法中,考虑一个点为内点的最大重投影误差。仅适用于RANSAC。
- 参数maxIters 鲁棒方法的最大迭代次数。
- 参数confidence 对估计变换的置信水平,在0和1之间。通常0.95到0.99之间的值就足够了。过于接近1的值可能会显著减慢估计过程。低于0.8-0.9的值可能导致变换估计不准确。
- 参数refineIters 精化算法(Levenberg-Marquardt)的最大迭代次数。传递0将禁用精化,因此输出矩阵将是鲁棒方法的输出。
返回值
输出 2D 仿射变换(4个自由度)矩阵 2×3,如果无法估计变换则返回空矩阵。
该函数估计一个具有4个自由度的最优2D仿射变换,限于平移、旋转和均匀缩放的组合。使用选定的鲁棒算法进行估计。
计算出的变换随后会进一步通过Levenberg-Marquardt方法进行精化(仅使用内点),以进一步减少重投影误差。
估计的变换矩阵为:
[ cos ( θ ) ⋅ s − sin ( θ ) ⋅ s t x sin ( θ ) ⋅ s cos ( θ ) ⋅ s t y ] \begin{bmatrix} \cos(\theta) \cdot s & -\sin(\theta) \cdot s & t_x \\ \sin(\theta) \cdot s & \cos(\theta) \cdot s & t_y \end{bmatrix} [cos(θ)⋅ssin(θ)⋅s−sin(θ)⋅scos(θ)⋅stxty]
其中 θ 是旋转角度,s 是缩放因子,tx 和 ty 分别是 x 轴和 y 轴上的平移量。
注释
RANSAC 方法实际上可以处理任意比例的外点,但需要一个阈值来区分内点和外点。LMeDS 方法不需要任何阈值,但它只有在内点超过50%的情况下才能正确工作。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
// 定义两组对应的2D点 (x, y) - 源点集和目标点集
vector< Point2f > from = { Point2f( 0, 0 ), Point2f( 1, 0 ), Point2f( 0, 1 ), Point2f( 1, 1 ) };
vector< Point2f > to = { Point2f( 2, 2 ), Point2f( 3, 2 ), Point2f( 2, 3 ), Point2f( 3, 3 ) };
// 定义一个 Mat 来接收输出的部分仿射变换矩阵
Mat affinePartialMatrix;
// 定义一个 Mat 来接收内点信息
vector< uchar > inliers;
// 调用 estimateAffinePartial2D 函数
affinePartialMatrix = estimateAffinePartial2D( from, to, inliers );
if ( !affinePartialMatrix.empty() )
{
cout << "Estimated Partial Affine Matrix:\n" << affinePartialMatrix << endl;
// 打印哪些点被认为是内点
for ( size_t i = 0; i < inliers.size(); ++i )
{
if ( inliers[ i ] )
{
cout << "Point pair (" << from[ i ] << ", " << to[ i ] << ") is an inlier.\n";
}
else
{
cout << "Point pair (" << from[ i ] << ", " << to[ i ] << ") is an outlier.\n";
}
}
}
else
{
cout << "Failed to estimate partial affine transformation." << endl;
}
return 0;
}
运行结果
Estimated Partial Affine Matrix:
[1, -0, 2;
0, 1, 2]
Point pair ([0, 0], [2, 2]) is an inlier.
Point pair ([1, 0], [3, 2]) is an inlier.
Point pair ([0, 1], [2, 3]) is an inlier.
Point pair ([1, 1], [3, 3]) is an inlier.