Skip to content

Commit 657c8d1

Browse files
authored
Merge pull request opencv#17454 from creinders:master
fix instable fisheye undistortPoints * remove artefacts when (un)distorting fisheye images with large distortion coefficient values * fix fisheye undistortion when theta is close to zero * add fisheye image undistort and distort test * Fixed type conversion warnings * fixed trailing whitespace
1 parent e03891b commit 657c8d1

File tree

2 files changed

+141
-11
lines changed

2 files changed

+141
-11
lines changed

modules/calib3d/src/fisheye.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -377,21 +377,24 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
377377
Vec2d pi = sdepth == CV_32F ? (Vec2d)srcf[i] : srcd[i]; // image point
378378
Vec2d pw((pi[0] - c[0])/f[0], (pi[1] - c[1])/f[1]); // world point
379379

380-
double scale = 1.0;
381-
382380
double theta_d = sqrt(pw[0]*pw[0] + pw[1]*pw[1]);
383381

384382
// the current camera model is only valid up to 180 FOV
385383
// for larger FOV the loop below does not converge
386384
// clip values so we still get plausible results for super fisheye images > 180 grad
387385
theta_d = min(max(-CV_PI/2., theta_d), CV_PI/2.);
388386

389-
if (theta_d > 1e-8)
387+
bool converged = false;
388+
double theta = theta_d;
389+
390+
double scale = 0.0;
391+
392+
if (fabs(theta_d) > 1e-8)
390393
{
391394
// compensate distortion iteratively
392-
double theta = theta_d;
393395

394396
const double EPS = 1e-8; // or std::numeric_limits<double>::epsilon();
397+
395398
for (int j = 0; j < 10; j++)
396399
{
397400
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta6*theta2;
@@ -401,22 +404,47 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
401404
(1 + 3*k0_theta2 + 5*k1_theta4 + 7*k2_theta6 + 9*k3_theta8);
402405
theta = theta - theta_fix;
403406
if (fabs(theta_fix) < EPS)
407+
{
408+
converged = true;
404409
break;
410+
}
405411
}
406412

407413
scale = std::tan(theta) / theta_d;
408414
}
415+
else
416+
{
417+
converged = true;
418+
}
409419

410-
Vec2d pu = pw * scale; //undistorted point
420+
// theta is monotonously increasing or decreasing depending on the sign of theta
421+
// if theta has flipped, it might converge due to symmetry but on the opposite of the camera center
422+
// so we can check whether theta has changed the sign during the optimization
423+
bool theta_flipped = ((theta_d < 0 && theta > 0) || (theta_d > 0 && theta < 0));
411424

412-
// reproject
413-
Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix
414-
Vec2d fi(pr[0]/pr[2], pr[1]/pr[2]); // final
425+
if (converged && !theta_flipped)
426+
{
427+
Vec2d pu = pw * scale; //undistorted point
428+
429+
// reproject
430+
Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix
431+
Vec2d fi(pr[0]/pr[2], pr[1]/pr[2]); // final
415432

416-
if( sdepth == CV_32F )
417-
dstf[i] = fi;
433+
if( sdepth == CV_32F )
434+
dstf[i] = fi;
435+
else
436+
dstd[i] = fi;
437+
}
418438
else
419-
dstd[i] = fi;
439+
{
440+
// Vec2d fi(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
441+
Vec2d fi(-1000000.0, -1000000.0);
442+
443+
if( sdepth == CV_32F )
444+
dstf[i] = fi;
445+
else
446+
dstd[i] = fi;
447+
}
420448
}
421449
}
422450

modules/calib3d/test/test_fisheye.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,108 @@ TEST_F(fisheyeTest, undistortImage)
141141
}
142142
}
143143

