2012-09-27 62 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