test_arucodetection.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*
  2. By downloading, copying, installing or using the software you agree to this
  3. license. If you do not agree to this license, do not download, install,
  4. copy or use the software.
  5. License Agreement
  6. For Open Source Computer Vision Library
  7. (3-clause BSD License)
  8. Copyright (C) 2013, OpenCV Foundation, all rights reserved.
  9. Third party copyrights are property of their respective owners.
  10. Redistribution and use in source and binary forms, with or without modification,
  11. are permitted provided that the following conditions are met:
  12. * Redistributions of source code must retain the above copyright notice,
  13. this list of conditions and the following disclaimer.
  14. * Redistributions in binary form must reproduce the above copyright notice,
  15. this list of conditions and the following disclaimer in the documentation
  16. and/or other materials provided with the distribution.
  17. * Neither the names of the copyright holders nor the names of the contributors
  18. may be used to endorse or promote products derived from this software
  19. without specific prior written permission.
  20. This software is provided by the copyright holders and contributors "as is" and
  21. any express or implied warranties, including, but not limited to, the implied
  22. warranties of merchantability and fitness for a particular purpose are
  23. disclaimed. In no event shall copyright holders or contributors be liable for
  24. any direct, indirect, incidental, special, exemplary, or consequential damages
  25. (including, but not limited to, procurement of substitute goods or services;
  26. loss of use, data, or profits; or business interruption) however caused
  27. and on any theory of liability, whether in contract, strict liability,
  28. or tort (including negligence or otherwise) arising in any way out of
  29. the use of this software, even if advised of the possibility of such damage.
  30. */
  31. #include "test_precomp.hpp"
  32. #include <opencv2/core/utils/logger.defines.hpp>
  33. namespace opencv_test { namespace {
  34. /**
  35. * @brief Draw 2D synthetic markers and detect them
  36. */
  37. class CV_ArucoDetectionSimple : public cvtest::BaseTest {
  38. public:
  39. CV_ArucoDetectionSimple();
  40. protected:
  41. void run(int);
  42. };
  43. CV_ArucoDetectionSimple::CV_ArucoDetectionSimple() {}
  44. void CV_ArucoDetectionSimple::run(int) {
  45. Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  46. // 20 images
  47. for(int i = 0; i < 20; i++) {
  48. const int markerSidePixels = 100;
  49. int imageSize = markerSidePixels * 2 + 3 * (markerSidePixels / 2);
  50. // draw synthetic image and store marker corners and ids
  51. vector< vector< Point2f > > groundTruthCorners;
  52. vector< int > groundTruthIds;
  53. Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255));
  54. for(int y = 0; y < 2; y++) {
  55. for(int x = 0; x < 2; x++) {
  56. Mat marker;
  57. int id = i * 4 + y * 2 + x;
  58. aruco::drawMarker(dictionary, id, markerSidePixels, marker);
  59. Point2f firstCorner =
  60. Point2f(markerSidePixels / 2.f + x * (1.5f * markerSidePixels),
  61. markerSidePixels / 2.f + y * (1.5f * markerSidePixels));
  62. Mat aux = img.colRange((int)firstCorner.x, (int)firstCorner.x + markerSidePixels)
  63. .rowRange((int)firstCorner.y, (int)firstCorner.y + markerSidePixels);
  64. marker.copyTo(aux);
  65. groundTruthIds.push_back(id);
  66. groundTruthCorners.push_back(vector< Point2f >());
  67. groundTruthCorners.back().push_back(firstCorner);
  68. groundTruthCorners.back().push_back(firstCorner + Point2f(markerSidePixels - 1, 0));
  69. groundTruthCorners.back().push_back(
  70. firstCorner + Point2f(markerSidePixels - 1, markerSidePixels - 1));
  71. groundTruthCorners.back().push_back(firstCorner + Point2f(0, markerSidePixels - 1));
  72. }
  73. }
  74. if(i % 2 == 1) img.convertTo(img, CV_8UC3);
  75. // detect markers
  76. vector< vector< Point2f > > corners;
  77. vector< int > ids;
  78. Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
  79. aruco::detectMarkers(img, dictionary, corners, ids, params);
  80. // check detection results
  81. for(unsigned int m = 0; m < groundTruthIds.size(); m++) {
  82. int idx = -1;
  83. for(unsigned int k = 0; k < ids.size(); k++) {
  84. if(groundTruthIds[m] == ids[k]) {
  85. idx = (int)k;
  86. break;
  87. }
  88. }
  89. if(idx == -1) {
  90. ts->printf(cvtest::TS::LOG, "Marker not detected");
  91. ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
  92. return;
  93. }
  94. for(int c = 0; c < 4; c++) {
  95. double dist = cv::norm(groundTruthCorners[m][c] - corners[idx][c]); // TODO cvtest
  96. if(dist > 0.001) {
  97. ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
  98. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  99. return;
  100. }
  101. }
  102. }
  103. }
  104. }
  105. static double deg2rad(double deg) { return deg * CV_PI / 180.; }
  106. /**
  107. * @brief Get rvec and tvec from yaw, pitch and distance
  108. */
  109. static void getSyntheticRT(double yaw, double pitch, double distance, Mat &rvec, Mat &tvec) {
  110. rvec = Mat(3, 1, CV_64FC1);
  111. tvec = Mat(3, 1, CV_64FC1);
  112. // Rvec
  113. // first put the Z axis aiming to -X (like the camera axis system)
  114. Mat rotZ(3, 1, CV_64FC1);
  115. rotZ.ptr< double >(0)[0] = 0;
  116. rotZ.ptr< double >(0)[1] = 0;
  117. rotZ.ptr< double >(0)[2] = -0.5 * CV_PI;
  118. Mat rotX(3, 1, CV_64FC1);
  119. rotX.ptr< double >(0)[0] = 0.5 * CV_PI;
  120. rotX.ptr< double >(0)[1] = 0;
  121. rotX.ptr< double >(0)[2] = 0;
  122. Mat camRvec, camTvec;
  123. composeRT(rotZ, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotX, Mat(3, 1, CV_64FC1, Scalar::all(0)),
  124. camRvec, camTvec);
  125. // now pitch and yaw angles
  126. Mat rotPitch(3, 1, CV_64FC1);
  127. rotPitch.ptr< double >(0)[0] = 0;
  128. rotPitch.ptr< double >(0)[1] = pitch;
  129. rotPitch.ptr< double >(0)[2] = 0;
  130. Mat rotYaw(3, 1, CV_64FC1);
  131. rotYaw.ptr< double >(0)[0] = yaw;
  132. rotYaw.ptr< double >(0)[1] = 0;
  133. rotYaw.ptr< double >(0)[2] = 0;
  134. composeRT(rotPitch, Mat(3, 1, CV_64FC1, Scalar::all(0)), rotYaw,
  135. Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec);
  136. // compose both rotations
  137. composeRT(camRvec, Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec,
  138. Mat(3, 1, CV_64FC1, Scalar::all(0)), rvec, tvec);
  139. // Tvec, just move in z (camera) direction the specific distance
  140. tvec.ptr< double >(0)[0] = 0.;
  141. tvec.ptr< double >(0)[1] = 0.;
  142. tvec.ptr< double >(0)[2] = distance;
  143. }
  144. /**
  145. * @brief Create a synthetic image of a marker with perspective
  146. */
  147. static Mat projectMarker(Ptr<aruco::Dictionary> &dictionary, int id, Mat cameraMatrix, double yaw,
  148. double pitch, double distance, Size imageSize, int markerBorder,
  149. vector< Point2f > &corners, int encloseMarker=0) {
  150. // canonical image
  151. Mat marker, markerImg;
  152. const int markerSizePixels = 100;
  153. aruco::drawMarker(dictionary, id, markerSizePixels, marker, markerBorder);
  154. marker.copyTo(markerImg);
  155. if(encloseMarker){ //to enclose the marker
  156. int enclose = int(marker.rows/4);
  157. markerImg = Mat::zeros(marker.rows+(2*enclose), marker.cols+(enclose*2), CV_8UC1);
  158. Mat field= markerImg.rowRange(int(enclose), int(markerImg.rows-enclose))
  159. .colRange(int(0), int(markerImg.cols));
  160. field.setTo(255);
  161. field= markerImg.rowRange(int(0), int(markerImg.rows))
  162. .colRange(int(enclose), int(markerImg.cols-enclose));
  163. field.setTo(255);
  164. field = markerImg(Rect(enclose,enclose,marker.rows,marker.cols));
  165. marker.copyTo(field);
  166. }
  167. // get rvec and tvec for the perspective
  168. Mat rvec, tvec;
  169. getSyntheticRT(yaw, pitch, distance, rvec, tvec);
  170. const float markerLength = 0.05f;
  171. vector< Point3f > markerObjPoints;
  172. markerObjPoints.push_back(Point3f(-markerLength / 2.f, +markerLength / 2.f, 0));
  173. markerObjPoints.push_back(markerObjPoints[0] + Point3f(markerLength, 0, 0));
  174. markerObjPoints.push_back(markerObjPoints[0] + Point3f(markerLength, -markerLength, 0));
  175. markerObjPoints.push_back(markerObjPoints[0] + Point3f(0, -markerLength, 0));
  176. // project markers and draw them
  177. Mat distCoeffs(5, 1, CV_64FC1, Scalar::all(0));
  178. projectPoints(markerObjPoints, rvec, tvec, cameraMatrix, distCoeffs, corners);
  179. vector< Point2f > originalCorners;
  180. originalCorners.push_back(Point2f(0+float(encloseMarker*markerSizePixels/4), 0+float(encloseMarker*markerSizePixels/4)));
  181. originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, 0));
  182. originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, (float)markerSizePixels));
  183. originalCorners.push_back(originalCorners[0]+Point2f(0, (float)markerSizePixels));
  184. Mat transformation = getPerspectiveTransform(originalCorners, corners);
  185. Mat img(imageSize, CV_8UC1, Scalar::all(255));
  186. Mat aux;
  187. const char borderValue = 127;
  188. warpPerspective(markerImg, aux, transformation, imageSize, INTER_NEAREST, BORDER_CONSTANT,
  189. Scalar::all(borderValue));
  190. // copy only not-border pixels
  191. for(int y = 0; y < aux.rows; y++) {
  192. for(int x = 0; x < aux.cols; x++) {
  193. if(aux.at< unsigned char >(y, x) == borderValue) continue;
  194. img.at< unsigned char >(y, x) = aux.at< unsigned char >(y, x);
  195. }
  196. }
  197. return img;
  198. }
  199. enum class ArucoAlgParams
  200. {
  201. USE_DEFAULT = 0,
  202. USE_APRILTAG=1, /// Detect marker candidates :: using AprilTag
  203. DETECT_INVERTED_MARKER, /// Check if there is a white marker
  204. USE_ARUCO3 /// Check if aruco3 should be used
  205. };
  206. /**
  207. * @brief Draws markers in perspective and detect them
  208. */
  209. class CV_ArucoDetectionPerspective : public cvtest::BaseTest {
  210. public:
  211. CV_ArucoDetectionPerspective(ArucoAlgParams arucoAlgParam) : arucoAlgParams(arucoAlgParam) {}
  212. protected:
  213. void run(int);
  214. ArucoAlgParams arucoAlgParams;
  215. };
  216. void CV_ArucoDetectionPerspective::run(int) {
  217. int iter = 0;
  218. int szEnclosed = 0;
  219. Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
  220. Size imgSize(500, 500);
  221. cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650;
  222. cameraMatrix.at< double >(0, 2) = imgSize.width / 2;
  223. cameraMatrix.at< double >(1, 2) = imgSize.height / 2;
  224. Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  225. // detect from different positions
  226. for(double distance = 0.1; distance < 0.7; distance += 0.2) {
  227. for(int pitch = 0; pitch < 360; pitch += (distance == 0.1? 60:180)) {
  228. for(int yaw = 70; yaw <= 120; yaw += 40){
  229. int currentId = iter % 250;
  230. int markerBorder = iter % 2 + 1;
  231. iter++;
  232. vector< Point2f > groundTruthCorners;
  233. Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
  234. params->minDistanceToBorder = 1;
  235. params->markerBorderBits = markerBorder;
  236. /// create synthetic image
  237. Mat img=
  238. projectMarker(dictionary, currentId, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
  239. distance, imgSize, markerBorder, groundTruthCorners, szEnclosed);
  240. // marker :: Inverted
  241. if(ArucoAlgParams::DETECT_INVERTED_MARKER == arucoAlgParams){
  242. img = ~img;
  243. params->detectInvertedMarker = true;
  244. }
  245. if(ArucoAlgParams::USE_APRILTAG == arucoAlgParams){
  246. params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG;
  247. }
  248. if (ArucoAlgParams::USE_ARUCO3 == arucoAlgParams) {
  249. params->useAruco3Detection = true;
  250. params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
  251. }
  252. // detect markers
  253. vector< vector< Point2f > > corners;
  254. vector< int > ids;
  255. aruco::detectMarkers(img, dictionary, corners, ids, params);
  256. // check results
  257. if(ids.size() != 1 || (ids.size() == 1 && ids[0] != currentId)) {
  258. if(ids.size() != 1)
  259. ts->printf(cvtest::TS::LOG, "Incorrect number of detected markers");
  260. else
  261. ts->printf(cvtest::TS::LOG, "Incorrect marker id");
  262. ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
  263. return;
  264. }
  265. for(int c = 0; c < 4; c++) {
  266. double dist = cv::norm(groundTruthCorners[c] - corners[0][c]); // TODO cvtest
  267. if(dist > 5) {
  268. ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
  269. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  270. return;
  271. }
  272. }
  273. }
  274. }
  275. // change the state :: to detect an enclosed inverted marker
  276. if(ArucoAlgParams::DETECT_INVERTED_MARKER == arucoAlgParams && distance == 0.1){
  277. distance -= 0.1;
  278. szEnclosed++;
  279. }
  280. }
  281. }
  282. /**
  283. * @brief Check max and min size in marker detection parameters
  284. */
  285. class CV_ArucoDetectionMarkerSize : public cvtest::BaseTest {
  286. public:
  287. CV_ArucoDetectionMarkerSize();
  288. protected:
  289. void run(int);
  290. };
  291. CV_ArucoDetectionMarkerSize::CV_ArucoDetectionMarkerSize() {}
  292. void CV_ArucoDetectionMarkerSize::run(int) {
  293. Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  294. int markerSide = 20;
  295. int imageSize = 200;
  296. // 10 cases
  297. for(int i = 0; i < 10; i++) {
  298. Mat marker;
  299. int id = 10 + i * 20;
  300. // create synthetic image
  301. Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255));
  302. aruco::drawMarker(dictionary, id, markerSide, marker);
  303. Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide);
  304. marker.copyTo(aux);
  305. vector< vector< Point2f > > corners;
  306. vector< int > ids;
  307. Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
  308. // set a invalid minMarkerPerimeterRate
  309. params->minMarkerPerimeterRate = min(4., (4. * markerSide) / float(imageSize) + 0.1);
  310. aruco::detectMarkers(img, dictionary, corners, ids, params);
  311. if(corners.size() != 0) {
  312. ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::minMarkerPerimeterRate");
  313. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  314. return;
  315. }
  316. // set an valid minMarkerPerimeterRate
  317. params->minMarkerPerimeterRate = max(0., (4. * markerSide) / float(imageSize) - 0.1);
  318. aruco::detectMarkers(img, dictionary, corners, ids, params);
  319. if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) {
  320. ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::minMarkerPerimeterRate");
  321. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  322. return;
  323. }
  324. // set a invalid maxMarkerPerimeterRate
  325. params->maxMarkerPerimeterRate = min(4., (4. * markerSide) / float(imageSize) - 0.1);
  326. aruco::detectMarkers(img, dictionary, corners, ids, params);
  327. if(corners.size() != 0) {
  328. ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::maxMarkerPerimeterRate");
  329. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  330. return;
  331. }
  332. // set an valid maxMarkerPerimeterRate
  333. params->maxMarkerPerimeterRate = max(0., (4. * markerSide) / float(imageSize) + 0.1);
  334. aruco::detectMarkers(img, dictionary, corners, ids, params);
  335. if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) {
  336. ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::maxMarkerPerimeterRate");
  337. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  338. return;
  339. }
  340. }
  341. }
  342. /**
  343. * @brief Check error correction in marker bits
  344. */
  345. class CV_ArucoBitCorrection : public cvtest::BaseTest {
  346. public:
  347. CV_ArucoBitCorrection();
  348. protected:
  349. void run(int);
  350. };
  351. CV_ArucoBitCorrection::CV_ArucoBitCorrection() {}
  352. void CV_ArucoBitCorrection::run(int) {
  353. Ptr<aruco::Dictionary> _dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  354. aruco::Dictionary &dictionary = *_dictionary;
  355. aruco::Dictionary dictionary2 = *_dictionary;
  356. int markerSide = 50;
  357. int imageSize = 150;
  358. Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
  359. // 10 markers
  360. for(int l = 0; l < 10; l++) {
  361. Mat marker;
  362. int id = 10 + l * 20;
  363. Mat currentCodeBytes = dictionary.bytesList.rowRange(id, id + 1);
  364. // 5 valid cases
  365. for(int i = 0; i < 5; i++) {
  366. // how many bit errors (the error is low enough so it can be corrected)
  367. params->errorCorrectionRate = 0.2 + i * 0.1;
  368. int errors =
  369. (int)std::floor(dictionary.maxCorrectionBits * params->errorCorrectionRate - 1.);
  370. // create erroneous marker in currentCodeBits
  371. Mat currentCodeBits =
  372. aruco::Dictionary::getBitsFromByteList(currentCodeBytes, dictionary.markerSize);
  373. for(int e = 0; e < errors; e++) {
  374. currentCodeBits.ptr< unsigned char >()[2 * e] =
  375. !currentCodeBits.ptr< unsigned char >()[2 * e];
  376. }
  377. // add erroneous marker to dictionary2 in order to create the erroneous marker image
  378. Mat currentCodeBytesError = aruco::Dictionary::getByteListFromBits(currentCodeBits);
  379. currentCodeBytesError.copyTo(dictionary2.bytesList.rowRange(id, id + 1));
  380. Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255));
  381. dictionary2.drawMarker(id, markerSide, marker);
  382. Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide);
  383. marker.copyTo(aux);
  384. // try to detect using original dictionary
  385. vector< vector< Point2f > > corners;
  386. vector< int > ids;
  387. aruco::detectMarkers(img, _dictionary, corners, ids, params);
  388. if(corners.size() != 1 || (corners.size() == 1 && ids[0] != id)) {
  389. ts->printf(cvtest::TS::LOG, "Error in bit correction");
  390. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  391. return;
  392. }
  393. }
  394. // 5 invalid cases
  395. for(int i = 0; i < 5; i++) {
  396. // how many bit errors (the error is too high to be corrected)
  397. params->errorCorrectionRate = 0.2 + i * 0.1;
  398. int errors =
  399. (int)std::floor(dictionary.maxCorrectionBits * params->errorCorrectionRate + 1.);
  400. // create erroneous marker in currentCodeBits
  401. Mat currentCodeBits =
  402. aruco::Dictionary::getBitsFromByteList(currentCodeBytes, dictionary.markerSize);
  403. for(int e = 0; e < errors; e++) {
  404. currentCodeBits.ptr< unsigned char >()[2 * e] =
  405. !currentCodeBits.ptr< unsigned char >()[2 * e];
  406. }
  407. // dictionary3 is only composed by the modified marker (in its original form)
  408. Ptr<aruco::Dictionary> _dictionary3 = makePtr<aruco::Dictionary>(
  409. dictionary2.bytesList.rowRange(id, id + 1).clone(),
  410. dictionary.markerSize,
  411. dictionary.maxCorrectionBits);
  412. // add erroneous marker to dictionary2 in order to create the erroneous marker image
  413. Mat currentCodeBytesError = aruco::Dictionary::getByteListFromBits(currentCodeBits);
  414. currentCodeBytesError.copyTo(dictionary2.bytesList.rowRange(id, id + 1));
  415. Mat img = Mat(imageSize, imageSize, CV_8UC1, Scalar::all(255));
  416. dictionary2.drawMarker(id, markerSide, marker);
  417. Mat aux = img.colRange(30, 30 + markerSide).rowRange(50, 50 + markerSide);
  418. marker.copyTo(aux);
  419. // try to detect using dictionary3, it should fail
  420. vector< vector< Point2f > > corners;
  421. vector< int > ids;
  422. aruco::detectMarkers(img, _dictionary3, corners, ids, params);
  423. if(corners.size() != 0) {
  424. ts->printf(cvtest::TS::LOG, "Error in DetectorParameters::errorCorrectionRate");
  425. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
  426. return;
  427. }
  428. }
  429. }
  430. }
  431. typedef CV_ArucoDetectionPerspective CV_AprilTagDetectionPerspective;
  432. typedef CV_ArucoDetectionPerspective CV_InvertedArucoDetectionPerspective;
  433. typedef CV_ArucoDetectionPerspective CV_Aruco3DetectionPerspective;
  434. TEST(CV_InvertedArucoDetectionPerspective, algorithmic) {
  435. CV_InvertedArucoDetectionPerspective test(ArucoAlgParams::DETECT_INVERTED_MARKER);
  436. test.safe_run();
  437. }
  438. TEST(CV_AprilTagDetectionPerspective, algorithmic) {
  439. CV_AprilTagDetectionPerspective test(ArucoAlgParams::USE_APRILTAG);
  440. test.safe_run();
  441. }
  442. TEST(CV_Aruco3DetectionPerspective, algorithmic) {
  443. CV_Aruco3DetectionPerspective test(ArucoAlgParams::USE_ARUCO3);
  444. test.safe_run();
  445. }
  446. TEST(CV_ArucoDetectionSimple, algorithmic) {
  447. CV_ArucoDetectionSimple test;
  448. test.safe_run();
  449. }
  450. TEST(CV_ArucoDetectionPerspective, algorithmic) {
  451. CV_ArucoDetectionPerspective test(ArucoAlgParams::USE_DEFAULT);
  452. test.safe_run();
  453. }
  454. TEST(CV_ArucoDetectionMarkerSize, algorithmic) {
  455. CV_ArucoDetectionMarkerSize test;
  456. test.safe_run();
  457. }
  458. TEST(CV_ArucoBitCorrection, algorithmic) {
  459. CV_ArucoBitCorrection test;
  460. test.safe_run();
  461. }
  462. TEST(CV_ArucoTutorial, can_find_singlemarkersoriginal)
  463. {
  464. string img_path = cvtest::findDataFile("singlemarkersoriginal.jpg", false);
  465. Mat image = imread(img_path);
  466. Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
  467. Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();
  468. vector< int > ids;
  469. vector< vector< Point2f > > corners, rejected;
  470. const size_t N = 6ull;
  471. // corners of ArUco markers with indices goldCornersIds
  472. const int goldCorners[N][8] = { {359,310, 404,310, 410,350, 362,350}, {427,255, 469,256, 477,289, 434,288},
  473. {233,273, 190,273, 196,241, 237,241}, {298,185, 334,186, 335,212, 297,211},
  474. {425,163, 430,186, 394,186, 390,162}, {195,155, 230,155, 227,178, 190,178} };
  475. const int goldCornersIds[N] = { 40, 98, 62, 23, 124, 203};
  476. map<int, const int*> mapGoldCorners;
  477. for (size_t i = 0; i < N; i++)
  478. mapGoldCorners[goldCornersIds[i]] = goldCorners[i];
  479. aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
  480. ASSERT_EQ(N, ids.size());
  481. for (size_t i = 0; i < N; i++)
  482. {
  483. int arucoId = ids[i];
  484. ASSERT_EQ(4ull, corners[i].size());
  485. ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end());
  486. for (int j = 0; j < 4; j++)
  487. {
  488. EXPECT_NEAR(static_cast<float>(mapGoldCorners[arucoId][j * 2]), corners[i][j].x, 1.f);
  489. EXPECT_NEAR(static_cast<float>(mapGoldCorners[arucoId][j * 2 + 1]), corners[i][j].y, 1.f);
  490. }
  491. }
  492. }
  493. TEST(CV_ArucoTutorial, can_find_gboriginal)
  494. {
  495. string imgPath = cvtest::findDataFile("gboriginal.png", false);
  496. Mat image = imread(imgPath);
  497. string dictPath = cvtest::findDataFile("tutorial_dict.yml", false);
  498. cv::Ptr<cv::aruco::Dictionary> dictionary;
  499. FileStorage fs(dictPath, FileStorage::READ);
  500. aruco::Dictionary::readDictionary(fs.root(), dictionary); // set marker from tutorial_dict.yml
  501. Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();
  502. vector< int > ids;
  503. vector< vector< Point2f > > corners, rejected;
  504. const size_t N = 35ull;
  505. // corners of ArUco markers with indices 0, 1, ..., 34
  506. const int goldCorners[N][8] = { {252,74, 286,81, 274,102, 238,95}, {295,82, 330,89, 319,111, 282,104},
  507. {338,91, 375,99, 365,121, 327,113}, {383,100, 421,107, 412,130, 374,123},
  508. {429,109, 468,116, 461,139, 421,132}, {235,100, 270,108, 257,130, 220,122},
  509. {279,109, 316,117, 304,140, 266,133}, {324,119, 362,126, 352,150, 313,143},
  510. {371,128, 410,136, 400,161, 360,152}, {418,139, 459,145, 451,170, 410,163},
  511. {216,128, 253,136, 239,161, 200,152}, {262,138, 300,146, 287,172, 248,164},
  512. {309,148, 349,156, 337,183, 296,174}, {358,158, 398,167, 388,194, 346,185},
  513. {407,169, 449,176, 440,205, 397,196}, {196,158, 235,168, 218,195, 179,185},
  514. {243,170, 283,178, 269,206, 228,197}, {293,180, 334,190, 321,218, 279,209},
  515. {343,192, 385,200, 374,230, 330,220}, {395,203, 438,211, 429,241, 384,233},
  516. {174,192, 215,201, 197,231, 156,221}, {223,204, 265,213, 249,244, 207,234},
  517. {275,215, 317,225, 303,257, 259,246}, {327,227, 371,238, 359,270, 313,259},
  518. {381,240, 426,249, 416,282, 369,273}, {151,228, 193,238, 173,271, 130,260},
  519. {202,241, 245,251, 228,285, 183,274}, {255,254, 300,264, 284,299, 238,288},
  520. {310,267, 355,278, 342,314, 295,302}, {366,281, 413,290, 402,327, 353,317},
  521. {125,267, 168,278, 147,314, 102,303}, {178,281, 223,293, 204,330, 157,317},
  522. {233,296, 280,307, 263,346, 214,333}, {291,310, 338,322, 323,363, 274,349},
  523. {349,325, 399,336, 386,378, 335,366} };
  524. map<int, const int*> mapGoldCorners;
  525. for (int i = 0; i < static_cast<int>(N); i++)
  526. mapGoldCorners[i] = goldCorners[i];
  527. aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
  528. ASSERT_EQ(N, ids.size());
  529. for (size_t i = 0; i < N; i++)
  530. {
  531. int arucoId = ids[i];
  532. ASSERT_EQ(4ull, corners[i].size());
  533. ASSERT_TRUE(mapGoldCorners.find(arucoId) != mapGoldCorners.end());
  534. for (int j = 0; j < 4; j++)
  535. {
  536. EXPECT_NEAR(static_cast<float>(mapGoldCorners[arucoId][j*2]), corners[i][j].x, 1.f);
  537. EXPECT_NEAR(static_cast<float>(mapGoldCorners[arucoId][j*2+1]), corners[i][j].y, 1.f);
  538. }
  539. }
  540. }
  541. }} // namespace