RANSAC擬合算法


 

最小二乘法只適合與誤差較小的情況。試想一下這種情況,假使需要從一個噪音較大的數據集中提取模型(比方說只有20%的數據時符合模型的)時,最小二乘法就顯得力不從心了。

算法簡介

隨機抽樣一致算法(RANdom SAmple Consensus,RANSAC)。它是一種迭代的方法,用來在一組包含離群的被觀測數據中估算出數學模型的參數。
RANSAC是一個非確定性算法,在某種意義上說,它會產生一個在一定概率下合理的結果,其允許使用更多次的迭代來使其概率增加。此RANSAC算法在1981年由Fischler和Bolles首次提出。
RANSAC的基本假設是 “內群”數據可以通過幾組模型參數來敘述其數據分布,而“離群”數據則是不適合模型化的數據。 數據會受噪聲影響,噪聲指的是離群,例如從極端的噪聲或錯誤解釋有關數據的測量或不正確的假設。
RANSAC假定,給定一組(通常很小)的內群,存在一個程序,這個程序可以估算最佳解釋或最適用於這一數據模型的參數。

一、范例

這里用一個簡單的例子來說明,在一組數據點中找到一條最適合的線。
假設,此有一組集合包含了內群以及離群,其中內群為可以被擬合到線段上的點,而離群則是無法被擬合的點。
如果我們用簡單的最小平方法來找此線,我們將無法得到一條適合於內群的線,因為最小平方法會受離群影響而影響其結果。
而RANSAC,可以只由內群來計算出模型,而且概率還夠高。
然而,RANSAC無法保證結果一定最好,所以必須小心選擇參數,使其能有足夠的概率。

二、算法實現過程

1.在數據中隨機選擇幾個點設定為內群
2.計算適合步驟一中內群的模型
3.把其它剛才沒選到的點帶入剛才建立的模型中,計算是否為內群
4.記下內群數量,如果有足夠多的點被歸類為假設的局內點,那么估計的模型就足夠合理。
5.重復以上步驟多做幾次
6.比較哪次計算中內群數量最多,內群最多的那次所建的模型就是我們所要求的解

這里有幾個問題
1.一開始的時候我們要隨機選擇多少點(n)
2.以及要重復做多少次(k)

三、算法

偽碼形式的算法如下所示:
輸入:
data —— 一組觀測數據
model —— 適應於數據的模型
n —— 適用於模型的最少數據個數
k —— 算法的迭代次數
t —— 用於決定數據是否適應於模型的閥值
d —— 判定模型是否適用於數據集的數據數目
輸出:
best_model —— 跟數據最匹配的模型參數(如果沒有找到好的模型,返回null)
best_consensus_set —— 估計出模型的數據點
best_error —— 跟數據相關的估計出的模型錯誤

iterations = 0
best_model = null
best_consensus_set = null
best_error = 無窮大
while ( iterations < k )
maybe_inliers = 從數據集中隨機選擇n個點
maybe_model = 適合於maybe_inliers的模型參數
consensus_set = maybe_inliers

for ( 每個數據集中不屬於maybe_inliers的點 )
if ( 如果點適合於maybe_model,且錯誤小於t )
將點添加到consensus_set
if ( consensus_set中的元素數目大於d )
已經找到了好的模型,現在測試該模型到底有多好
better_model = 適合於consensus_set中所有點的模型參數
this_error = better_model究竟如何適合這些點的度量
if ( this_error < best_error )
我們發現了比以前好的模型,保存該模型直到更好的模型出現
best_model = better_model
best_consensus_set = consensus_set
best_error = this_error
增加迭代次數
返回 best_model, best_consensus_set, best_error

RANSAC算法的可能變化包括以下幾種:
(1)如果發現了一種足夠好的模型(該模型有足夠小的錯誤率),則跳出主循環。這樣可能會節約計算額外參數的時間。
(2)直接從maybe_model計算this_error,而不從consensus_set重新估計模型。這樣可能會節約比較兩種模型錯誤的時間,但可能會對噪聲更敏感。

 

四、參數確定

假設每個點時真正內群的概率為 w

