findHomography,getPerspectiveTransform,& getAffineTransform

时间:2012-06-28 04:08:44

标签: c++ opencv affinetransform homography

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

  1. findHomographygetPerspectiveTransform之间的区别是什么?我从文档中的理解是getPerspectiveTransform使用4个对应关系(这是计算单应性/透视变换所需的最小值)来计算变换,其中findHomography计算变换,即使你提供了4个以上的对应关系(大概使用像最小二乘法的东西?)。 它是否正确? (在这种情况下,OpenCV仍然继续支持getPerspectiveTransform的唯一原因应该是遗产?)

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

  3. 最佳,

5 个答案:

答案 0 :(得分:31)

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

getPerspectiveTransform有很多有用的理由 - 它是findHomography的基础,它在很多只有4分的情况下很有用,而且你知道它们是正确的。 findHomography通常用于自动检测到的一组点 - 你可以找到很多点,但信心很低。当你确定4个角落时,getPerspectiveTransform是好的 - 比如手动标记或自动检测矩形。

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

答案 1 :(得分:10)

我同意@vasile撰写的所有内容。我只想补充一些意见:

getPerspectiveTransform()getAffineTransform()适用于 4 3 点(分别)已知正确对应。对于使用真实相机拍摄的真实图像,您可以从不获得准确的对应关系,而不是自动或手动标记相应的点。

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

那就是说,如果你非常确定仿射变换是正确的,那么这是一个建议:

  • 使用已将 RANSAC 合并到功能中的findHomography来摆脱异常值并获得图像转换的初步估算
  • 选择3个正确的匹配 - 对应(符合发现的单应性),或者从第1个图像到第2个重新投影3个点(使用单应性)
  • getAffineTransform()
  • 中使用这3场比赛(尽可能接近正确) 如果你愿意的话,
  • 将所有内容包装在你自己的findAffine()中 - 瞧!

答案 2 :(得分:4)

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

答案 3 :(得分:1)

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

  1. 请注意,一般情况下,仿射变换通过使用伪逆或类似技术找到过度确定的线性方程组Ax = B的解,所以
  2. x =(A A t -1 A t B

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

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

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

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

    2. 注意:忽略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幻灯片,请使用此link。如果您想在计算机视觉的背景下更多地了解伪逆,最好的来源是: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;
      

答案 4 :(得分:0)

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

getPerspectiveTransform:透视变换是项目映射 enter image description here