test_aruco_utils.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html.
  4. #include "test_precomp.hpp"
  5. #include "test_aruco_utils.hpp"
  6. namespace opencv_test {
  7. vector<Point2f> getAxis(InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _rvec,
  8. InputArray _tvec, float length, const Point2f offset) {
  9. vector<Point3f> axis;
  10. axis.push_back(Point3f(offset.x, offset.y, 0.f));
  11. axis.push_back(Point3f(length+offset.x, offset.y, 0.f));
  12. axis.push_back(Point3f(offset.x, length+offset.y, 0.f));
  13. axis.push_back(Point3f(offset.x, offset.y, length));
  14. vector<Point2f> axis_to_img;
  15. projectPoints(axis, _rvec, _tvec, _cameraMatrix, _distCoeffs, axis_to_img);
  16. return axis_to_img;
  17. }
  18. vector<Point2f> getMarkerById(int id, const vector<vector<Point2f> >& corners, const vector<int>& ids) {
  19. for (size_t i = 0ull; i < ids.size(); i++)
  20. if (ids[i] == id)
  21. return corners[i];
  22. return vector<Point2f>();
  23. }
  24. void getSyntheticRT(double yaw, double pitch, double distance, Mat& rvec, Mat& tvec) {
  25. rvec = Mat::zeros(3, 1, CV_64FC1);
  26. tvec = Mat::zeros(3, 1, CV_64FC1);
  27. // rotate "scene" in pitch axis (x-axis)
  28. Mat rotPitch(3, 1, CV_64FC1);
  29. rotPitch.at<double>(0) = -pitch;
  30. rotPitch.at<double>(1) = 0;
  31. rotPitch.at<double>(2) = 0;
  32. // rotate "scene" in yaw (y-axis)
  33. Mat rotYaw(3, 1, CV_64FC1);
  34. rotYaw.at<double>(0) = 0;
  35. rotYaw.at<double>(1) = yaw;
  36. rotYaw.at<double>(2) = 0;
  37. // compose both rotations
  38. composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw,
  39. Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec);
  40. // Tvec, just move in z (camera) direction the specific distance
  41. tvec.at<double>(0) = 0.;
  42. tvec.at<double>(1) = 0.;
  43. tvec.at<double>(2) = distance;
  44. }
  45. void projectMarker(Mat& img, const aruco::Board& board, int markerIndex, Mat cameraMatrix, Mat rvec, Mat tvec,
  46. int markerBorder) {
  47. // canonical image
  48. Mat markerImg;
  49. const int markerSizePixels = 100;
  50. aruco::generateImageMarker(board.getDictionary(), board.getIds()[markerIndex], markerSizePixels, markerImg, markerBorder);
  51. // projected corners
  52. Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
  53. vector<Point2f> corners;
  54. // get max coordinate of board
  55. Point3f maxCoord = board.getRightBottomCorner();
  56. // copy objPoints
  57. vector<Point3f> objPoints = board.getObjPoints()[markerIndex];
  58. // move the marker to the origin
  59. for (size_t i = 0; i < objPoints.size(); i++)
  60. objPoints[i] -= (maxCoord / 2.f);
  61. projectPoints(objPoints, rvec, tvec, cameraMatrix, distCoeffs, corners);
  62. // get perspective transform
  63. vector<Point2f> originalCorners;
  64. originalCorners.push_back(Point2f(0, 0));
  65. originalCorners.push_back(Point2f((float)markerSizePixels, 0));
  66. originalCorners.push_back(Point2f((float)markerSizePixels, (float)markerSizePixels));
  67. originalCorners.push_back(Point2f(0, (float)markerSizePixels));
  68. Mat transformation = getPerspectiveTransform(originalCorners, corners);
  69. // apply transformation
  70. Mat aux;
  71. const char borderValue = 127;
  72. warpPerspective(markerImg, aux, transformation, img.size(), INTER_NEAREST, BORDER_CONSTANT,
  73. Scalar::all(borderValue));
  74. // copy only not-border pixels
  75. for (int y = 0; y < aux.rows; y++) {
  76. for (int x = 0; x < aux.cols; x++) {
  77. if (aux.at< unsigned char >(y, x) == borderValue) continue;
  78. img.at< unsigned char >(y, x) = aux.at< unsigned char >(y, x);
  79. }
  80. }
  81. }
  82. Mat projectBoard(const aruco::GridBoard& board, Mat cameraMatrix, double yaw, double pitch, double distance,
  83. Size imageSize, int markerBorder) {
  84. Mat rvec, tvec;
  85. getSyntheticRT(yaw, pitch, distance, rvec, tvec);
  86. Mat img = Mat(imageSize, CV_8UC1, Scalar::all(255));
  87. for (unsigned int index = 0; index < board.getIds().size(); index++)
  88. projectMarker(img, board, index, cameraMatrix, rvec, tvec, markerBorder);
  89. return img;
  90. }
  91. /** Check if a set of 3d points are enough for calibration. Z coordinate is ignored.
  92. * Only axis parallel lines are considered */
  93. static bool _arePointsEnoughForPoseEstimation(const std::vector<Point3f> &points) {
  94. if(points.size() < 4) return false;
  95. std::vector<double> sameXValue; // different x values in points
  96. std::vector<int> sameXCounter; // number of points with the x value in sameXValue
  97. for(unsigned int i = 0; i < points.size(); i++) {
  98. bool found = false;
  99. for(unsigned int j = 0; j < sameXValue.size(); j++) {
  100. if(sameXValue[j] == points[i].x) {
  101. found = true;
  102. sameXCounter[j]++;
  103. }
  104. }
  105. if(!found) {
  106. sameXValue.push_back(points[i].x);
  107. sameXCounter.push_back(1);
  108. }
  109. }
  110. // count how many x values has more than 2 points
  111. int moreThan2 = 0;
  112. for(unsigned int i = 0; i < sameXCounter.size(); i++) {
  113. if(sameXCounter[i] >= 2) moreThan2++;
  114. }
  115. // if we have more than 1 two xvalues with more than 2 points, calibration is ok
  116. if(moreThan2 > 1)
  117. return true;
  118. return false;
  119. }
  120. bool getCharucoBoardPose(InputArray charucoCorners, InputArray charucoIds, const aruco::CharucoBoard &board,
  121. InputArray cameraMatrix, InputArray distCoeffs, InputOutputArray rvec, InputOutputArray tvec,
  122. bool useExtrinsicGuess) {
  123. CV_Assert((charucoCorners.getMat().total() == charucoIds.getMat().total()));
  124. if(charucoIds.getMat().total() < 4) return false; // need, at least, 4 corners
  125. std::vector<Point3f> objPoints;
  126. objPoints.reserve(charucoIds.getMat().total());
  127. for(unsigned int i = 0; i < charucoIds.getMat().total(); i++) {
  128. int currId = charucoIds.getMat().at< int >(i);
  129. CV_Assert(currId >= 0 && currId < (int)board.getChessboardCorners().size());
  130. objPoints.push_back(board.getChessboardCorners()[currId]);
  131. }
  132. // points need to be in different lines, check if detected points are enough
  133. if(!_arePointsEnoughForPoseEstimation(objPoints)) return false;
  134. solvePnP(objPoints, charucoCorners, cameraMatrix, distCoeffs, rvec, tvec, useExtrinsicGuess);
  135. return true;
  136. }
  137. /**
  138. * @brief Return object points for the system centered in a middle (by default) or in a top left corner of single
  139. * marker, given the marker length
  140. */
  141. static Mat _getSingleMarkerObjectPoints(float markerLength, bool use_aruco_ccw_center) {
  142. CV_Assert(markerLength > 0);
  143. Mat objPoints(4, 1, CV_32FC3);
  144. // set coordinate system in the top-left corner of the marker, with Z pointing out
  145. if (use_aruco_ccw_center) {
  146. objPoints.ptr<Vec3f>(0)[0] = Vec3f(-markerLength/2.f, markerLength/2.f, 0);
  147. objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength/2.f, markerLength/2.f, 0);
  148. objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength/2.f, -markerLength/2.f, 0);
  149. objPoints.ptr<Vec3f>(0)[3] = Vec3f(-markerLength/2.f, -markerLength/2.f, 0);
  150. }
  151. else {
  152. objPoints.ptr<Vec3f>(0)[0] = Vec3f(0.f, 0.f, 0);
  153. objPoints.ptr<Vec3f>(0)[1] = Vec3f(markerLength, 0.f, 0);
  154. objPoints.ptr<Vec3f>(0)[2] = Vec3f(markerLength, markerLength, 0);
  155. objPoints.ptr<Vec3f>(0)[3] = Vec3f(0.f, markerLength, 0);
  156. }
  157. return objPoints;
  158. }
  159. void getMarkersPoses(InputArrayOfArrays corners, float markerLength, InputArray cameraMatrix, InputArray distCoeffs,
  160. OutputArray _rvecs, OutputArray _tvecs, OutputArray objPoints,
  161. bool use_aruco_ccw_center, SolvePnPMethod solvePnPMethod) {
  162. CV_Assert(markerLength > 0);
  163. Mat markerObjPoints = _getSingleMarkerObjectPoints(markerLength, use_aruco_ccw_center);
  164. int nMarkers = (int)corners.total();
  165. _rvecs.create(nMarkers, 1, CV_64FC3);
  166. _tvecs.create(nMarkers, 1, CV_64FC3);
  167. Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat();
  168. for (int i = 0; i < nMarkers; i++)
  169. solvePnP(markerObjPoints, corners.getMat(i), cameraMatrix, distCoeffs, rvecs.at<Vec3d>(i), tvecs.at<Vec3d>(i),
  170. false, solvePnPMethod);
  171. if(objPoints.needed())
  172. markerObjPoints.convertTo(objPoints, -1);
  173. }
  174. }