mirror of
https://github.com/opencv/opencv.git
synced 2026-11-09 18:04:29 -06:00
calib3d: fix IPPE rotation vector for rotations near 180 degrees
PoseSolver::rot2vec reads the rotation axis from R - R^T and divides by sin(theta). Both vanish when the rotation is close to 180 degrees, which is the pose of a square tag seen exactly head-on, so SOLVEPNP_IPPE_SQUARE returned a pose flipped by 180 degrees. Fall back to Rodrigues in that case and clamp the acos argument. The fast path for all other rotations is unchanged.
This commit is contained in:
@@ -303,13 +303,17 @@ void PoseSolver::rot2vec(InputArray _R, OutputArray _r)
|
||||
Mat rvec = _r.getMat();
|
||||
|
||||
double trace = R.at<double>(0, 0) + R.at<double>(1, 1) + R.at<double>(2, 2);
|
||||
double w_norm = acos((trace - 1.0) / 2.0);
|
||||
double w_norm = acos(std::min(std::max((trace - 1.0) / 2.0, -1.0), 1.0));
|
||||
double eps = std::numeric_limits<float>::epsilon();
|
||||
double d = 1 / (2 * sin(w_norm)) * w_norm;
|
||||
if (w_norm < eps) //rotation is the identity
|
||||
{
|
||||
rvec.setTo(0);
|
||||
}
|
||||
else if (sin(w_norm) < 1e-5) //close to 180 degrees, the axis can't be read from R - R^T
|
||||
{
|
||||
Rodrigues(R, rvec);
|
||||
}
|
||||
else
|
||||
{
|
||||
double c0 = R.at<double>(2, 1) - R.at<double>(1, 2);
|
||||
|
||||
@@ -2404,4 +2404,30 @@ TEST(AP3P, ctheta1p_nan_23607)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Calib3d_SolvePnP, regression_30402)
|
||||
{
|
||||
// A tag seen exactly head-on: the pose has a 180 degree rotation about X.
|
||||
const double L = 0.05;
|
||||
std::vector<Point3d> objectPoints = {
|
||||
Point3d(-L, L, 0), Point3d( L, L, 0), Point3d( L, -L, 0), Point3d(-L, -L, 0)
|
||||
};
|
||||
Matx33d intrinsics(800, 0, 320,
|
||||
0, 800, 240,
|
||||
0, 0, 1);
|
||||
Vec3d rvecGT(CV_PI, 0, 0), tvecGT(0, 0, 0.5);
|
||||
|
||||
std::vector<Point2d> imagePoints;
|
||||
projectPoints(objectPoints, rvecGT, tvecGT, intrinsics, noArray(), imagePoints);
|
||||
|
||||
Mat rvec, tvec;
|
||||
ASSERT_TRUE(solvePnP(objectPoints, imagePoints, intrinsics, noArray(), rvec, tvec,
|
||||
false, SOLVEPNP_IPPE_SQUARE));
|
||||
|
||||
Mat R, RGT;
|
||||
cv::Rodrigues(rvec, R);
|
||||
cv::Rodrigues(rvecGT, RGT);
|
||||
EXPECT_LE(cvtest::norm(R, RGT, NORM_INF), 1e-6);
|
||||
EXPECT_LE(cvtest::norm(tvec, Mat(tvecGT), NORM_INF), 1e-6);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Reference in New Issue
Block a user