| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- // 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<Point2f> getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec,
- InputArray _tvec, float length, const Point2f offset) {
- vector<Point3f> 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<Point2f> axis_to_img;
- projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img);
- return axis_to_img;
- }
- vector<Point2f> getMarkerById(int id, const vector<vector<Point2f> >& corners, const vector<int>& ids) {
- for (size_t i = 0ull; i < ids.size(); i++)
- if (ids[i] == id)
- return corners[i];
- return vector<Point2f>();
- }
- 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<double>(0) = -pitch;
- rotPitch.at<double>(1) = 0;
- rotPitch.at<double>(2) = 0;
- // rotate "scene" in yaw (y-axis)
- Mat rotYaw(3, 1, CV_64FC1);
- rotYaw.at<double>(0) = 0;
- rotYaw.at<double>(1) = yaw;
- rotYaw.at<double>(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<double>(0) = 0.;
- tvec.at<double>(1) = 0.;
- tvec.at<double>(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<Point2f> corners;
- // get max coordinate of board
- Point3f maxCoord = board.getRightBottomCorner();
- // copy objPoints
- vector<Point3f> 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<Point2f> 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<Point3f> &points) {
- if(points.size() < 4) return false;
- std::vector<double> sameXValue; // different x values in points
- std::vector<int> 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<Point3f> 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<Vec3f>(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0);
- objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0);
- objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0);
- objPoints.ptr<Vec3f>(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0);
- }
- else {
- objPoints.ptr<Vec3f>(0)[0] = Vec3f(0.f, 0.f, 0);
- objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength, 0.f, 0);
- objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength, markerLength, 0);
- objPoints.ptr<Vec3f>(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<Vec3d>(i), tvecs.at<Vec3d>(i),
- false, solvePnPMethod);
- if(objPoints.needed())
- markerObjPoints.convertTo(objPoints, -1);
- }
- }
|