用于纠正FishEye镜头的桶形失真校正算法 - 无法用Java实现

时间:2012-09-27 11:11:31

标签: java algorithm image-processing distortion fisheye

我有一大堆用鱼眼镜头拍摄的照片。由于我想对照片进行一些图像处理(例如边缘检测),我想要去除桶形失真,这会严重影响我的结果。

经过一些研究和大量阅读文章,我发现了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_(光学)),它们会更好吗?

感谢您的帮助!

4 个答案:

答案 0 :(得分:8)

你遇到的主要错误是算法指定r_corr和r_src以min((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

找到描述算法的页面

答案 1 :(得分:2)

我认为你的圈子是由这一行引起的:

double srcYd = centerY + (diffX * factor);

我猜应该是:

double srcYd = centerY + (diffY * factor);

答案 2 :(得分:0)

可能你的径向畸变参数太大,图像被包裹在球体上。尝试在abcd中添加较小的值。

答案 3 :(得分:0)

你的价值观非常极端,所以你会看到极端的结果。

尝试a = 0,b = 0,c = 1。这描述没有任何修正,如果你的程序是正确的,你应该看到原始图像。然后逐渐改变c和b。以0.1为增量的变化是一个良好的开端。