【问题标题】:Barrel distortion correction algorithm to correct FishEye lens - failing to implement with Java用于校正 FishEye 镜头的桶形失真校正算法 - 无法用 Java 实现
【发布时间】:2012-09-19 03:48:55
【问题描述】:

我有大量使用鱼眼镜头拍摄的照片。因为我想对照片进行一些图像处理(例如边缘检测),所以我想消除严重影响结果的桶形失真。

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

感谢您的帮助!

【问题讨论】:

  • 边缘附近的方形像素网格可以说明问题可能是什么。你的算法是否适用于任何照片,我不知道。它不起作用的一个可能原因是您可能过度校正了失真。
  • 正如我在下面提到的,我尝试将 b 设置为无限小的值。它给出了不同的结果(不再进行球面校正),但仍然不显示相同的图像。见这里:imageshack.us/f/191/barreldistortioncorrect.jpg
  • 可能无限小的 b 值在 other 方向上过度校正?
  • 尝试制作一个动画,当您将参数值从一个极端滑动到另一个极端时,图像会发生什么;这可以说明你的问题。如果您可以使用 Wolfram Mathematica 之类的工具,这将非常简单,但即使没有这些,您也可以让它为不同的参数值生成大量图像并将它们拼接成动画。
  • 另外,试着用铅笔在纸上复习所有数学,看看图像像素实际发生了什么,以确保你的数学是正确的。

标签: java algorithm image-processing distortion fisheye


【解决方案1】:

您遇到的主要错误是算法指定 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

找到描述该算法的页面

【讨论】:

  • 我一直在使用鱼眼镜头制作 360 度全景。我使用 ptiGui 作为图像不失真和拼接的参考。但问题是,当我将 ptgui 提供的用于不失真的 a b c 参数放入您的代码中时,结果非常不同。事实上,在 Ptgui 中,a 的效果与您的代码几乎相反。您认为可能是什么问题??
  • 嗨@SteveH,很好的解决方案!你知道一种方法来获取你创建的新坐标并将它们转换回 R 中的图像或矩阵格式吗?我尝试了您的解决方案,但您的最后一行遇到了问题:像素 [dstPos] = pixelCopy [srcY * width + srcX]。我认为这在 Java 中的工作方式与在 R 中不同。
【解决方案2】:

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

double srcYd = centerY + (diffX * factor);

我猜应该是:

double srcYd = centerY + (diffY * factor);

【讨论】:

    【解决方案3】:

    可能您的径向畸变参数太大,图像被压缩在一个球体上。尝试在abcd 中设置较小的值。

    【讨论】:

    • 为 b 设置一个无限小的值(并保留 a = c = 0),不再有球体,但图像中的所有像素似乎仍然混合在一起。见这里:imageshack.us/f/191/barreldistortioncorrect.jpg 是什么让我认为我的代码而不是算法一定有问题。如果我设置 a = b = c = 0 和 d = 1,那么一切正常,图像保持不变。
    【解决方案4】:

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

    尝试 a=0、b=0、c=1。这根本没有更正,如果您的程序正确,您应该看到原始图像。然后逐渐改变c和b。以 0.1 为增量进行更改是一个好的开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 2011-06-26
      • 1970-01-01
      • 2015-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多