这个问题是关于OpenCV函数的,findHomography
,getPerspectiveTransform
和getAffineTransform
-
findHomography
和getPerspectiveTransform
有什么区别?我从文档中的理解是,getPerspectiveTransform
使用 4 个对应关系(这是计算同形异义/透视变换所需的最小值)计算变换,findHomography
即使您提供超过 4 个对应关系(大概使用最小二乘法之类的东西?这是对的吗?(在这种情况下,OpenCV仍然继续支持getPerspectiveTransform的唯一原因应该是遗留的?) -
我的下一个问题是,我想知道是否有一个等效的
findHomography
来计算仿射变换?即使用最小二乘法或等效鲁棒方法来计算和仿射变换的函数。根据文档getAffineTransform
只接受 3 个对应关系(这是计算仿射变换所需的最小值)。
最好
Q #1:是的,findHomgraphy试图找到两组点之间的最佳变换。它使用比最小二乘法更聪明的东西,称为 RANSAC,它能够拒绝异常值 - 如果至少 50% + 1 个数据点正常,RANSAC 将尽最大努力找到它们,并构建可靠的转换。
getPerspectiveTransform有很多有用的理由可以留下来 - 它是findHomography的基础,并且在许多只有4个点的情况下很有用,并且您知道它们是正确的。findHomography 通常用于自动检测的点集 - 您可以找到其中的许多点,但置信度较低。getPerspectiveTransform在确定4个角时很好 - 例如手动标记或矩形的自动检测。
Q #2 仿射变换没有等效项。您可以使用 findHomography,因为仿射变换是同形异义词的子集。
我同意@vasile写的一切。我只想补充一些观察:
getPerspectiveTransform()
和getAffineTransform()
分别用于处理已知正确的对应关系的 4 或 3 个点。在用真实相机拍摄的真实图像上,你永远无法获得准确的对应关系,不能自动或手动标记相应的点。
总是有异常值。只要看看想要通过点拟合曲线的简单情况(例如,取一个带有噪声y1 = f(x) = 3.12x + gauss_noise
或y2 = g(x) = 0.1x^2 + 3.1x + gauss_noise
的生成方程):在这两种情况下找到一个好的二次函数来估计点比找到一个好的线性函数要容易得多。二次可能有点矫枉过正,但在大多数情况下不会(在删除异常值之后),如果你想在那里拟合一条直线,你最好非常确定这是正确的模型,否则你会得到无法使用的结果。
也就是说,如果您非常确定仿射变换是正确的,这里有一个建议:
- 使用
findHomography
,将 RANSAC 合并到功能中,以摆脱异常值并获得图像转换的初始估计
选择 3 个 - 正确的匹配对应项(与找到的同源性相符),或将 3 个点从第 1 个图像重新投影到第 2 个图像(使用同源异构)
- 在
getAffineTransform()
中使用这 3 个匹配项(尽可能接近正确) - 如果你愿意,可以用你自己的
findAffine()
包装所有这些 - 瞧!
Re Q#2,estimateRigidTransform是getAffineTransform的过采样等价物。我不知道首次发布时它是否在 OCV 中,但它在 2.4 中可用。
一个简单的解决方案可以找到超定方程组的仿射变换。
- 请注意,一般来说,仿射变换通过使用伪逆或类似技术找到超定线性方程组 Ax=B 的解,因此
x = (AA t )-1 At B
此外,这在核心 openCV 功能中通过简单的求解调用(A, B, X)来处理。
熟悉opencv/modules/imgproc/src/imgwarp.cpp中的仿射变换代码:它实际上只做了两件事:
a. 重新排列输入以创建系统 Ax=B;
B. 然后调用求解(A, B, X);
注意:忽略 openCV 代码中的函数注释 - 它们令人困惑,不能反映矩阵中元素的实际顺序。如果要求解 [u, v]'= 仿射 * [x, y, 1],则重排为:
x1 y1 1 0 0 1
0 0 0 x1 y1 1
x2 y2 1 0 0 1
A = 0 0 0 x2 y2 1
x3 y3 1 0 0 1
0 0 0 x3 y3 1
X = [Affine11, Affine12, Affine13, Affine21, Affine22, Affine23]’
u1 v1
B = u2 v2
u3 v3
您需要做的就是添加更多积分。要使求解(A,B,X)在超定系统上工作,请添加DECOMP_SVD参数。若要查看有关该主题的 PowerPoint 幻灯片,请使用此链接。如果你想了解更多关于计算机视觉背景下的伪逆,最好的来源是:计算机视觉,见第15章和附录C。
如果您仍然不确定如何添加更多点,请参阅下面的代码:
// extension for n points;
cv::Mat getAffineTransformOverdetermined( const Point2f src[], const Point2f dst[], int n )
{
Mat M(2, 3, CV_64F), X(6, 1, CV_64F, M.data); // output
double* a = (double*)malloc(12*n*sizeof(double));
double* b = (double*)malloc(2*n*sizeof(double));
Mat A(2*n, 6, CV_64F, a), B(2*n, 1, CV_64F, b); // input
for( int i = 0; i < n; i++ )
{
int j = i*12; // 2 equations (in x, y) with 6 members: skip 12 elements
int k = i*12+6; // second equation: skip extra 6 elements
a[j] = a[k+3] = src[i].x;
a[j+1] = a[k+4] = src[i].y;
a[j+2] = a[k+5] = 1;
a[j+3] = a[j+4] = a[j+5] = 0;
a[k] = a[k+1] = a[k+2] = 0;
b[i*2] = dst[i].x;
b[i*2+1] = dst[i].y;
}
solve( A, B, X, DECOMP_SVD );
delete a;
delete b;
return M;
}
// call original transform
vector<Point2f> src(3);
vector<Point2f> dst(3);
src[0] = Point2f(0.0, 0.0);src[1] = Point2f(1.0, 0.0);src[2] = Point2f(0.0, 1.0);
dst[0] = Point2f(0.0, 0.0);dst[1] = Point2f(1.0, 0.0);dst[2] = Point2f(0.0, 1.0);
Mat M = getAffineTransform(Mat(src), Mat(dst));
cout<<M<<endl;
// call new transform
src.resize(4); src[3] = Point2f(22, 2);
dst.resize(4); dst[3] = Point2f(22, 2);
Mat M2 = getAffineTransformOverdetermined(src.data(), dst.data(), src.size());
cout<<M2<<endl;
getAffineTransform:仿射变换是平移、缩放、剪切和旋转的组合https://www.mathworks.com/discovery/affine-transformation.htmlhttps://www.tutorialspoint.com/computer_graphics/2d_transformation.htm
getPerspectiveTransform:透视转换是项目映射在此处输入图像描述