2012-06-28 189 views
20

這個問題上OpenCV的功能findHomographygetPerspectiveTransform & getAffineTransformfindHomography,getPerspectiveTransform,與getAffineTransform

  1. 是什麼findHomographygetPerspectiveTransform之間的區別?我從文檔中得到的理解是,getPerspectiveTransform使用4個對應關係(這是計算單應性/透視變換所需的最小值)來計算變換,其中findHomography即使提供了4個以上的對應關係也可計算變換(推測可能使用像最小二乘方法?)。 這是正確的嗎? (在這種情況下,唯一的原因,OpenCV中仍然繼續支持getPerspectiveTransform應該是遺產?)

  2. 我的下一個問題是,我想知道是否有一個相當於findHomography計算的仿射變換?即使用最小二乘或等效魯棒方法來計算和仿射變換的函數。 根據文檔getAffineTransform僅需要3個對應關係(這是計算仿射變換所需的最小值)。

最佳,

+1

也許[estimatedRigidTransform](http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking。html#estimaterigidtransform)將符合您的需求。 – cgat

回答

24

,Q#1:對,findHomography試圖找到最好的兩套點之間變換。它使用比稱爲RANSAC的最小二乘法更聰明的方法,它能夠拒絕異常值 - 如果您的數據點中至少有50%+ 1是正常的,RANSAC將盡其所能找到它們並構建可靠的變換。

getPerspectiveTransform有很多有用的理由可以保留 - 它是findHomography的基礎,它在很多情況下很有用,你只有4分,而且你知道它們是正確的。 findHomography通常與自動檢測到的一組點一起使用 - 您可以找到它們中的許多點,但信心不足。當您確定四個角落時,getPerspectiveTransform是很好的 - 例如手動標記或自動檢測矩形。

Q#2仿射變換沒有等價物。你可以使用findHomography,因爲仿射變換是單應性的一個子集。

8

我同意@vasile寫的所有內容。我只想補充一些意見:

getPerspectiveTransform()getAffineTransform()是爲了在或點(分別),所已知正確的對應工作。在用真實相機拍攝的真實圖像上,您可以從永不獲得準確的對應關係,而不是自動或手動標記相應的點。

總是有異常。 看一下簡單的例子想要擬合通過點的曲線(例如採取生成方程與噪音y1 = f(x) = 3.12x + gauss_noisey2 = g(x) = 0.1x^2 + 3.1x + gauss_noise):在兩種情況下找到一個好的二次函數來估計點要容易得多線性好。二次方可能是一種矯枉過正,但在大多數情況下不會(在去除異常值後),並且如果您想要適合一條直線,那麼您最好大膽確定這是正確的模型,否則您將得到不可用的結果。

這就是說,如果你是強烈地確信仿射變換是正確的,這裏有一個建議:

  • 使用findHomography,有RANSAC到功能結合,擺脫異常值並獲得圖像變換的初始估計值
  • 選擇3個正確的匹配 - 對應值(與所發現的單應性符合),或者將第一個圖像中的3個點重新投影到第二個(使用單應性)
  • getAffineTransform()
  • 中使用那3個匹配項(這些匹配項儘可能接近地更正)如果你想 - 並且瞧!
+0

有沒有辦法找到最好的「仿射」矩陣?我想強制單應性的最後一行是[0,0,1]。 – Royi

+1

@Drazick我寫過的「算法」幾乎可以做到這一點 - 它使用findHomography去除異常值,因此您不必編寫自己的RANSAC,並且可以在任意3點上使用getAffineTransform()獲得接近最佳的親密關係。 Alternatevley,你可以使用getAffineTransform()而不是getPerspectiveTransform()作爲核心函數來編寫你自己的RANSAC算法。 – penelope

+0

@penleope,我發現了一個解決方案,如何使用SVD以類似於估計最佳單應性的方式找到最佳(12明智)仿射變換。 – Royi

3

Re Q#2,estimatedRigidTransform是getAffineTransform的過採樣等價物。我不知道它是在OCV上發佈的時候,但它在2.4版本中可用。

1

找到超定方程系統的仿射變換有一個簡單的解決方案。

  1. 注意,在一般的仿射變換通過使用僞逆或類似技術找到一個解決方案,線性方程組Ax = B的超定系統,所以

X =( AA-1

此外,這是在芯的OpenCV功能由一個簡單的呼叫處理,以解決(A,B,X)。

  • 熟悉仿射的代碼中的OpenCV /模塊/ imgproc/SRC/imgwarp.cpp變換:它確實只是兩件事情:

    一個。重新排列輸入以創建一個系統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參數。要查看該主題的幻燈片,請使用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; 
    
    相關問題