ArUco marker z-axis mirrored
We are using ArUco markers for pose estimation and are encountering an issue.
Fairly frequently a marker's z-axis will flip. First of all it is not even clear to me how this would be possible since a proper orthogonal rotation would not be able to mirror an axis by itself.
Why is this a valid output of ArUco? It would mean the marker is turned away from the camera which would make it impossible to detect in the first place. Is it possible to fix with ArUco parameters?
Turning the corner refinement on, introduces another issue: http://answers.opencv.org/question/14...
Even if you don't know how to fix the underlying issue, it would be helpful to suggest how to undo the glitch. Described in words, a fix would check whether the normal vector of the marker is pointing away from the camera and then reflect the transformation on the xy-plane of the marker. Intuitively this sounds easy, but the rotation representation is very difficult for me to reason about.
Below is a test program that takes a file name as argument and two example images to demonstrate the issue. The two images are two consecutive frames of a video and have very little differences.
#include "opencv2/opencv.hpp"
#include "opencv2/aruco.hpp"
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
cv::Mat frame = cv::imread(argv[1]);
cv::Mat debugFrame = frame.clone();
// Intrinsic camera parameters.
cv::Mat camMatrix = cv::Mat::eye(3, 3, CV_64F);
cv::Mat distortionCoeffs = cv::Mat::zeros(8, 1, CV_64F);
camMatrix.at<double>(0, 0) = 2.3396381685789738e+03;
camMatrix.at<double>(0, 2) = 960.;
camMatrix.at<double>(1, 1) = 2.3396381685789738e+03;
camMatrix.at<double>(1, 2) = 540.;
distortionCoeffs.at<double>(0, 0) = -1.0982746232841779e-01;
distortionCoeffs.at<double>(1, 0) = 2.2689585715220828e-01;
distortionCoeffs.at<double>(2, 0) = 0.;
distortionCoeffs.at<double>(3, 0) = 0.;
distortionCoeffs.at<double>(4, 0) = -2.2112148171171589e-01;
cv::Ptr<cv::aruco::Dictionary> dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners;
std::vector<cv::Vec3d> rotationVec, translationVec;
params->doCornerRefinement = false;
cv::aruco::detectMarkers(frame, dict, markerCorners, markerIds, params);
auto numMarkers = markerIds.size();
if (numMarkers > 0)
{
cv::aruco::drawDetectedMarkers(debugFrame, markerCorners, markerIds);
cv::aruco::estimatePoseSingleMarkers(markerCorners, 5.0, camMatrix, distortionCoeffs, rotationVec, translationVec);
for (int i = 0; i < numMarkers; i++)
{
// Official aruco axis drawing.
cv::aruco::drawAxis(debugFrame, camMatrix, distortionCoeffs, rotationVec[i], translationVec[i], 0.5 * 5.0);
// Project marker origin.
std::vector< cv::Point3f > axisPoints;
axisPoints.push_back(cv::Point3f(0, 0, 0));
std::vector< cv::Point2f > imagePoints;
cv::projectPoints(axisPoints, rotationVec[i], translationVec[i], camMatrix, distortionCoeffs, imagePoints);
// Convert rotation vector to rotation matrix.
cv::Mat rotMat;
cv::Rodrigues(rotationVec[i], rotMat);
// Check for proper rotation matrix.
assert(cv::determinant(rotMat) > 0.99 && cv::determinant(rotMat) < 1.01);
// Manually rotate the z-axis vector.
cv::Mat testVec(cv::Point3f(0, 0, 10.0f));
testVec.convertTo(testVec, CV_64FC1);
cv::Mat testTransformed ...
@eskrs I believe it is just an optical illusion. For the last image, the marker axis is right-handed but the z-axis is not going downward or inside the table but toward us. The x-axis is going more or less inside the table. It is easier to see that by looking at the cube where the dimension does not fit if the rotation matrix was left-handed.
See also.
Pose estimation is not a trivial problem. Noise in the measurement or in the intrinsic parameters could lead to issue like that in my opinion.