2012-09-27 61 views
5

我有大量的魚眼鏡頭拍攝的照片。由於我想對照片進行一些圖像處理(例如邊緣檢測),因此我想消除桶形失真,這會嚴重影響我的效果。糾正FishEye鏡頭的桶形失真校正算法 - 未能用Java實現

經過一番研究和大量的閱讀文章,我發現這page:他們描述了一個算法(和一些公式)來解決這個問題。

M = A * rcorr^3 + B * rcorr^2 + C * rcorr + d
RSRC =(A * rcorr^3 + B * rcorr^2 + C * rcorr + d)* rcorr

RSRC =從源圖像的中心的像素的距離
rcorr =校正圖像
A,b,C =圖像 d的失真=線性圖像的縮放從中心像素的距離

我用這些公式並試圖在Java應用程序中實現這一點。不幸的是,它不工作,我沒有成功。 「修正」圖像看起來不像原始照片,而是在中間顯示一些神祕的圈子。看看這裏:

http://imageshack.us/f/844/barreldistortioncorrect.jpg/ (這曾經是一個白色的牛在前面的藍色牆壁的照片)

這裏是我的代碼:

protected int[] correction(int[] pixels) { 

    // 
    int[] pixelsCopy = pixels.clone(); 

    // parameters for correction 
    double paramA = 0.0; // affects only the outermost pixels of the image 
    double paramB = -0.02; // most cases only require b optimization 
    double paramC = 0.0; // most uniform correction 
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image 

    // 
    for(int x = 0; x < dstView.getImgWidth(); x++) { 
     for(int y = 0; y < dstView.getImgHeight(); y++) { 

      int dstX = x; 
      int dstY = y; 

      // center of dst image 
      double centerX = (dstView.getImgWidth() - 1)/2.0; 
      double centerY = (dstView.getImgHeight() - 1)/2.0; 

      // difference between center and point 
      double diffX = centerX - dstX; 
      double diffY = centerY - dstY; 
      // distance or radius of dst image 
      double dstR = Math.sqrt(diffX * diffX + diffY * diffY); 

      // distance or radius of src image (with formula) 
      double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR; 

      // comparing old and new distance to get factor 
      double factor = Math.abs(dstR/srcR); 
      // coordinates in source image 
      double srcXd = centerX + (diffX * factor); 
      double srcYd = centerY + (diffX * factor); 

      // no interpolation yet (just nearest point) 
      int srcX = (int)srcXd; 
      int srcY = (int)srcYd; 

      if(srcX >= 0 && srcY >= 0 && srcX < dstView.getImgWidth() && srcY < dstView.getImgHeight()) { 

       int dstPos = dstY * dstView.getImgWidth() + dstX; 
       pixels[dstPos] = pixelsCopy[srcY * dstView.getImgWidth() + srcX]; 
      } 
     } 
    } 

    return pixels; 
} 

我的問題是:
1)這個公式是否正確?
2)我是否犯了一個錯誤將該公式轉換爲軟件?
3)還有其他的算法(例如How to simulate fisheye lens effect by openCV?或wiki/Distortion_(光學)),它們更好嗎?

感謝您的幫助!

+0

邊緣附近的像素的正方形網格說明了很多問題可能是什麼。無論您的算法是否適用於任何照片,我都不知道。一個可能的原因是它不起作用,因爲你可能會過度糾正失真。 – AJMansfield

+0

正如我在下面提到的,我嘗試將b設置爲一個無限小的值。它給出了不同的結果(不再有球面校正),但仍然不顯示相同的圖像。請看這裏:http://imageshack.us/f/191/barreldistortioncorrect.jpg/ – Lucas

+0

可能無限小的b值在_other_方向過矯? – AJMansfield

回答

7

你擁有的主要缺陷是,該算法指定r_corr和r_src以((xDim-1)/ 2,(yDim-1)/ 2)爲單位。這需要完成以規範化計算,以便參數值不依賴於源圖像的大小。如果是這樣的代碼,您需要爲paramB使用更小的值,例如它對paramB = 0.00000002(對於尺寸爲2272 x 1704的圖像)可以正常工作。

您在計算與源圖像相比產生的圖像旋轉180度的中心差異方面也存在一個錯誤。

修復這兩個錯誤應該給你這樣的事情:

protected static int[] correction2(int[] pixels, int width, int height) { 
    int[] pixelsCopy = pixels.clone(); 

    // parameters for correction 
    double paramA = -0.007715; // affects only the outermost pixels of the image 
    double paramB = 0.026731; // most cases only require b optimization 
    double paramC = 0.0; // most uniform correction 
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image 

    for (int x = 0; x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int d = Math.min(width, height)/2; // radius of the circle 

      // center of dst image 
      double centerX = (width - 1)/2.0; 
      double centerY = (height - 1)/2.0; 

      // cartesian coordinates of the destination point (relative to the centre of the image) 
      double deltaX = (x - centerX)/d; 
      double deltaY = (y - centerY)/d; 

      // distance or radius of dst image 
      double dstR = Math.sqrt(deltaX * deltaX + deltaY * deltaY); 

      // distance or radius of src image (with formula) 
      double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR; 

      // comparing old and new distance to get factor 
      double factor = Math.abs(dstR/srcR); 

      // coordinates in source image 
      double srcXd = centerX + (deltaX * factor * d); 
      double srcYd = centerY + (deltaY * factor * d); 

      // no interpolation yet (just nearest point) 
      int srcX = (int) srcXd; 
      int srcY = (int) srcYd; 

      if (srcX >= 0 && srcY >= 0 && srcX < width && srcY < height) { 
       int dstPos = y * width + x; 
       pixels[dstPos] = pixelsCopy[srcY * width + srcX]; 
      } 
     } 
    } 

    return pixels; 
} 

在這個版本中,您可以使用現有鏡頭數據庫的參數值一樣LensFun(雖然你將需要翻轉每個參數的符號) 。描述算法的頁面現在可以在http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction

+0

感謝您的幫助! – Lucas

+0

我一直在使用魚眼鏡頭的360全景。我使用ptiGui作爲圖像失真和拼接的參考。但問題是,當我把ptgui提供的b c參數提供給你的代碼失真時,結果是非常不同的。事實上,在你的代碼中,p的作用幾乎與Ptgui相反。你認爲可能是什麼問題? –

0

大概是你的徑向畸變參數太大了,圖像變成了一個球體。嘗試在a,b,cd中輸入較小的值。

+0

設置一個無限小的值對於b(並且保留a = c = 0),不再有球體,但圖像中的所有像素似乎仍然混合在一起。看到這裏:http://imageshack.us/f/191/barreldistortioncorrect.jpg/是什麼讓我覺得我的代碼必須有問題,而不是算法。如果我設置a = b = c = 0和d = 1,那麼一切正常,圖像保持不變。 – Lucas

0

你的價值觀非常極端,所以你會看到極端的結果。

嘗試a = 0,b = 0,c = 1。這完全沒有描述任何修正,如果你的程序是正確的,你應該看到原始圖像。然後逐漸改變c和b。以0.1爲增量進行更改是一個好的開始。

2

我覺得你的圈子是由這一行造成的:

double srcYd = centerY + (diffX * factor); 

,我猜應該是:

double srcYd = centerY + (diffY * factor); 
+0

謝謝,這有幫助! – Lucas