I managed to understand the code from math.c (this is what I call unreadable code), but all the other questions are still open.
Hello,
I've read a lot on the PanoTools lens correction model in this user group and over the Internet, however I still have a lot of questions. All its disadvantages were discussed several times, but I could not a detailed description of the mechanism itself.
I have calibrated a fisheye lens using Kannala model, which is basically a polynomial R(theta), where R is the polar coordinates radius of current point. Now I have a need to work with this calibration in PTGui. We use Canon APS-C matrix crop-factor 1.62, 4272x2848 matrix in the landscape mode, 8 mm Sigma fisheyes.
I need to express PanoTools model as a function R(theta). To accomplish this, I can iterate all possible thetas in the [0, pi/2] range and find the radius R, corresponding to it. This is how I do it:
1) calculate undistorted equidistant radius (used both by Panotools and PTGui, not by Hugin, right?) R_undist = f*theta*m as undistorted radius in pixels. I take m as a mm-to-pixel conversion factor from camera specification: matrix width is 22.2 mm = 4272 pixels.
2) convert it to PTGui units using the condition r=1 when r=width/2 (this is taken from PTGui documentation). r = R_undist / (width/2).
3) solve quartic equation in PTGui units: a*r^4 + b*r^3 + c*r^2 + (1-a-b-c)*r - Rundist = 0 , which gives us r - the distorted radius
4) convert distorted radius to pixels: r_dist_px = r_dist*(width/2), so we have a r(theta) mapping.
For backprojection theta(R) we have to do the same it the opposite direction:
1) Convert R to PTGui units: r = R/(width/2)
2) Apply the undistorting polynomial: r_undist = a*r^4+b*r^3+c^r*2+(1-a-b-c)*r
3) Convert r_undist back to pixels, and then to mm : r_undist_mm = r_undist*(width/2)/m
4) Taking f*theta model into account, return r_undist_mm / f as a backprojection angle theta.
This looks logical, but doesn't work. The radiuses don't get distorted and undistorted as expected. I've also found this intriguing code in math.c of libpano13. Why are the coefficients used with r, and not polynomially? The same thing can be found in "inv_radial" function, where the polynomial is solved. Maybe I misunderstand the whole model?
int radial( double x_dest, double y_dest, double* x_src, double* y_src, void* params)
{
// params: double coefficients[4], scale, correction_radius
register double r, scale;
r = (sqrt( x_dest*x_dest + y_dest*y_dest )) / ((double*)params)[4]; // r = r_px/scale
if( r < ((double*)params)[5] )
{
scale = ((((double*)params)[3] * r + ((double*)params)[2]) * r +
((double*)params)[1]) * r + ((double*)params)[0]; // scale = a*r+b*r+c*r+d
}
else
scale = 1000.0;
*x_src = x_dest * scale ;
*y_src = y_dest * scale ;
return 1;
}
I've also tried to understand the conversion from fov to f and back. This is what Joost, the author of PTGui, exactly told me:
Yes PTGui still uses the f*theta projection.
This is how PTGui calculates the horz fov:
hfov=4*RAD2DEG*asin(cropwidth_mm/4.0/focallength);
cropwidth_mm is the diameter of the cropping circle, or the width of the image.
I realize that this is not consistent with the equidistant formula but it does match the fov for practical lenses.
However, when I use the diameter of crop circle as cropwidth_mm (4500 px in my case), it doesn't work correctly. I empirically found, that the mistake is linear, and the correct conversion formula is f = 0.967555562*D/(4*m*sin(radians(fov)/4)) or hfov=4*asin(0.967555562*D/(4*m*focallength)), where D is the crop width in pixels, m the scale coefficient desribed earlier. The fact that the mistake is linear makes me think I use a different value of m? Or maybe it's more complicated.
I've contacted the author of PTGui himself, he has answered some of my questions, but was not willing to solve the problems, so I decided to try to get advice in this group.
Thanks in advance for any help,
Lire