144+
TEST_F(fisheyeTest, undistortAndDistortImage)
145+
{
146+
cv::Matx33d K_src = this->K;
147+
cv::Mat D_src = cv::Mat(this->D);
148+
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg");
149+
cv::Matx33d K_dst = K_src;
150+
cv::Mat image = cv::imread(file), image_projected;
151+
cv::Vec4d D_dst_vec (-1.0, 0.0, 0.0, 0.0);
152+
cv::Mat D_dst = cv::Mat(D_dst_vec);
153+
154+
int imageWidth = (int)this->imageSize.width;
155+
int imageHeight = (int)this->imageSize.height;
156+
157+
cv::Mat imagePoints(imageHeight, imageWidth, CV_32FC2), undPoints, distPoints;
158+
cv::Vec2f* pts = imagePoints.ptr<cv::Vec2f>();
159+
160+
for(int y = 0, k = 0; y < imageHeight; ++y)
161+
{
162+
for(int x = 0; x < imageWidth; ++x)
163+
{
164+
cv::Vec2f point((float)x, (float)y);
165+
pts[k++] = point;
166+
}
167+
}
168+
169+
cv::fisheye::undistortPoints(imagePoints, undPoints, K_dst, D_dst);
170+
cv::fisheye::distortPoints(undPoints, distPoints, K_src, D_src);
171+
cv::remap(image, image_projected, distPoints, cv::noArray(), cv::INTER_LINEAR);
172+
173+
float dx, dy, r_sq;
174+
float R_MAX = 250;
175+
float imageCenterX = (float)imageWidth / 2;
176+
float imageCenterY = (float)imageHeight / 2;
177+
178+
cv::Mat undPointsGt(imageHeight, imageWidth, CV_32FC2);
179+
cv::Mat imageGt(imageHeight, imageWidth, CV_8UC3);
180+
181+
for(int y = 0, k = 0; y < imageHeight; ++y)
182+
{
183+
for(int x = 0; x < imageWidth; ++x)
184+
{
185+
dx = x - imageCenterX;
186+
dy = y - imageCenterY;
187+
r_sq = dy * dy + dx * dx;
188+
189+
Vec2f & und_vec = undPoints.at<Vec2f>(y,x);
190+
Vec3b & pixel = image_projected.at<Vec3b>(y,x);
191+
192+
Vec2f & undist_vec_gt = undPointsGt.at<Vec2f>(y,x);
193+
Vec3b & pixel_gt = imageGt.at<Vec3b>(y,x);
194+
195+
if (r_sq > R_MAX * R_MAX)
196+
{
197+
198+
undist_vec_gt[0] = -1e6;
199+
undist_vec_gt[1] = -1e6;
200+
201+
pixel_gt[0] = 0;
202+
pixel_gt[1] = 0;
203+
pixel_gt[2] = 0;
204+
}
205+
else
206+
{
207+
undist_vec_gt[0] = und_vec[0];
208+
undist_vec_gt[1] = und_vec[1];
209+
210+
pixel_gt[0] = pixel[0];
211+
pixel_gt[1] = pixel[1];
212+
pixel_gt[2] = pixel[2];
213+
}
214+
215+
k++;
216+
}
217+
}
218+
219+
EXPECT_MAT_NEAR(undPoints, undPointsGt, 1e-10);
220+
EXPECT_MAT_NEAR(image_projected, imageGt, 1e-10);
221+
222+
Vec2f dist_point_1 = distPoints.at<Vec2f>(400, 640);
223+
Vec2f dist_point_1_gt(640.044f, 400.041f);
224+
225+
Vec2f dist_point_2 = distPoints.at<Vec2f>(400, 440);
226+
Vec2f dist_point_2_gt(409.731f, 403.029f);
227+
228+
Vec2f dist_point_3 = distPoints.at<Vec2f>(200, 640);
229+
Vec2f dist_point_3_gt(643.341f, 168.896f);
230+
231+
Vec2f dist_point_4 = distPoints.at<Vec2f>(300, 480);
232+
Vec2f dist_point_4_gt(463.402f, 290.317f);
233+
234+
Vec2f dist_point_5 = distPoints.at<Vec2f>(550, 750);
235+
Vec2f dist_point_5_gt(797.51f, 611.637f);
236+
237+
EXPECT_MAT_NEAR(dist_point_1, dist_point_1_gt, 1e-2);
238+
EXPECT_MAT_NEAR(dist_point_2, dist_point_2_gt, 1e-2);
239+
EXPECT_MAT_NEAR(dist_point_3, dist_point_3_gt, 1e-2);
240+
EXPECT_MAT_NEAR(dist_point_4, dist_point_4_gt, 1e-2);
241+
EXPECT_MAT_NEAR(dist_point_5, dist_point_5_gt, 1e-2);
242+
243+
CV_Assert(cv::imwrite(combine(datasets_repository_path, "new_distortion.png"), image_projected));
244+
}
245+
144246
TEST_F(fisheyeTest, jacobians)
145247
{
146248
int n = 10;

0 commit comments

Comments
 (0)