mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
8.2 KiB
205 lines
8.2 KiB
// 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); |
|
} |
|
|
|
}
|
|
|