用于校正鱼眼镜头的桶形失真校正算法 - 无法用 Java 实现
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12620025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Barrel distortion correction algorithm to correct FishEye lens - failing to implement with Java
提问by Lucas
I have a large bulk of photographs taken with a fisheye lens. As I want to do some image-processing (e.g. edge detection) on the photos I want to remove the barrel distortion which effects my results heavily.
我有大量用鱼眼镜头拍摄的照片。因为我想对照片进行一些图像处理(例如边缘检测),所以我想消除严重影响我的结果的桶形失真。
After some research and lots of read articles I found this page: They describe an algorithm (and some formulas) to solve this problem.
经过一些研究和大量阅读文章后,我找到了这个页面:他们描述了一个算法(和一些公式)来解决这个问题。
M = a *rcorr^3 + b * rcorr^2 + c * rcorr + d
rsrc = (a * rcorr^3 + b * rcorr^2 + c * rcorr + d) * rcorrrsrc = distance of a pixel from the center of the source image
rcorr = distance of a pixel from the center in the corrected image
a,b,c = distortion of image d = linear scaling of image
M = a *rcorr^3 + b * rcorr^2 + c * rcorr + d
rsrc = (a * rcorr^3 + b * rcorr^2 + c * rcorr + d) * rcorrrsrc = 一个像素到源图像中心的
距离 rcorr = 一个像素到校正后图像中心的距离
a,b,c = 图像失真 d = 图像的线性缩放
I used these formulas and tried to implement this in a Java application. Unfortunately it doesn't work and I failed to make it work. "Corrected" image look nothing like the original photograph and instead show some mysterious circles in the middle. Look here:
我使用了这些公式并尝试在 Java 应用程序中实现它。不幸的是它不起作用,我没能使它起作用。“校正后”的图像看起来与原始照片完全不同,而是在中间显示了一些神秘的圆圈。看这里:
http://imageshack.us/f/844/barreldistortioncorrect.jpg/(this used to be a photograph of a white cow in front a blue wall)
http://imageshack.us/f/844/barreldistortioncorrect.jpg/(这个曾经是蓝墙前白牛的照片)
Here is my code:
这是我的代码:
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;
}
My questions are:
1) Is this formula correct?
2) Do I have made a mistake turning that formula into a piece of software?
3) There are other algorithms out there (e.g. How to simulate fisheye lens effect by openCV?or wiki/Distortion_(optics)), are they better?
我的问题是:
1)这个公式是否正确?
2) 我把那个公式变成一个软件是不是搞错了?
3)还有其他算法(例如,如何通过 openCV 模拟鱼眼镜头效果?或 wiki/Distortion_(optics)),它们更好吗?
Thanks for your help!
谢谢你的帮助!
回答by SteveH
The main bug you have is that the algorithm specifies that r_corr and r_src are in units of min((xDim-1)/2, (yDim-1)/2). That needs to be done to normalise the calculation so that the parameter values are not dependent on the size of the source image. With the code as it is you'll need to use much smaller values for paramB, e.g. it worked ok for me with paramB = 0.00000002 (for an image with dimensions 2272 x 1704).
您遇到的主要错误是该算法指定 r_corr 和 r_src 的单位为 min((xDim-1)/2, (yDim-1)/2)。需要这样做来规范化计算,以便参数值不依赖于源图像的大小。使用代码,你需要为 paramB 使用更小的值,例如它对我来说工作正常,paramB = 0.00000002(对于尺寸为 2272 x 1704 的图像)。
You also have a bug in calculating the difference from the center that causes the resulting image to be rotated 180 degree compared to the source image.
您在计算与中心的差异时也有一个错误,导致生成的图像与源图像相比旋转了 180 度。
Fixing both these bugs should give you something like this:
修复这两个错误应该会给你这样的东西:
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;
}
With this version you can use parameter values from existing lens databases like LensFun (though you'll need to flip the sign of each parameter). The page describing the algorithm can now be found at http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction
在此版本中,您可以使用来自现有镜头数据库(如 LensFun)的参数值(尽管您需要翻转每个参数的符号)。现在可以在http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correction找到描述算法的页面
回答by Tom Makin
I think your circles are caused by this line:
我认为你的圈子是由这条线引起的:
double srcYd = centerY + (diffX * factor);
which I'm guessing should be:
我猜应该是:
double srcYd = centerY + (diffY * factor);
回答by Andrey Rubshtein
Probably your radial distortion parameters are too large, and the image became packed on a sphere. Try to put smaller values in a
,b
,c
and d
.
可能是您的径向畸变参数太大,并且图像被包裹在一个球体上。尝试把较小的值a
,b
,c
和d
。
回答by Bernie Sumption
Your values are very extreme, so you see extreme results.
你的价值观非常极端,所以你会看到极端的结果。
Try a=0, b=0, c=1. That describes no correction at all, if your program is correct you should see the original image. Then gradually change c and b. Changing in increments of 0.1 is a good start.
尝试 a=0、b=0、c=1。这根本就说明没有更正,如果您的程序正确,您应该看到原始图像。然后逐渐改变c和b。以 0.1 为增量进行更改是一个好的开始。