2014-01-09 107 views
2

使用Ransac和OpenCV中的一組圖像中的一組點進行擬合一條或多條好線的最佳和最有效的方法是什麼?OpenCV - Ransac擬合線

Ransac是適合生產線的最有效方法?

回答

2

看看Least Mean Square metod。它比RANSAC更快更簡單。

也看看OpenCV的fitLine方法。

當您的數據中有很多異常值或複雜的假設時,RANSAC的表現會更好。

+1

哦,非常感謝。我認爲最小均方將不起作用,因爲我有一組點,我想要最佳線條具有一些屬性,例如距圖像中心x角和x米。 – DualSim

1

RANSAC不是最高效的,但對大量異常值更好。下面是如何使用OpenCV的做到這一點:

一個有用的結構 -

struct SLine 
{ 
    SLine(): 
     numOfValidPoints(0), 
     params(-1.f, -1.f, -1.f, -1.f) 
    {} 
    cv::Vec4f params;//(cos(t), sin(t), X0, Y0) 
    int numOfValidPoints; 
}; 

總用來製作適合成功對

cv::Vec4f TotalLeastSquares(
    std::vector<cv::Point>& nzPoints, 
    std::vector<int> ptOnLine) 
{ 
    //if there are enough inliers calculate model 
    float x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, w = 0; 
    float dx2, dy2, dxy; 
    float t; 
    for(size_t i = 0; i < nzPoints.size(); ++i) 
    { 
     x += ptOnLine[i] * nzPoints[i].x; 
     y += ptOnLine[i] * nzPoints[i].y; 
     x2 += ptOnLine[i] * nzPoints[i].x * nzPoints[i].x; 
     y2 += ptOnLine[i] * nzPoints[i].y * nzPoints[i].y; 
     xy += ptOnLine[i] * nzPoints[i].x * nzPoints[i].y; 
     w += ptOnLine[i]; 
    } 

    x /= w; 
    y /= w; 
    x2 /= w; 
    y2 /= w; 
    xy /= w; 

    //Covariance matrix 
    dx2 = x2 - x * x; 
    dy2 = y2 - y * y; 
    dxy = xy - x * y; 

    t = (float) atan2(2 * dxy, dx2 - dy2)/2; 
    cv::Vec4f line; 
    line[0] = (float) cos(t); 
    line[1] = (float) sin(t); 

    line[2] = (float) x; 
    line[3] = (float) y; 

    return line; 
} 

實際RANSAC最小二乘

SLine LineFitRANSAC(
    float t,//distance from main line 
    float p,//chance of hitting a valid pair 
    float e,//percentage of outliers 
    int T,//number of expected minimum inliers 
    std::vector<cv::Point>& nzPoints) 
{ 
    int s = 2;//number of points required by the model 
    int N = (int)ceilf(log(1-p)/log(1 - pow(1-e, s)));//number of independent trials 

    std::vector<SLine> lineCandidates; 
    std::vector<int> ptOnLine(nzPoints.size());//is inlier 
    RNG rng((uint64)-1); 
    SLine line; 
    for (int i = 0; i < N; i++) 
    { 
     //pick two points 
     int idx1 = (int)rng.uniform(0, (int)nzPoints.size()); 
     int idx2 = (int)rng.uniform(0, (int)nzPoints.size()); 
     cv::Point p1 = nzPoints[idx1]; 
     cv::Point p2 = nzPoints[idx2]; 

     //points too close - discard 
     if (cv::norm(p1- p2) < t) 
     { 
      continue; 
     } 

     //line equation -> (y1 - y2)X + (x2 - x1)Y + x1y2 - x2y1 = 0 
     float a = static_cast<float>(p1.y - p2.y); 
     float b = static_cast<float>(p2.x - p1.x); 
     float c = static_cast<float>(p1.x*p2.y - p2.x*p1.y); 
     //normalize them 
     float scale = 1.f/sqrt(a*a + b*b); 
     a *= scale; 
     b *= scale; 
     c *= scale; 

     //count inliers 
     int numOfInliers = 0; 
     for (size_t i = 0; i < nzPoints.size(); ++i) 
     { 
      cv::Point& p0 = nzPoints[i]; 
      float rho  = abs(a*p0.x + b*p0.y + c); 
      bool isInlier = rho < t; 
      if (isInlier) numOfInliers++; 
      ptOnLine[i] = isInlier; 
     } 

     if (numOfInliers < T) 
     { 
      continue; 
     } 

     line.params = TotalLeastSquares(nzPoints, ptOnLine); 
     line.numOfValidPoints = numOfInliers; 
     lineCandidates.push_back(line); 
    } 

    int bestLineIdx = 0; 
    int bestLineScore = 0; 
    for (size_t i = 0; i < lineCandidates.size(); i++) 
    { 
     if (lineCandidates[i].numOfValidPoints > bestLineScore) 
     { 
      bestLineIdx = i; 
      bestLineScore = lineCandidates[i].numOfValidPoints; 
     } 
    } 

    if (lineCandidates.empty()) 
    { 
     return SLine(); 
    } 
    else 
    { 
     return lineCandidates[bestLineIdx]; 
    } 
} 
+0

爲什麼你在試圖獲取線的參數時使用了Leastsquare? – Sara

+0

另外我還有一個問題;如果我在Hough變換之後嘗試使用RANSAC,應該向RANSAC輸入什麼內容以及Hough變換如何幫助RANSAC檢測確切的行? 我試圖遵循鱈科技車道檢測算法 – Sara

+0

@Sara我使用總最小二乘。也被稱爲SVD。簡單的最小二乘法更強大一點。 LS是獲取線路參數的最簡單方法。 –