perf_aruco.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 "perf_precomp.hpp"
  5. #include "opencv2/calib3d.hpp"
  6. namespace opencv_test {
  7. using namespace perf;
  8. typedef tuple<bool, int> UseArucoParams;
  9. typedef TestBaseWithParam<UseArucoParams> EstimateAruco;
  10. #define ESTIMATE_PARAMS Combine(Values(false, true), Values(-1))
  11. static double deg2rad(double deg) { return deg * CV_PI / 180.; }
  12. class MarkerPainter
  13. {
  14. private:
  15. int imgMarkerSize = 0;
  16. Mat cameraMatrix;
  17. public:
  18. MarkerPainter(const int size) {
  19. setImgMarkerSize(size);
  20. }
  21. void setImgMarkerSize(const int size) {
  22. imgMarkerSize = size;
  23. cameraMatrix = Mat::eye(3, 3, CV_64FC1);
  24. cameraMatrix.at<double>(0, 0) = cameraMatrix.at<double>(1, 1) = imgMarkerSize;
  25. cameraMatrix.at<double>(0, 2) = imgMarkerSize / 2.0;
  26. cameraMatrix.at<double>(1, 2) = imgMarkerSize / 2.0;
  27. }
  28. static std::pair<Mat, Mat> getSyntheticRT(double yaw, double pitch, double distance) {
  29. auto rvec_tvec = std::make_pair(Mat(3, 1, CV_64FC1), Mat(3, 1, CV_64FC1));
  30. Mat& rvec = rvec_tvec.first;
  31. Mat& tvec = rvec_tvec.second;
  32. // Rvec
  33. // first put the Z axis aiming to -X (like the camera axis system)
  34. Mat rotZ(3, 1, CV_64FC1);
  35. rotZ.ptr<double>(0)[0] = 0;
  36. rotZ.ptr<double>(0)[1] = 0;
  37. rotZ.ptr<double>(0)[2] = -0.5 * CV_PI;
  38. Mat rotX(3, 1, CV_64FC1);
  39. rotX.ptr<double>(0)[0] = 0.5 * CV_PI;
  40. rotX.ptr<double>(0)[1] = 0;
  41. rotX.ptr<double>(0)[2] = 0;
  42. Mat camRvec, camTvec;
  43. composeRT(rotZ, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotX, Mat(3, 1, CV_64FC1, Scalar::all(0)),
  44. camRvec, camTvec);
  45. // now pitch and yaw angles
  46. Mat rotPitch(3, 1, CV_64FC1);
  47. rotPitch.ptr<double>(0)[0] = 0;
  48. rotPitch.ptr<double>(0)[1] = pitch;
  49. rotPitch.ptr<double>(0)[2] = 0;
  50. Mat rotYaw(3, 1, CV_64FC1);
  51. rotYaw.ptr<double>(0)[0] = yaw;
  52. rotYaw.ptr<double>(0)[1] = 0;
  53. rotYaw.ptr<double>(0)[2] = 0;
  54. composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw,
  55. Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec);
  56. // compose both rotations
  57. composeRT(camRvec, Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec,
  58. Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec);
  59. // Tvec, just move in z (camera) direction the specific distance
  60. tvec.ptr<double>(0)[0] = 0.;
  61. tvec.ptr<double>(0)[1] = 0.;
  62. tvec.ptr<double>(0)[2] = distance;
  63. return rvec_tvec;
  64. }
  65. std::pair<Mat, vector<Point2f> > getProjectMarker(int id, double yaw, double pitch,
  66. const aruco::DetectorParameters& parameters,
  67. const aruco::Dictionary& dictionary) {
  68. auto marker_corners = std::make_pair(Mat(imgMarkerSize, imgMarkerSize, CV_8UC1, Scalar::all(255)), vector<Point2f>());
  69. Mat& img = marker_corners.first;
  70. vector<Point2f>& corners = marker_corners.second;
  71. // canonical image
  72. const int markerSizePixels = static_cast<int>(imgMarkerSize/sqrt(2.f));
  73. aruco::generateImageMarker(dictionary, id, markerSizePixels, img, parameters.markerBorderBits);
  74. // get rvec and tvec for the perspective
  75. const double distance = 0.1;
  76. auto rvec_tvec = MarkerPainter::getSyntheticRT(yaw, pitch, distance);
  77. Mat& rvec = rvec_tvec.first;
  78. Mat& tvec = rvec_tvec.second;
  79. const float markerLength = 0.05f;
  80. vector<Point3f> markerObjPoints;
  81. markerObjPoints.emplace_back(Point3f(-markerLength / 2.f, +markerLength / 2.f, 0));
  82. markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(markerLength, 0, 0));
  83. markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(markerLength, -markerLength, 0));
  84. markerObjPoints.emplace_back(markerObjPoints[0] + Point3f(0, -markerLength, 0));
  85. // project markers and draw them
  86. Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
  87. projectPoints(markerObjPoints, rvec, tvec, cameraMatrix, distCoeffs, corners);
  88. vector<Point2f> originalCorners;
  89. originalCorners.emplace_back(Point2f(0.f, 0.f));
  90. originalCorners.emplace_back(originalCorners[0]+Point2f((float)markerSizePixels, 0));
  91. originalCorners.emplace_back(originalCorners[0]+Point2f((float)markerSizePixels, (float)markerSizePixels));
  92. originalCorners.emplace_back(originalCorners[0]+Point2f(0, (float)markerSizePixels));
  93. Mat transformation = getPerspectiveTransform(originalCorners, corners);
  94. warpPerspective(img, img, transformation, Size(imgMarkerSize, imgMarkerSize), INTER_NEAREST, BORDER_CONSTANT,
  95. Scalar::all(255));
  96. return marker_corners;
  97. }
  98. std::pair<Mat, map<int, vector<Point2f> > > getProjectMarkersTile(const int numMarkers,
  99. const aruco::DetectorParameters& params,
  100. const aruco::Dictionary& dictionary) {
  101. Mat tileImage(imgMarkerSize*numMarkers, imgMarkerSize*numMarkers, CV_8UC1, Scalar::all(255));
  102. map<int, vector<Point2f> > idCorners;
  103. int iter = 0, pitch = 0, yaw = 0;
  104. for (int i = 0; i < numMarkers; i++) {
  105. for (int j = 0; j < numMarkers; j++) {
  106. int currentId = iter;
  107. auto marker_corners = getProjectMarker(currentId, deg2rad(70+yaw), deg2rad(pitch), params, dictionary);
  108. Point2i startPoint(j*imgMarkerSize, i*imgMarkerSize);
  109. Mat tmp_roi = tileImage(Rect(startPoint.x, startPoint.y, imgMarkerSize, imgMarkerSize));
  110. marker_corners.first.copyTo(tmp_roi);
  111. for (Point2f& point: marker_corners.second)
  112. point += static_cast<Point2f>(startPoint);
  113. idCorners[currentId] = marker_corners.second;
  114. auto test = idCorners[currentId];
  115. yaw = (yaw + 10) % 51; // 70+yaw >= 70 && 70+yaw <= 120
  116. iter++;
  117. }
  118. pitch = (pitch + 60) % 360;
  119. }
  120. return std::make_pair(tileImage, idCorners);
  121. }
  122. };
  123. static inline double getMaxDistance(map<int, vector<Point2f> > &golds, const vector<int>& ids,
  124. const vector<vector<Point2f> >& corners) {
  125. std::map<int, double> mapDist;
  126. for (const auto& el : golds)
  127. mapDist[el.first] = std::numeric_limits<double>::max();
  128. for (size_t i = 0; i < ids.size(); i++) {
  129. int id = ids[i];
  130. const auto gold_corners = golds.find(id);
  131. if (gold_corners != golds.end()) {
  132. double distance = 0.;
  133. for (int c = 0; c < 4; c++)
  134. distance = std::max(distance, cv::norm(gold_corners->second[c] - corners[i][c]));
  135. mapDist[id] = distance;
  136. }
  137. }
  138. return std::max_element(std::begin(mapDist), std::end(mapDist),
  139. [](const pair<int, double>& p1, const pair<int, double>& p2){return p1.second < p2.second;})->second;
  140. }
  141. PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) {
  142. UseArucoParams testParams = GetParam();
  143. aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  144. aruco::DetectorParameters detectorParams;
  145. detectorParams.minDistanceToBorder = 1;
  146. detectorParams.markerBorderBits = 1;
  147. detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
  148. const int markerSize = 100;
  149. const int numMarkersInRow = 9;
  150. //USE_ARUCO3
  151. detectorParams.useAruco3Detection = get<0>(testParams);
  152. if (detectorParams.useAruco3Detection) {
  153. detectorParams.minSideLengthCanonicalImg = 32;
  154. detectorParams.minMarkerLengthRatioOriginalImg = 0.04f / numMarkersInRow;
  155. }
  156. aruco::ArucoDetector detector(dictionary, detectorParams);
  157. MarkerPainter painter(markerSize);
  158. auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary);
  159. // detect markers
  160. vector<vector<Point2f> > corners;
  161. vector<int> ids;
  162. TEST_CYCLE() {
  163. detector.detectMarkers(image_map.first, corners, ids);
  164. }
  165. ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast<int>(ids.size()));
  166. double maxDistance = getMaxDistance(image_map.second, ids, corners);
  167. ASSERT_LT(maxDistance, 3.);
  168. SANITY_CHECK_NOTHING();
  169. }
  170. PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) {
  171. UseArucoParams testParams = GetParam();
  172. aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  173. aruco::DetectorParameters detectorParams;
  174. detectorParams.minDistanceToBorder = 1;
  175. detectorParams.markerBorderBits = 1;
  176. detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
  177. //USE_ARUCO3
  178. detectorParams.useAruco3Detection = get<0>(testParams);
  179. if (detectorParams.useAruco3Detection) {
  180. detectorParams.minSideLengthCanonicalImg = 64;
  181. detectorParams.minMarkerLengthRatioOriginalImg = 0.f;
  182. }
  183. aruco::ArucoDetector detector(dictionary, detectorParams);
  184. const int markerSize = 200;
  185. const int numMarkersInRow = 11;
  186. MarkerPainter painter(markerSize);
  187. auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary);
  188. // detect markers
  189. vector<vector<Point2f> > corners;
  190. vector<int> ids;
  191. TEST_CYCLE() {
  192. detector.detectMarkers(image_map.first, corners, ids);
  193. }
  194. ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast<int>(ids.size()));
  195. double maxDistance = getMaxDistance(image_map.second, ids, corners);
  196. ASSERT_LT(maxDistance, 3.);
  197. SANITY_CHECK_NOTHING();
  198. }
  199. struct Aruco3Params {
  200. bool useAruco3Detection = false;
  201. float minMarkerLengthRatioOriginalImg = 0.f;
  202. int minSideLengthCanonicalImg = 0;
  203. Aruco3Params(bool useAruco3, float minMarkerLen, int minSideLen): useAruco3Detection(useAruco3),
  204. minMarkerLengthRatioOriginalImg(minMarkerLen),
  205. minSideLengthCanonicalImg(minSideLen) {}
  206. friend std::ostream& operator<<(std::ostream& os, const Aruco3Params& d) {
  207. os << d.useAruco3Detection << " " << d.minMarkerLengthRatioOriginalImg << " " << d.minSideLengthCanonicalImg;
  208. return os;
  209. }
  210. };
  211. typedef tuple<Aruco3Params, pair<int, int>> ArucoTestParams;
  212. typedef TestBaseWithParam<ArucoTestParams> EstimateLargeAruco;
  213. #define ESTIMATE_FHD_PARAMS Combine(Values(Aruco3Params(false, 0.f, 0), Aruco3Params(true, 0.f, 32), \
  214. Aruco3Params(true, 0.015f, 32), Aruco3Params(true, 0.f, 16), Aruco3Params(true, 0.0069f, 16)), \
  215. Values(std::make_pair(1440, 1), std::make_pair(480, 3), std::make_pair(144, 10)))
  216. PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) {
  217. ArucoTestParams testParams = GetParam();
  218. aruco::Dictionary dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  219. aruco::DetectorParameters detectorParams;
  220. detectorParams.minDistanceToBorder = 1;
  221. detectorParams.markerBorderBits = 1;
  222. detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
  223. //USE_ARUCO3
  224. detectorParams.useAruco3Detection = get<0>(testParams).useAruco3Detection;
  225. if (detectorParams.useAruco3Detection) {
  226. detectorParams.minSideLengthCanonicalImg = get<0>(testParams).minSideLengthCanonicalImg;
  227. detectorParams.minMarkerLengthRatioOriginalImg = get<0>(testParams).minMarkerLengthRatioOriginalImg;
  228. }
  229. aruco::ArucoDetector detector(dictionary, detectorParams);
  230. const int markerSize = get<1>(testParams).first; // 1440 or 480 or 144
  231. const int numMarkersInRow = get<1>(testParams).second; // 1 or 3 or 144
  232. MarkerPainter painter(markerSize); // num pixels is 1440x1440 as in FHD 1920x1080
  233. auto image_map = painter.getProjectMarkersTile(numMarkersInRow, detectorParams, dictionary);
  234. // detect markers
  235. vector<vector<Point2f> > corners;
  236. vector<int> ids;
  237. TEST_CYCLE()
  238. {
  239. detector.detectMarkers(image_map.first, corners, ids);
  240. }
  241. ASSERT_EQ(numMarkersInRow*numMarkersInRow, static_cast<int>(ids.size()));
  242. double maxDistance = getMaxDistance(image_map.second, ids, corners);
  243. ASSERT_LT(maxDistance, 3.);
  244. SANITY_CHECK_NOTHING();
  245. }
  246. }