// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. #include "test_precomp.hpp" #include "test_aruco_utils.hpp" namespace opencv_test { vector getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec, InputArray _tvec, float length, const Point2f offset) { vector axis; axis.push_back(Point3f(offset.x, offset.y, 0.f)); axis.push_back(Point3f(length+offset.x, offset.y, 0.f)); axis.push_back(Point3f(offset.x, length+offset.y, 0.f)); axis.push_back(Point3f(offset.x, offset.y, length)); vector axis_to_img; projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img); return axis_to_img; } vector getMarkerById(int id, const vector >& corners, const vector& ids) { for (size_t i = 0ull; i < ids.size(); i++) if (ids[i] == id) return corners[i]; return vector(); } void getSyntheticRT(double yaw, double pitch, double distance, Mat& rvec, Mat& tvec) { rvec = Mat::zeros(3, 1, CV_64FC1); tvec = Mat::zeros(3, 1, CV_64FC1); // rotate "scene" in pitch axis (x-axis) Mat rotPitch(3, 1, CV_64FC1); rotPitch.at(0) = -pitch; rotPitch.at(1) = 0; rotPitch.at(2) = 0; // rotate "scene" in yaw (y-axis) Mat rotYaw(3, 1, CV_64FC1); rotYaw.at(0) = 0; rotYaw.at(1) = yaw; rotYaw.at(2) = 0; // compose both rotations composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw, Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec); // Tvec, just move in z (camera) direction the specific distance tvec.at(0) = 0.; tvec.at(1) = 0.; tvec.at(2) = distance; } void projectMarker(Mat& img, const aruco::Board& board, int markerIndex, Mat cameraMatrix, Mat rvec, Mat tvec, int markerBorder) { // canonical image Mat markerImg; const int markerSizePixels = 100; aruco::generateImageMarker(board.getDictionary(), board.getIds()[markerIndex], markerSizePixels, markerImg, markerBorder); // projected corners Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0)); vector corners; // get max coordinate of board Point3f maxCoord = board.getRightBottomCorner(); // copy objPoints vector objPoints = board.getObjPoints()[markerIndex]; // move the marker to the origin for (size_t i = 0; i < objPoints.size(); i++) objPoints[i] -= (maxCoord / 2.f); projectPoints(objPoints, rvec, tvec, cameraMatrix, distCoeffs, corners); // get perspective transform vector originalCorners; originalCorners.push_back(Point2f(0, 0)); originalCorners.push_back(Point2f((float)markerSizePixels, 0)); originalCorners.push_back(Point2f((float)markerSizePixels, (float)markerSizePixels)); originalCorners.push_back(Point2f(0, (float)markerSizePixels)); Mat transformation = getPerspectiveTransform(originalCorners, corners); // apply transformation Mat aux; const char borderValue = 127; warpPerspective(markerImg, aux, transformation, img.size(), INTER_NEAREST, BORDER_CONSTANT, Scalar::all(borderValue)); // copy only not-border pixels for (int y = 0; y < aux.rows; y++) { for (int x = 0; x < aux.cols; x++) { if (aux.at< unsigned char >(y, x) == borderValue) continue; img.at< unsigned char >(y, x) = aux.at< unsigned char >(y, x); } } } Mat projectBoard(const aruco::GridBoard& board, Mat cameraMatrix, double yaw, double pitch, double distance, Size imageSize, int markerBorder) { Mat rvec, tvec; getSyntheticRT(yaw, pitch, distance, rvec, tvec); Mat img = Mat(imageSize, CV_8UC1, Scalar::all(255)); for (unsigned int index = 0; index < board.getIds().size(); index++) projectMarker(img, board, index, cameraMatrix, rvec, tvec, markerBorder); return img; } /** Check if a set of 3d points are enough for calibration. Z coordinate is ignored. * Only axis parallel lines are considered */ static bool _arePointsEnoughForPoseEstimation(const std::vector &points) { if(points.size() < 4) return false; std::vector sameXValue; // different x values in points std::vector sameXCounter; // number of points with the x value in sameXValue for(unsigned int i = 0; i < points.size(); i++) { bool found = false; for(unsigned int j = 0; j < sameXValue.size(); j++) { if(sameXValue[j] == points[i].x) { found = true; sameXCounter[j]++; } } if(!found) { sameXValue.push_back(points[i].x); sameXCounter.push_back(1); } } // count how many x values has more than 2 points int moreThan2 = 0; for(unsigned int i = 0; i < sameXCounter.size(); i++) { if(sameXCounter[i] >= 2) moreThan2++; } // if we have more than 1 two xvalues with more than 2 points, calibration is ok if(moreThan2 > 1) return true; return false; } bool getCharucoBoardPose(InputArray charucoCorners, InputArray charucoIds, const aruco::CharucoBoard &board, InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, InputOutputArray tvec, bool useExtrinsicGuess) { CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total())); if(charucoIds.getMat().total() < 4) return false; // need, at least, 4 corners std::vector objPoints; objPoints.reserve(charucoIds.getMat().total()); for(unsigned int i = 0; i < charucoIds.getMat().total(); i++) { int currId = charucoIds.getMat().at< int >(i); CV_Assert(currId >= 0 && currId < (int)board.getChessboardCorners().size()); objPoints.push_back(board.getChessboardCorners()[currId]); } // points need to be in different lines, check if detected points are enough if(!_arePointsEnoughForPoseEstimation(objPoints)) return false; solvePnP(objPoints, charucoCorners, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess); return true; } /** * @brief Return object points for the system centered in a middle (by default) or in a top left corner of single * marker, given the marker length */ static Mat _getSingleMarkerObjectPoints(float markerLength, bool use_aruco_ccw_center) { CV_Assert(markerLength > 0); Mat objPoints(4, 1, CV_32FC3); // set coordinate system in the top-left corner of the marker, with Z pointing out if (use_aruco_ccw_center) { objPoints.ptr(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0); objPoints.ptr(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0); objPoints.ptr(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0); objPoints.ptr(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0); } else { objPoints.ptr(0)[0] = Vec3f(0.f, 0.f, 0); objPoints.ptr(0)[1] = Vec3f(markerLength, 0.f, 0); objPoints.ptr(0)[2] = Vec3f(markerLength, markerLength, 0); objPoints.ptr(0)[3] = Vec3f(0.f, markerLength, 0); } return objPoints; } void getMarkersPoses(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs, OutputArray _rvecs, OutputArray _tvecs, OutputArray objPoints, bool use_aruco_ccw_center, SolvePnPMethod solvePnPMethod) { CV_Assert(markerLength > 0); Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, use_aruco_ccw_center); int nMarkers = (int)corners.total(); _rvecs.create(nMarkers, 1, CV_64FC3); _tvecs.create(nMarkers, 1, CV_64FC3); Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat(); for (int i = 0; i < nMarkers; i++) solvePnP(markerObjPoints, corners.getMat(i), cameraMatrix, distCoeffs, rvecs.at(i), tvecs.at(i), false, solvePnPMethod); if(objPoints.needed()) markerObjPoints.convertTo(objPoints, -1); } }