w = 內群的數目/(內群數目+外群數目)
通常我們不知道 w 是多少, w^n是所選擇的n個點都是內群的機率, 1-w^n 是所選擇的n個點至少有一個不是內群的機率, (1 ? w^n)^k 是表示重復 k 次都沒有全部的n個點都是內群的機率, 這邊定算法跑 k 次以后成功的機率是p,那么,

1 - p = (1 - w^n)^k
p = 1 - (1 - w^n)^k
所以如果希望成功機率高,p = 0.99, 當n不變時,k越大,p越大, 當w不變時,n越大,所需的k就越大, 通常w未知,所以n 選小一點比較好。

五、優點與缺點

RANSAC的優點是它能魯棒的估計模型參數。例如,它能從包含大量局外點的數據集中估計出高精度的參數。RANSAC的缺點是它計算參數的迭代次數沒有上限;如果設置迭代次數的上限,得到的結果可能不是最優的結果,甚至可能得到錯誤的結果。RANSAC只有一定的概率得到可信的模型,概率與迭代次數成正比。RANSAC的另一個缺點是它要求設置跟問題相關的閥值。
RANSAC只能從特定的數據集中估計出一個模型,如果存在兩個(或多個)模型,RANSAC不能找到別的模型。

六、應用

RANSAC算法經常用於計算機視覺,例如同時求解相關問題與估計立體攝像機的基礎矩陣。

C語言實現

float Ransac
(
  Point2D32f* points, 
  size_t Cnt, 
  float *line,
  int numForEstimate,
  float successProbability,
  float maxOutliersPercentage
){
 
  //float outlierPercentage = maxOutliersPercentage;//估計值
  float numerator = log(1.0f-successProbability);
  float denominator = log(1.0f- pow(1.0-maxOutliersPercentage, numForEstimate));
  //隨機抽取一定比例的點
  int ransac_times = (int)(numerator/denominator + 0.5);
  
  printf("ransac_times: %d\n", ransac_times);
  int numDataObjects = Cnt;
  //int numForEstimate = Cnt*0.1;
  int maxVoteCnt = 0;
  float tempLine[4];
  float inliersPercentage = 0.0;
  
  
  int *Chosen = new int[numDataObjects];
 
  Point2D32f *subPoints = new Point2D32f[numForEstimate];
  int pointCnt = 0;
  int voteCnt = 0;
  for(int i = 0; i < ransac_times; i++)
  {
    //隨機抽取 
    //randomly select data for exact model fit ('numForEstimate' objects).
        memset(Chosen,0,numDataObjects*sizeof(int));
        int maxIndex = numDataObjects-1;
    for(int j = 0; j < numForEstimate; j++)
    {
      int selectedIndex = rand() % numDataObjects;
      Chosen[selectedIndex] = 1;
    }
    //擬合
    pointCnt = 0;
    for(int k = 0; k < numDataObjects; k++)
    {
      if(Chosen[k])
      {
        subPoints[pointCnt].x = points[k].x;
        subPoints[pointCnt].y = points[k].y;
        pointCnt++;
      }
    }
    FitLine2D(subPoints, pointCnt, tempLine);
    float a = tempLine[1]/tempLine[0];
    float b = tempLine[3] - a*tempLine[2];
    
    
    //擬合完整之后要對擬合的結果進行鑒定,選出最優的結果
    voteCnt = 0;
    for(int k = 0; k < Cnt; k++)
    {
      //如果在直線上或者附近
      if(abs(points[k].y - a*points[k].x - b) < 2)
      {
        voteCnt++;
      }
    }
 
    if(voteCnt > maxVoteCnt)
    {
      maxVoteCnt = voteCnt;
      inliersPercentage = (float)maxVoteCnt/Cnt;
//			printf("a: %f\tb%f\tpercent: %f\n", a, b, inliersPercentage);
      for(int m = 0; m < 4; m++)
      {
        line[m] = tempLine[m];
      }
      
    }	
    //當inliers的比例比較高的時候就可以直接取該值作為最優解
//		if(inliersPercentage > 0.2)
//		{
//			return inliersPercentage;
//		}
  }
  return inliersPercentage;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM