Skip to content

Commit 1bea537

Browse files
committed
Merge pull request opencv#18165 from catree:fix_hand_eye_calibration_Andreff_NaN_3.4
2 parents 75e88d6 + 379b83e commit 1bea537

File tree

3 files changed

+128
-41
lines changed

3 files changed

+128
-41
lines changed

modules/calib3d/include/opencv2/calib3d.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,23 +1989,23 @@ CV_EXPORTS_W Mat getOptimalNewCameraMatrix( InputArray cameraMatrix, InputArray
19891989
19901990
@param[in] R_gripper2base Rotation part extracted from the homogeneous matrix that transforms a point
19911991
expressed in the gripper frame to the robot base frame (\f$_{}^{b}\textrm{T}_g\f$).
1992-
This is a vector (`vector<Mat>`) that contains the rotation matrices for all the transformations
1993-
from gripper frame to robot base frame.
1992+
This is a vector (`vector<Mat>`) that contains the rotation, `(3x3)` rotation matrices or `(3x1)` rotation vectors,
1993+
for all the transformations from gripper frame to robot base frame.
19941994
@param[in] t_gripper2base Translation part extracted from the homogeneous matrix that transforms a point
19951995
expressed in the gripper frame to the robot base frame (\f$_{}^{b}\textrm{T}_g\f$).
1996-
This is a vector (`vector<Mat>`) that contains the translation vectors for all the transformations
1996+
This is a vector (`vector<Mat>`) that contains the `(3x1)` translation vectors for all the transformations
19971997
from gripper frame to robot base frame.
19981998
@param[in] R_target2cam Rotation part extracted from the homogeneous matrix that transforms a point
19991999
expressed in the target frame to the camera frame (\f$_{}^{c}\textrm{T}_t\f$).
2000-
This is a vector (`vector<Mat>`) that contains the rotation matrices for all the transformations
2001-
from calibration target frame to camera frame.
2000+
This is a vector (`vector<Mat>`) that contains the rotation, `(3x3)` rotation matrices or `(3x1)` rotation vectors,
2001+
for all the transformations from calibration target frame to camera frame.
20022002
@param[in] t_target2cam Rotation part extracted from the homogeneous matrix that transforms a point
20032003
expressed in the target frame to the camera frame (\f$_{}^{c}\textrm{T}_t\f$).
2004-
This is a vector (`vector<Mat>`) that contains the translation vectors for all the transformations
2004+
This is a vector (`vector<Mat>`) that contains the `(3x1)` translation vectors for all the transformations
20052005
from calibration target frame to camera frame.
2006-
@param[out] R_cam2gripper Estimated rotation part extracted from the homogeneous matrix that transforms a point
2006+
@param[out] R_cam2gripper Estimated `(3x3)` rotation part extracted from the homogeneous matrix that transforms a point
20072007
expressed in the camera frame to the gripper frame (\f$_{}^{g}\textrm{T}_c\f$).
2008-
@param[out] t_cam2gripper Estimated translation part extracted from the homogeneous matrix that transforms a point
2008+
@param[out] t_cam2gripper Estimated `(3x1)` translation part extracted from the homogeneous matrix that transforms a point
20092009
expressed in the camera frame to the gripper frame (\f$_{}^{g}\textrm{T}_c\f$).
20102010
@param[in] method One of the implemented Hand-Eye calibration method, see cv::HandEyeCalibrationMethod
20112011

modules/calib3d/src/calibration_handeye.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ static Mat homogeneousInverse(const Mat& T)
2929
// q = sin(theta/2) * v
3030
// theta - rotation angle
3131
// v - unit rotation axis, |v| = 1
32+
// Reference: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
3233
static Mat rot2quatMinimal(const Mat& R)
3334
{
3435
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
@@ -44,7 +45,7 @@ static Mat rot2quatMinimal(const Mat& R)
4445
qx = (m21 - m12) / S;
4546
qy = (m02 - m20) / S;
4647
qz = (m10 - m01) / S;
47-
} else if ((m00 > m11)&(m00 > m22)) {
48+
} else if (m00 > m11 && m00 > m22) {
4849
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
4950
qx = 0.25 * S;
5051
qy = (m01 + m10) / S;
@@ -98,6 +99,7 @@ static Mat quatMinimal2rot(const Mat& q)
9899
//
99100
// q - 4x1 unit quaternion <qw, qx, qy, qz>
100101
// R - 3x3 rotation matrix
102+
// Reference: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
101103
static Mat rot2quat(const Mat& R)
102104
{
103105
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
@@ -114,7 +116,7 @@ static Mat rot2quat(const Mat& R)
114116
qx = (m21 - m12) / S;
115117
qy = (m02 - m20) / S;
116118
qz = (m10 - m01) / S;
117-
} else if ((m00 > m11)&(m00 > m22)) {
119+
} else if (m00 > m11 && m00 > m22) {
118120
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
119121
qw = (m21 - m12) / S;
120122
qx = 0.25 * S;
@@ -572,7 +574,11 @@ static void calibrateHandEyeAndreff(const std::vector<Mat>& Hg, const std::vecto
572574
R = R.reshape(1, 2, newSize);
573575
//Eq 15
574576
double det = determinant(R);
575-
R = pow(sign_double(det) / abs(det), 1.0/3.0) * R;
577+
if (std::fabs(det) < FLT_EPSILON)
578+
{
579+
CV_Error(Error::StsNoConv, "calibrateHandEye() with CALIB_HAND_EYE_ANDREFF method: determinant(R) is null");
580+
}
581+
R = cubeRoot(static_cast<float>(sign_double(det) / abs(det))) * R;
576582

577583
Mat w, u, vt;
578584
SVDecomp(R, w, u, vt);

modules/calib3d/test/test_calibration_hand_eye.cpp

Lines changed: 111 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,38 @@
77

88
namespace opencv_test { namespace {
99

10+
static std::string getMethodName(HandEyeCalibrationMethod method)
11+
{
12+
std::string method_name = "";
13+
switch (method)
14+
{
15+
case CALIB_HAND_EYE_TSAI:
16+
method_name = "Tsai";
17+
break;
18+
19+
case CALIB_HAND_EYE_PARK:
20+
method_name = "Park";
21+
break;
22+
23+
case CALIB_HAND_EYE_HORAUD:
24+
method_name = "Horaud";
25+
break;
26+
27+
case CALIB_HAND_EYE_ANDREFF:
28+
method_name = "Andreff";
29+
break;
30+
31+
case CALIB_HAND_EYE_DANIILIDIS:
32+
method_name = "Daniilidis";
33+
break;
34+
35+
default:
36+
break;
37+
}
38+
39+
return method_name;
40+
}
41+
1042
class CV_CalibrateHandEyeTest : public cvtest::BaseTest
1143
{
1244
public:
@@ -48,7 +80,6 @@ class CV_CalibrateHandEyeTest : public cvtest::BaseTest
4880
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
4981
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper);
5082
Mat homogeneousInverse(const Mat& T);
51-
std::string getMethodName(HandEyeCalibrationMethod method);
5283
double sign_double(double val);
5384

5485
double eps_rvec[5];
@@ -340,45 +371,95 @@ Mat CV_CalibrateHandEyeTest::homogeneousInverse(const Mat& T)
340371
return Tinv;
341372
}
342373

343-
std::string CV_CalibrateHandEyeTest::getMethodName(HandEyeCalibrationMethod method)
374+
double CV_CalibrateHandEyeTest::sign_double(double val)
344375
{
345-
std::string method_name = "";
346-
switch (method)
347-
{
348-
case CALIB_HAND_EYE_TSAI:
349-
method_name = "Tsai";
350-
break;
376+
return (0 < val) - (val < 0);
377+
}
351378

352-
case CALIB_HAND_EYE_PARK:
353-
method_name = "Park";
354-
break;
379+
///////////////////////////////////////////////////////////////////////////////////////////////////
355380

356-
case CALIB_HAND_EYE_HORAUD:
357-
method_name = "Horaud";
358-
break;
381+
TEST(Calib3d_CalibrateHandEye, regression) { CV_CalibrateHandEyeTest test; test.safe_run(); }
359382

360-
case CALIB_HAND_EYE_ANDREFF:
361-
method_name = "Andreff";
362-
break;
383+
TEST(Calib3d_CalibrateHandEye, regression_17986)
384+
{
385+
const std::string camera_poses_filename = findDataFile("cv/hand_eye_calibration/cali.txt");
386+
const std::string end_effector_poses = findDataFile("cv/hand_eye_calibration/robot_cali.txt");
363387

364-
case CALIB_HAND_EYE_DANIILIDIS:
365-
method_name = "Daniilidis";
366-
break;
388+
std::vector<Mat> R_target2cam;
389+
std::vector<Mat> t_target2cam;
390+
// Parse camera poses
391+
{
392+
std::ifstream file(camera_poses_filename.c_str());
393+
ASSERT_TRUE(file.is_open());
394+
395+
int ndata = 0;
396+
file >> ndata;
397+
R_target2cam.reserve(ndata);
398+
t_target2cam.reserve(ndata);
399+
400+
std::string image_name;
401+
Matx33d cameraMatrix;
402+
Matx33d R;
403+
Matx31d t;
404+
Matx16d distCoeffs;
405+
Matx13d distCoeffs2;
406+
while (file >> image_name >>
407+
cameraMatrix(0,0) >> cameraMatrix(0,1) >> cameraMatrix(0,2) >>
408+
cameraMatrix(1,0) >> cameraMatrix(1,1) >> cameraMatrix(1,2) >>
409+
cameraMatrix(2,0) >> cameraMatrix(2,1) >> cameraMatrix(2,2) >>
410+
R(0,0) >> R(0,1) >> R(0,2) >>
411+
R(1,0) >> R(1,1) >> R(1,2) >>
412+
R(2,0) >> R(2,1) >> R(2,2) >>
413+
t(0) >> t(1) >> t(2) >>
414+
distCoeffs(0) >> distCoeffs(1) >> distCoeffs(2) >> distCoeffs(3) >> distCoeffs(4) >>
415+
distCoeffs2(0) >> distCoeffs2(1) >> distCoeffs2(2)) {
416+
R_target2cam.push_back(Mat(R));
417+
t_target2cam.push_back(Mat(t));
418+
}
419+
}
367420

368-
default:
369-
break;
421+
std::vector<Mat> R_gripper2base;
422+
std::vector<Mat> t_gripper2base;
423+
// Parse end-effector poses
424+
{
425+
std::ifstream file(end_effector_poses.c_str());
426+
ASSERT_TRUE(file.is_open());
427+
428+
int ndata = 0;
429+
file >> ndata;
430+
R_gripper2base.reserve(ndata);
431+
t_gripper2base.reserve(ndata);
432+
433+
Matx33d R;
434+
Matx31d t;
435+
Matx14d last_row;
436+
while (file >>
437+
R(0,0) >> R(0,1) >> R(0,2) >> t(0) >>
438+
R(1,0) >> R(1,1) >> R(1,2) >> t(1) >>
439+
R(2,0) >> R(2,1) >> R(2,2) >> t(2) >>
440+
last_row(0) >> last_row(1) >> last_row(2) >> last_row(3)) {
441+
R_gripper2base.push_back(Mat(R));
442+
t_gripper2base.push_back(Mat(t));
443+
}
370444
}
371445

372-
return method_name;
373-
}
446+
std::vector<HandEyeCalibrationMethod> methods;
447+
methods.push_back(CALIB_HAND_EYE_TSAI);
448+
methods.push_back(CALIB_HAND_EYE_PARK);
449+
methods.push_back(CALIB_HAND_EYE_HORAUD);
450+
methods.push_back(CALIB_HAND_EYE_ANDREFF);
451+
methods.push_back(CALIB_HAND_EYE_DANIILIDIS);
374452

375-
double CV_CalibrateHandEyeTest::sign_double(double val)
376-
{
377-
return (0 < val) - (val < 0);
378-
}
453+
for (size_t idx = 0; idx < methods.size(); idx++) {
454+
SCOPED_TRACE(cv::format("method=%s", getMethodName(methods[idx]).c_str()));
379455

380-
///////////////////////////////////////////////////////////////////////////////////////////////////
456+
Matx33d R_cam2gripper_est;
457+
Matx31d t_cam2gripper_est;
458+
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
381459

382-
TEST(Calib3d_CalibrateHandEye, regression) { CV_CalibrateHandEyeTest test; test.safe_run(); }
460+
EXPECT_TRUE(checkRange(R_cam2gripper_est));
461+
EXPECT_TRUE(checkRange(t_cam2gripper_est));
462+
}
463+
}
383464

384465
}} // namespace

0 commit comments

Comments
 (0)