35

这个问题是关于 OpenCV 函数的findHomographygetPerspectiveTransform&getAffineTransform

  1. findHomography和有什么区别getPerspectiveTransform?我从文档中的理解是,getPerspectiveTransform使用 4 个对应关系(这是计算单应性/透视变换所需的最小值)findHomography计算变换,即使您提供超过 4 个对应关系(大概使用最小二乘法之类的方法? )。这个对吗?(在这种情况下,OpenCV 仍然继续支持 getPerspectiveTransform 的唯一原因应该是遗留的?)

  2. 我的下一个问题是我想知道是否有findHomography计算仿射变换的等价物?即使用最小二乘法或等效稳健方法来计算和仿射变换的函数。根据文档getAffineTransform只接受 3 个对应关系(这是计算仿射变换所需的最小值)。

最好的,

4

5 回答 5

43

Q #1:是的,findHomography 试图找到两组点之间的最佳变换。它使用比最小二乘法更智能的东西,称为 RANSAC,它能够拒绝异常值 - 如果至少 50% + 1 的数据点正常,RANSAC 将尽最大努力找到它们,并构建可靠的转换。

getPerspectiveTransform 有很多有用的理由留下来——它是 findHomography 的基础,它在许多只有 4 个点并且你知道它们是正确的点的情况下很有用。findHomography 通常与自动检测到的点集一起使用 - 您可以找到其中的许多点,但置信度较低。当您确定 4 个角时,getPerspectiveTransform 非常好——例如手动标记或自动检测矩形。

Q #2仿射变换没有等价物。您可以使用 findHomography,因为仿射变换是单应性的子集。

于 2012-06-28T05:58:17.413 回答
13

我同意@vasile 所写的一切。我只想补充一些意见:

getPerspectiveTransform()并且getAffineTransform()旨在分别处理43个点,这些点已知是正确的对应关系。在使用真实相机拍摄的真实图像上,您永远无法获得如此准确的对应关系,而不是自动或手动标记对应点。

总是有异常值。只需看一下想要通过点拟合曲线的简单情况(例如,采用带有噪声的生成方程y1 = f(x) = 3.12x + gauss_noisey2 = g(x) = 0.1x^2 + 3.1x + gauss_noise):在这两种情况下,找到一个好的二次函数来估计点要比一个好的线性函数容易得多. 二次方可能是一种矫枉过正,但在大多数情况下不会(在去除异常值之后),如果你想在那里拟合一条直线,你最好非常确定这是正确的模型,否则你会得到不可用的结果。

也就是说,如果您非常确定仿射变换是正确的,这里有一个建议:

  • 使用findHomography,将RANSAC合并到功能中,以消除异常值并获得图像转换的初始估计
  • 选择 3 个正确的匹配对应关系(与找到的单应性匹配),或将 3 个点从第一个图像重新投影到第二个(使用单应性)
  • 使用这 3 个匹配项(尽可能接近正确)getAffineTransform()
  • 如果您愿意,可以将所有这些都用您自己的方式包装起来findAffine()——瞧!
于 2012-06-28T09:08:03.317 回答
4

关于 Q#2,estimateRigidTransform 是 getAffineTransform 的过采样等价物。我不知道它第一次发布时是否在 OCV 中,但它在 2.4 中可用。

于 2014-08-17T23:44:08.087 回答
3

找到超定方程组的仿射变换有一个简单的解决方案。

  1. 请注意,一般来说,仿射变换通过使用伪逆或类似技术来找到线性方程组 Ax=B 的超定系统的解,因此

x = (AA t ) -1 A t B

此外,这是通过简单调用solve(A, B, X) 在核心openCV 功能中处理的。

  1. 熟悉一下 opencv/modules/imgproc/src/imgwarp.cpp 中的仿射变换代码:它实际上只做了两件事:

    一个。重新排列输入以创建系统 Ax=B;

    湾。然后调用solve(A, B, X);

注意:忽略 openCV 代码中的函数注释——它们令人困惑,并且不反映矩阵中元素的实际顺序。如果您正在求解 [u, v]'= Affine * [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 

您需要做的就是添加更多积分。要使 Solve(A, B, X) 在超定系统上工作,请添加 DECOMP_SVD 参数。要查看有关该主题的 powerpoint 幻灯片,请使用此链接。如果您想了解更多关于计算机视觉背景下的伪逆,最好的来源是:ComputerVision,参见第 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;
于 2015-01-16T22:55:11.010 回答
0

getAffineTransform:仿射变换是平移、缩放、剪切和旋转的组合 https://www.mathworks.com/discovery/affine-transformation.html https://www.tutorialspoint.com/computer_graphics/2d_transformation.htm

getPerspectiveTransform:透视变换是项目映射 在此处输入图像描述

于 2019-05-15T09:24:37.350 回答