test_convhull.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342
  1. /*M///////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. //
  5. // By downloading, copying, installing or using the software you agree to this license.
  6. // If you do not agree to this license, do not download, install,
  7. // copy or use the software.
  8. //
  9. //
  10. // Intel License Agreement
  11. // For Open Source Computer Vision Library
  12. //
  13. // Copyright (C) 2000, Intel Corporation, all rights reserved.
  14. // Third party copyrights are property of their respective owners.
  15. //
  16. // Redistribution and use in source and binary forms, with or without modification,
  17. // are permitted provided that the following conditions are met:
  18. //
  19. // * Redistribution's of source code must retain the above copyright notice,
  20. // this list of conditions and the following disclaimer.
  21. //
  22. // * Redistribution's in binary form must reproduce the above copyright notice,
  23. // this list of conditions and the following disclaimer in the documentation
  24. // and/or other materials provided with the distribution.
  25. //
  26. // * The name of Intel Corporation may not be used to endorse or promote products
  27. // derived from this software without specific prior written permission.
  28. //
  29. // This software is provided by the copyright holders and contributors "as is" and
  30. // any express or implied warranties, including, but not limited to, the implied
  31. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  32. // In no event shall the Intel Corporation or contributors be liable for any direct,
  33. // indirect, incidental, special, exemplary, or consequential damages
  34. // (including, but not limited to, procurement of substitute goods or services;
  35. // loss of use, data, or profits; or business interruption) however caused
  36. // and on any theory of liability, whether in contract, strict liability,
  37. // or tort (including negligence or otherwise) arising in any way out of
  38. // the use of this software, even if advised of the possibility of such damage.
  39. //
  40. //M*/
  41. #include "opencv2/core/hal/interface.h"
  42. #include "opencv2/ts.hpp"
  43. #include "opencv2/ts/cuda_test.hpp"
  44. #include "test_precomp.hpp"
  45. namespace opencv_test { namespace {
  46. /****************************************************************************************\
  47. * minEnclosingCircle Test 3 *
  48. \****************************************************************************************/
  49. TEST(minEnclosingCircle, basic_test)
  50. {
  51. const float EPS = 1.0e-3f;
  52. Point2f center;
  53. float radius;
  54. {
  55. const vector<Point2f> pts = { {5, 10} };
  56. minEnclosingCircle(pts, center, radius);
  57. EXPECT_NEAR(center.x, 5, EPS);
  58. EXPECT_NEAR(center.y, 10, EPS);
  59. EXPECT_NEAR(radius, 0, EPS);
  60. }
  61. {
  62. const vector<Point2f> pts = { {5, 10}, {11, 18} };
  63. minEnclosingCircle(pts, center, radius);
  64. EXPECT_NEAR(center.x, 8, EPS);
  65. EXPECT_NEAR(center.y, 14, EPS);
  66. EXPECT_NEAR(radius, 5, EPS);
  67. }
  68. // pts[2] is within the circle with diameter pts[0] - pts[1].
  69. // 2
  70. // 0 1
  71. // NB: The triangle is obtuse, so the only pts[0] and pts[1] are on the circle.
  72. {
  73. const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 1} };
  74. minEnclosingCircle(pts, center, radius);
  75. EXPECT_NEAR(center.x, 5, EPS);
  76. EXPECT_NEAR(center.y, 0, EPS);
  77. EXPECT_NEAR(5, radius, EPS);
  78. }
  79. // pts[2] is on the circle with diameter pts[0] - pts[1].
  80. // 2
  81. // 0 1
  82. {
  83. const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 5} };
  84. minEnclosingCircle(pts, center, radius);
  85. EXPECT_NEAR(center.x, 5, EPS);
  86. EXPECT_NEAR(center.y, 0, EPS);
  87. EXPECT_NEAR(5, radius, EPS);
  88. }
  89. // pts[2] is outside the circle with diameter pts[0] - pts[1].
  90. // 2
  91. //
  92. //
  93. // 0 1
  94. // NB: The triangle is acute, so all 3 points are on the circle.
  95. {
  96. const vector<Point2f> pts = { {0, 0}, {10, 0}, {5, 10} };
  97. minEnclosingCircle(pts, center, radius);
  98. EXPECT_NEAR(center.x, 5, EPS);
  99. EXPECT_NEAR(center.y, 3.75, EPS);
  100. EXPECT_NEAR(6.25f, radius, EPS);
  101. }
  102. // The 3 points are colinear.
  103. {
  104. const vector<Point2f> pts = { {0, 0}, {10, 0}, {3, 0} };
  105. minEnclosingCircle(pts, center, radius);
  106. EXPECT_NEAR(center.x, 5, EPS);
  107. EXPECT_NEAR(center.y, 0, EPS);
  108. EXPECT_NEAR(5, radius, EPS);
  109. }
  110. // 2 points are the same.
  111. {
  112. const vector<Point2f> pts = { {0, 0}, {10, 0}, {10, 0} };
  113. minEnclosingCircle(pts, center, radius);
  114. EXPECT_NEAR(center.x, 5, EPS);
  115. EXPECT_NEAR(center.y, 0, EPS);
  116. EXPECT_NEAR(5, radius, EPS);
  117. }
  118. // 3 points are the same.
  119. {
  120. const vector<Point2f> pts = { {10, 0}, {10, 0}, {10, 0} };
  121. minEnclosingCircle(pts, center, radius);
  122. EXPECT_NEAR(center.x, 10, EPS);
  123. EXPECT_NEAR(center.y, 0, EPS);
  124. EXPECT_NEAR(0, radius, EPS);
  125. }
  126. }
  127. TEST(Imgproc_minEnclosingCircle, regression_16051) {
  128. vector<Point2f> pts;
  129. pts.push_back(Point2f(85, 1415));
  130. pts.push_back(Point2f(87, 1415));
  131. pts.push_back(Point2f(89, 1414));
  132. pts.push_back(Point2f(89, 1414));
  133. pts.push_back(Point2f(87, 1412));
  134. Point2f center;
  135. float radius;
  136. minEnclosingCircle(pts, center, radius);
  137. EXPECT_NEAR(center.x, 86.9f, 1e-3);
  138. EXPECT_NEAR(center.y, 1414.1f, 1e-3);
  139. EXPECT_NEAR(2.1024551f, radius, 1e-3);
  140. }
  141. TEST(Imgproc_minEnclosingCircle, regression_27891) {
  142. {
  143. const vector<Point2f> pts = { {219, 301}, {639, 635}, {740, 569}, {740, 569}, {309, 123}, {349, 88} };
  144. Point2f center;
  145. float radius;
  146. minEnclosingCircle(pts, center, radius);
  147. EXPECT_NEAR(center.x, 522.476f, 1e-3f);
  148. EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
  149. EXPECT_NEAR(radius, 311.2331f, 1e-3f);
  150. }
  151. {
  152. const vector<Point2f> pts = { {219, 301}, {639, 635}, {740, 569}, {740, 569}, {349, 88} };
  153. Point2f center;
  154. float radius;
  155. minEnclosingCircle(pts, center, radius);
  156. EXPECT_NEAR(center.x, 522.476f, 1e-3f);
  157. EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
  158. EXPECT_NEAR(radius, 311.2331f, 1e-3f);
  159. }
  160. {
  161. const vector<Point2f> pts = { {639, 635}, {740, 569}, {740, 569}, {349, 88} };
  162. Point2f center;
  163. float radius;
  164. minEnclosingCircle(pts, center, radius);
  165. EXPECT_NEAR(center.x, 522.476f, 1e-3f);
  166. EXPECT_NEAR(center.y, 346.4029f, 1e-3f);
  167. EXPECT_NEAR(radius, 311.2331f, 1e-3f);
  168. }
  169. }
  170. PARAM_TEST_CASE(ConvexityDefects_regression_5908, bool, int)
  171. {
  172. public:
  173. int start_index;
  174. bool clockwise;
  175. Mat contour;
  176. virtual void SetUp()
  177. {
  178. clockwise = GET_PARAM(0);
  179. start_index = GET_PARAM(1);
  180. const int N = 11;
  181. const Point2i points[N] = {
  182. Point2i(154, 408),
  183. Point2i(45, 223),
  184. Point2i(115, 275), // inner
  185. Point2i(104, 166),
  186. Point2i(154, 256), // inner
  187. Point2i(169, 144),
  188. Point2i(185, 256), // inner
  189. Point2i(235, 170),
  190. Point2i(240, 320), // inner
  191. Point2i(330, 287),
  192. Point2i(224, 390)
  193. };
  194. contour = Mat(N, 1, CV_32SC2);
  195. for (int i = 0; i < N; i++)
  196. {
  197. contour.at<Point2i>(i) = (!clockwise) // image and convexHull coordinate systems are different
  198. ? points[(start_index + i) % N]
  199. : points[N - 1 - ((start_index + i) % N)];
  200. }
  201. }
  202. };
  203. TEST_P(ConvexityDefects_regression_5908, simple)
  204. {
  205. std::vector<int> hull;
  206. cv::convexHull(contour, hull, clockwise, false);
  207. std::vector<Vec4i> result;
  208. cv::convexityDefects(contour, hull, result);
  209. EXPECT_EQ(4, (int)result.size());
  210. }
  211. INSTANTIATE_TEST_CASE_P(Imgproc, ConvexityDefects_regression_5908,
  212. testing::Combine(
  213. testing::Bool(),
  214. testing::Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  215. ));
  216. TEST(Imgproc_FitLine, regression_15083)
  217. {
  218. int points2i_[] = {
  219. 432, 654,
  220. 370, 656,
  221. 390, 656,
  222. 410, 656,
  223. 348, 658
  224. };
  225. Mat points(5, 1, CV_32SC2, points2i_);
  226. Vec4f lineParam;
  227. fitLine(points, lineParam, DIST_L1, 0, 0.01, 0.01);
  228. EXPECT_GE(fabs(lineParam[0]), fabs(lineParam[1]) * 4) << lineParam;
  229. }
  230. TEST(Imgproc_FitLine, regression_4903)
  231. {
  232. float points2f_[] = {
  233. 1224.0, 576.0,
  234. 1234.0, 683.0,
  235. 1215.0, 471.0,
  236. 1184.0, 137.0,
  237. 1079.0, 377.0,
  238. 1239.0, 788.0,
  239. };
  240. Mat points(6, 1, CV_32FC2, points2f_);
  241. Vec4f lineParam;
  242. fitLine(points, lineParam, DIST_WELSCH, 0, 0.01, 0.01);
  243. EXPECT_GE(fabs(lineParam[1]), fabs(lineParam[0]) * 4) << lineParam;
  244. }
  245. #if 0
  246. #define DRAW(x) x
  247. #else
  248. #define DRAW(x)
  249. #endif
  250. // the Python test by @hannarud is converted to C++; see the issue #4539
  251. TEST(Imgproc_ConvexityDefects, ordering_4539)
  252. {
  253. int contour[][2] =
  254. {
  255. {26, 9}, {25, 10}, {24, 10}, {23, 10}, {22, 10}, {21, 10}, {20, 11}, {19, 11}, {18, 11}, {17, 12},
  256. {17, 13}, {18, 14}, {18, 15}, {18, 16}, {18, 17}, {19, 18}, {19, 19}, {20, 20}, {21, 21}, {21, 22},
  257. {22, 23}, {22, 24}, {23, 25}, {23, 26}, {24, 27}, {25, 28}, {26, 29}, {27, 30}, {27, 31}, {28, 32},
  258. {29, 32}, {30, 33}, {31, 34}, {30, 35}, {29, 35}, {30, 35}, {31, 34}, {32, 34}, {33, 34}, {34, 33},
  259. {35, 32}, {35, 31}, {35, 30}, {36, 29}, {37, 28}, {37, 27}, {38, 26}, {39, 25}, {40, 24}, {40, 23},
  260. {41, 22}, {42, 21}, {42, 20}, {42, 19}, {43, 18}, {43, 17}, {44, 16}, {45, 15}, {45, 14}, {46, 13},
  261. {46, 12}, {45, 11}, {44, 11}, {43, 11}, {42, 10}, {41, 10}, {40, 9}, {39, 9}, {38, 9}, {37, 9},
  262. {36, 9}, {35, 9}, {34, 9}, {33, 9}, {32, 9}, {31, 9}, {30, 9}, {29, 9}, {28, 9}, {27, 9}
  263. };
  264. int npoints = (int)(sizeof(contour)/sizeof(contour[0][0])/2);
  265. Mat contour_(1, npoints, CV_32SC2, contour);
  266. vector<Point> hull;
  267. vector<int> hull_ind;
  268. vector<Vec4i> defects;
  269. #if 0 // deprecated behavior
  270. // first, check the original contour as-is, without intermediate fillPoly/drawContours.
  271. convexHull(contour_, hull_ind, false, false);
  272. EXPECT_THROW( convexityDefects(contour_, hull_ind, defects), cv::Exception );
  273. #endif
  274. int scale = 20;
  275. contour_ *= (double)scale;
  276. Mat canvas_gray(Size(60*scale, 45*scale), CV_8U, Scalar::all(0));
  277. const Point* ptptr = contour_.ptr<Point>();
  278. fillPoly(canvas_gray, &ptptr, &npoints, 1, Scalar(255, 255, 255));
  279. vector<vector<Point> > contours;
  280. findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
  281. convexHull(contours[0], hull_ind, false, false);
  282. #if 0 // deprecated behavior
  283. // the original contour contains self-intersections,
  284. // therefore convexHull does not return a monotonous sequence of points
  285. // and therefore convexityDefects throws an exception
  286. EXPECT_THROW( convexityDefects(contours[0], hull_ind, defects), cv::Exception );
  287. #endif
  288. #if 1
  289. // one way to eliminate the contour self-intersection in this particular case is to apply dilate(),
  290. // so that the self-repeating points are not self-repeating anymore
  291. dilate(canvas_gray, canvas_gray, Mat());
  292. #else
  293. // another popular technique to eliminate such thin "hair" is to use morphological "close" operation,
  294. // which is erode() + dilate()
  295. erode(canvas_gray, canvas_gray, Mat());
  296. dilate(canvas_gray, canvas_gray, Mat());
  297. #endif
  298. // after the "fix", the newly retrieved contour should not have self-intersections,
  299. // and everything should work well
  300. findContours(canvas_gray, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
  301. convexHull(contours[0], hull, false, true);
  302. convexHull(contours[0], hull_ind, false, false);
  303. DRAW(Mat canvas(Size(60*scale, 45*scale), CV_8UC3, Scalar::all(0));
  304. drawContours(canvas, contours, -1, Scalar(255, 255, 255), -1));
  305. size_t nhull = hull.size();
  306. ASSERT_EQ( nhull, hull_ind.size() );
  307. if( nhull > 2 )
  308. {
  309. bool initial_lt = hull_ind[0] < hull_ind[1];
  310. for( size_t i = 0; i < nhull; i++ )
  311. {
  312. int ind = hull_ind[i];
  313. Point pt = contours[0][ind];
  314. ASSERT_EQ(pt, hull[i]);
  315. if( i > 0 )
  316. {
  317. // check that the convex hull indices are monotone
  318. if( initial_lt )
  319. {
  320. ASSERT_LT(hull_ind[i-1], hull_ind[i]);
  321. }
  322. else
  323. {
  324. ASSERT_GT(hull_ind[i-1], hull_ind[i]);
  325. }
  326. }
  327. DRAW(circle(canvas, pt, 7, Scalar(180, 0, 180), -1, LINE_AA);
  328. putText(canvas, format("%d (%d)", (int)i, ind), pt+Point(15, 0), FONT_HERSHEY_SIMPLEX, 0.4, Scalar(200, 0, 200), 1, LINE_AA));
  329. //printf("%d. ind=%d, pt=(%d, %d)\n", (int)i, ind, pt.x, pt.y);
  330. }
  331. }
  332. convexityDefects(contours[0], hull_ind, defects);
  333. for(size_t i = 0; i < defects.size(); i++ )
  334. {
  335. Vec4i d = defects[i];
  336. //printf("defect %d. start=%d, end=%d, farthest=%d, depth=%d\n", (int)i, d[0], d[1], d[2], d[3]);
  337. EXPECT_LT(d[0], d[1]);
  338. EXPECT_LE(d[0], d[2]);
  339. EXPECT_LE(d[2], d[1]);
  340. DRAW(Point start = contours[0][d[0]];
  341. Point end = contours[0][d[1]];
  342. Point far = contours[0][d[2]];
  343. line(canvas, start, end, Scalar(255, 255, 128), 3, LINE_AA);
  344. line(canvas, start, far, Scalar(255, 150, 255), 3, LINE_AA);
  345. line(canvas, end, far, Scalar(255, 150, 255), 3, LINE_AA);
  346. circle(canvas, start, 7, Scalar(0, 0, 255), -1, LINE_AA);
  347. circle(canvas, end, 7, Scalar(0, 0, 255), -1, LINE_AA);
  348. circle(canvas, far, 7, Scalar(255, 0, 0), -1, LINE_AA));
  349. }
  350. DRAW(imshow("defects", canvas);
  351. waitKey());
  352. }
  353. #undef DRAW
  354. TEST(Imgproc_ConvexHull, overflow)
  355. {
  356. std::vector<Point> points;
  357. std::vector<Point2f> pointsf;
  358. points.push_back(Point(14763, 2890));
  359. points.push_back(Point(14388, 72088));
  360. points.push_back(Point(62810, 72274));
  361. points.push_back(Point(63166, 3945));
  362. points.push_back(Point(56782, 3945));
  363. points.push_back(Point(56763, 3077));
  364. points.push_back(Point(34666, 2965));
  365. points.push_back(Point(34547, 2953));
  366. points.push_back(Point(34508, 2866));
  367. points.push_back(Point(34429, 2965));
  368. size_t i, n = points.size();
  369. for( i = 0; i < n; i++ )
  370. pointsf.push_back(Point2f(points[i]));
  371. std::vector<int> hull;
  372. std::vector<int> hullf;
  373. convexHull(points, hull, false, false);
  374. convexHull(pointsf, hullf, false, false);
  375. ASSERT_EQ(hull, hullf);
  376. }
  377. static
  378. bool checkMinAreaRect(const RotatedRect& rr, const Mat& c, double eps = 0.5f)
  379. {
  380. int N = c.rows;
  381. Mat rr_pts;
  382. boxPoints(rr, rr_pts);
  383. double maxError = 0.0;
  384. int nfailed = 0;
  385. for (int i = 0; i < N; i++)
  386. {
  387. double d = pointPolygonTest(rr_pts, c.at<Point2f>(i), true);
  388. maxError = std::max(-d, maxError);
  389. if (d < -eps)
  390. nfailed++;
  391. }
  392. if (nfailed)
  393. std::cout << "nfailed=" << nfailed << " (total=" << N << ") maxError=" << maxError << std::endl;
  394. return nfailed == 0;
  395. }
  396. TEST(Imgproc_minAreaRect, reproducer_18157)
  397. {
  398. const int N = 168;
  399. float pts_[N][2] = {
  400. { 1903, 266 }, { 1897, 267 }, { 1893, 268 }, { 1890, 269 },
  401. { 1878, 275 }, { 1875, 277 }, { 1872, 279 }, { 1868, 282 },
  402. { 1862, 287 }, { 1750, 400 }, { 1748, 402 }, { 1742, 407 },
  403. { 1742, 408 }, { 1740, 410 }, { 1738, 412 }, { 1593, 558 },
  404. { 1590, 560 }, { 1588, 562 }, { 1586, 564 }, { 1580, 570 },
  405. { 1443, 709 }, { 1437, 714 }, { 1435, 716 }, { 1304, 848 },
  406. { 1302, 850 }, { 1292, 860 }, { 1175, 979 }, { 1172, 981 },
  407. { 1049, 1105 }, { 936, 1220 }, { 933, 1222 }, { 931, 1224 },
  408. { 830, 1326 }, { 774, 1383 }, { 769, 1389 }, { 766, 1393 },
  409. { 764, 1396 }, { 762, 1399 }, { 760, 1402 }, { 757, 1408 },
  410. { 757, 1410 }, { 755, 1413 }, { 754, 1416 }, { 753, 1420 },
  411. { 752, 1424 }, { 752, 1442 }, { 753, 1447 }, { 754, 1451 },
  412. { 755, 1454 }, { 757, 1457 }, { 757, 1459 }, { 761, 1467 },
  413. { 763, 1470 }, { 765, 1473 }, { 767, 1476 }, { 771, 1481 },
  414. { 779, 1490 }, { 798, 1510 }, { 843, 1556 }, { 847, 1560 },
  415. { 851, 1564 }, { 863, 1575 }, { 907, 1620 }, { 909, 1622 },
  416. { 913, 1626 }, { 1154, 1866 }, { 1156, 1868 }, { 1158, 1870 },
  417. { 1207, 1918 }, { 1238, 1948 }, { 1252, 1961 }, { 1260, 1968 },
  418. { 1264, 1971 }, { 1268, 1974 }, { 1271, 1975 }, { 1273, 1977 },
  419. { 1283, 1982 }, { 1286, 1983 }, { 1289, 1984 }, { 1294, 1985 },
  420. { 1300, 1986 }, { 1310, 1986 }, { 1316, 1985 }, { 1320, 1984 },
  421. { 1323, 1983 }, { 1326, 1982 }, { 1338, 1976 }, { 1341, 1974 },
  422. { 1344, 1972 }, { 1349, 1968 }, { 1358, 1960 }, { 1406, 1911 },
  423. { 1421, 1897 }, { 1624, 1693 }, { 1788, 1528 }, { 1790, 1526 },
  424. { 1792, 1524 }, { 1794, 1522 }, { 1796, 1520 }, { 1798, 1518 },
  425. { 1800, 1516 }, { 1919, 1396 }, { 1921, 1394 }, { 2038, 1275 },
  426. { 2047, 1267 }, { 2048, 1265 }, { 2145, 1168 }, { 2148, 1165 },
  427. { 2260, 1052 }, { 2359, 952 }, { 2434, 876 }, { 2446, 863 },
  428. { 2450, 858 }, { 2453, 854 }, { 2455, 851 }, { 2457, 846 },
  429. { 2459, 844 }, { 2460, 842 }, { 2460, 840 }, { 2462, 837 },
  430. { 2463, 834 }, { 2464, 830 }, { 2465, 825 }, { 2465, 809 },
  431. { 2464, 804 }, { 2463, 800 }, { 2462, 797 }, { 2461, 794 },
  432. { 2456, 784 }, { 2454, 781 }, { 2452, 778 }, { 2450, 775 },
  433. { 2446, 770 }, { 2437, 760 }, { 2412, 734 }, { 2410, 732 },
  434. { 2408, 730 }, { 2382, 704 }, { 2380, 702 }, { 2378, 700 },
  435. { 2376, 698 }, { 2372, 694 }, { 2370, 692 }, { 2368, 690 },
  436. { 2366, 688 }, { 2362, 684 }, { 2360, 682 }, { 2252, 576 },
  437. { 2250, 573 }, { 2168, 492 }, { 2166, 490 }, { 2085, 410 },
  438. { 2026, 352 }, { 1988, 315 }, { 1968, 296 }, { 1958, 287 },
  439. { 1953, 283 }, { 1949, 280 }, { 1946, 278 }, { 1943, 276 },
  440. { 1940, 274 }, { 1936, 272 }, { 1934, 272 }, { 1931, 270 },
  441. { 1928, 269 }, { 1925, 268 }, { 1921, 267 }, { 1915, 266 }
  442. };
  443. Mat contour(N, 1, CV_32FC2, (void*)pts_);
  444. RotatedRect rr = cv::minAreaRect(contour);
  445. EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
  446. }
  447. TEST(Imgproc_minAreaRect, reproducer_19769_lightweight)
  448. {
  449. const int N = 23;
  450. float pts_[N][2] = {
  451. {1325, 732}, {1248, 808}, {582, 1510}, {586, 1524},
  452. {595, 1541}, {599, 1547}, {789, 1745}, {829, 1786},
  453. {997, 1958}, {1116, 2074}, {1207, 2066}, {1216, 2058},
  454. {1231, 2044}, {1265, 2011}, {2036, 1254}, {2100, 1191},
  455. {2169, 1123}, {2315, 979}, {2395, 900}, {2438, 787},
  456. {2434, 782}, {2416, 762}, {2266, 610}
  457. };
  458. Mat contour(N, 1, CV_32FC2, (void*)pts_);
  459. RotatedRect rr = cv::minAreaRect(contour);
  460. EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
  461. }
  462. TEST(Imgproc_minAreaRect, reproducer_19769)
  463. {
  464. const int N = 169;
  465. float pts_[N][2] = {
  466. {1854, 227}, {1850, 228}, {1847, 229}, {1835, 235},
  467. {1832, 237}, {1829, 239}, {1825, 242}, {1818, 248},
  468. {1807, 258}, {1759, 306}, {1712, 351}, {1708, 356},
  469. {1658, 404}, {1655, 408}, {1602, 459}, {1599, 463},
  470. {1542, 518}, {1477, 582}, {1402, 656}, {1325, 732},
  471. {1248, 808}, {1161, 894}, {1157, 898}, {1155, 900},
  472. {1068, 986}, {1060, 995}, {1058, 997}, {957, 1097},
  473. {956, 1097}, {814, 1238}, {810, 1242}, {805, 1248},
  474. {610, 1442}, {603, 1450}, {599, 1455}, {596, 1459},
  475. {594, 1462}, {592, 1465}, {590, 1470}, {588, 1472},
  476. {586, 1476}, {586, 1478}, {584, 1481}, {583, 1485},
  477. {582, 1490}, {582, 1510}, {583, 1515}, {584, 1518},
  478. {585, 1521}, {586, 1524}, {593, 1538}, {595, 1541},
  479. {597, 1544}, {599, 1547}, {603, 1552}, {609, 1559},
  480. {623, 1574}, {645, 1597}, {677, 1630}, {713, 1667},
  481. {753, 1707}, {789, 1744}, {789, 1745}, {829, 1786},
  482. {871, 1828}, {909, 1867}, {909, 1868}, {950, 1910},
  483. {953, 1912}, {997, 1958}, {1047, 2009}, {1094, 2056},
  484. {1105, 2066}, {1110, 2070}, {1113, 2072}, {1116, 2074},
  485. {1119, 2076}, {1122, 2077}, {1124, 2079}, {1130, 2082},
  486. {1133, 2083}, {1136, 2084}, {1139, 2085}, {1142, 2086},
  487. {1148, 2087}, {1166, 2087}, {1170, 2086}, {1174, 2085},
  488. {1177, 2084}, {1180, 2083}, {1188, 2079}, {1190, 2077},
  489. {1193, 2076}, {1196, 2074}, {1199, 2072}, {1202, 2070},
  490. {1207, 2066}, {1216, 2058}, {1231, 2044}, {1265, 2011},
  491. {1314, 1962}, {1360, 1917}, {1361, 1917}, {1408, 1871},
  492. {1457, 1822}, {1508, 1773}, {1512, 1768}, {1560, 1722},
  493. {1617, 1665}, {1671, 1613}, {1730, 1554}, {1784, 1502},
  494. {1786, 1500}, {1787, 1498}, {1846, 1440}, {1850, 1437},
  495. {1908, 1380}, {1974, 1314}, {2034, 1256}, {2036, 1254},
  496. {2100, 1191}, {2169, 1123}, {2242, 1051}, {2315, 979},
  497. {2395, 900}, {2426, 869}, {2435, 859}, {2438, 855},
  498. {2440, 852}, {2442, 849}, {2443, 846}, {2445, 844},
  499. {2446, 842}, {2446, 840}, {2448, 837}, {2449, 834},
  500. {2450, 829}, {2450, 814}, {2449, 809}, {2448, 806},
  501. {2447, 803}, {2442, 793}, {2440, 790}, {2438, 787},
  502. {2434, 782}, {2428, 775}, {2416, 762}, {2411, 758},
  503. {2342, 688}, {2340, 686}, {2338, 684}, {2266, 610},
  504. {2260, 605}, {2170, 513}, {2075, 417}, {2073, 415},
  505. {2069, 412}, {1955, 297}, {1955, 296}, {1913, 254},
  506. {1904, 246}, {1897, 240}, {1894, 238}, {1891, 236},
  507. {1888, 234}, {1880, 230}, {1877, 229}, {1874, 228},
  508. {1870, 227}
  509. };
  510. Mat contour(N, 1, CV_32FC2, (void*)pts_);
  511. RotatedRect rr = cv::minAreaRect(contour);
  512. EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
  513. }
  514. TEST(Imgproc_minEnclosingTriangle, regression_17585)
  515. {
  516. const int N = 3;
  517. float pts_[N][2] = { {0, 0}, {0, 1}, {1, 1} };
  518. cv::Mat points(N, 2, CV_32FC1, static_cast<void*>(pts_));
  519. vector<Point2f> triangle;
  520. EXPECT_NO_THROW(minEnclosingTriangle(points, triangle));
  521. }
  522. TEST(Imgproc_minEnclosingTriangle, regression_20890)
  523. {
  524. vector<Point> points;
  525. points.push_back(Point(0, 0));
  526. points.push_back(Point(0, 1));
  527. points.push_back(Point(1, 1));
  528. vector<Point2f> triangle;
  529. EXPECT_NO_THROW(minEnclosingTriangle(points, triangle));
  530. }
  531. TEST(Imgproc_minEnclosingTriangle, regression_mat_with_diff_channels)
  532. {
  533. const int N = 3;
  534. float pts_[N][2] = { {0, 0}, {0, 1}, {1, 1} };
  535. cv::Mat points1xN(1, N, CV_32FC2, static_cast<void*>(pts_));
  536. cv::Mat pointsNx1(N, 1, CV_32FC2, static_cast<void*>(pts_));
  537. vector<Point2f> triangle;
  538. EXPECT_NO_THROW(minEnclosingTriangle(points1xN, triangle));
  539. EXPECT_NO_THROW(minEnclosingTriangle(pointsNx1, triangle));
  540. }
  541. //==============================================================================
  542. typedef testing::TestWithParam<tuple<int, int>> fitLine_Modes;
  543. TEST_P(fitLine_Modes, accuracy)
  544. {
  545. const int data_type = get<0>(GetParam());
  546. const int dist_type = get<1>(GetParam());
  547. const int CN = CV_MAT_CN(data_type);
  548. const int res_type = CV_32FC(CN);
  549. for (int ITER = 0; ITER < 20; ++ITER)
  550. {
  551. SCOPED_TRACE(cv::format("iteration %d", ITER));
  552. Mat v0(1, 1, data_type), v1(1, 1, data_type); // pt = v0 + v1 * t
  553. Mat v1n;
  554. RNG& rng = TS::ptr()->get_rng();
  555. cvtest::randUni(rng, v0, Scalar::all(1), Scalar::all(100));
  556. cvtest::randUni(rng, v1, Scalar::all(1), Scalar::all(100));
  557. normalize(v1, v1n, 1, 0, NORM_L2, res_type);
  558. v0.convertTo(v0, res_type);
  559. v1.convertTo(v1, res_type);
  560. const int NUM = rng.uniform(30, 100);
  561. Mat points(NUM, 1, data_type, Scalar::all(0));
  562. for (int i = 0; i < NUM; ++i)
  563. {
  564. Mat pt = v0 + v1 * i;
  565. if (CV_MAT_DEPTH(data_type) == CV_32F)
  566. {
  567. Mat noise = cvtest::randomMat(rng, Size(1, 1), res_type, -0.01, 0.01, false);
  568. pt += noise;
  569. }
  570. pt.copyTo(points.row(i));
  571. }
  572. Mat line_;
  573. cv::fitLine(points, line_, dist_type, 0, 0.1, 0.01);
  574. Mat line = line_.reshape(points.channels(), 1);
  575. // check result type and size
  576. EXPECT_EQ(res_type, line.type());
  577. EXPECT_EQ(Size(2, 1), line.size());
  578. // check result pt1
  579. const double angle = line.col(0).dot(v1n);
  580. EXPECT_NEAR(abs(angle), 1, 1e-2);
  581. // put result pt0 to the original equation (pt = v0 + v1 * t) and find "t"
  582. Mat diff = line.col(1) - v0;
  583. cv::divide(diff, v1, diff);
  584. cv::divide(diff, diff.at<float>(0, 0), diff);
  585. const Mat unit(1, 1, res_type, Scalar::all(1));
  586. EXPECT_NEAR(cvtest::norm(diff, unit, NORM_L1), 0, 0.01);
  587. }
  588. }
  589. INSTANTIATE_TEST_CASE_P(/**/,
  590. fitLine_Modes,
  591. testing::Combine(
  592. testing::Values(CV_32FC2, CV_32FC3, CV_32SC2, CV_32SC3),
  593. testing::Values(DIST_L1, DIST_L2, DIST_L12, DIST_FAIR, DIST_WELSCH, DIST_HUBER)));
  594. //==============================================================================
  595. inline float normAngle(float angle_deg)
  596. {
  597. while (angle_deg < 0.f)
  598. angle_deg += 180.f;
  599. while (angle_deg > 180.f)
  600. angle_deg -= 180.f;
  601. if (abs(angle_deg - 180.f) < 0.01) // border case
  602. angle_deg = 0.f;
  603. return angle_deg;
  604. }
  605. inline float angleToDeg(float angle_rad)
  606. {
  607. return angle_rad * 180.f / (float)M_PI;
  608. }
  609. inline float angleDiff(float a, float b)
  610. {
  611. float res = a - b;
  612. return normAngle(res);
  613. }
  614. typedef testing::TestWithParam<int> fitEllipse_Modes;
  615. TEST_P(fitEllipse_Modes, accuracy)
  616. {
  617. const int data_type = GetParam();
  618. const float int_scale = 1000.f;
  619. const Size sz(1, 2);
  620. const Matx22f rot {0.f, -1.f, 1.f, 0.f};
  621. RNG& rng = TS::ptr()->get_rng();
  622. for (int ITER = 0; ITER < 20; ++ITER)
  623. {
  624. SCOPED_TRACE(cv::format("iteration %d", ITER));
  625. Mat f0(sz, CV_32FC1), f1(sz, CV_32FC1), f2(sz, CV_32FC1);
  626. cvtest::randUni(rng, f0, Scalar::all(-100), Scalar::all(100));
  627. cvtest::randUni(rng, f1, Scalar::all(-100), Scalar::all(100));
  628. if (ITER % 4 == 0)
  629. {
  630. // 0/90 degrees case
  631. f1.at<float>(0, 0) = 0.;
  632. }
  633. // f2 is orthogonal to f1 and scaled
  634. f2 = rot * f1 * cvtest::randomDouble(0.01, 3);
  635. const Point2f ref_center(f0.at<float>(0), f0.at<float>(1));
  636. const Size2f ref_size(
  637. (float)cvtest::norm(f1, NORM_L2) * 2.f,
  638. (float)cvtest::norm(f2, NORM_L2) * 2.f);
  639. const float ref_angle1 = angleToDeg(atan(f1.at<float>(1) / f1.at<float>(0)));
  640. const float ref_angle2 = angleToDeg(atan(f2.at<float>(1) / f2.at<float>(0)));
  641. const int NUM = rng.uniform(10, 30);
  642. Mat points(NUM, 1, data_type, Scalar::all(0));
  643. for (int i = 0; i < NUM; ++i)
  644. {
  645. Mat pt = f0 + f1 * sin(i) + f2 * cos(i);
  646. pt = pt.reshape(2);
  647. if (data_type == CV_32SC2)
  648. {
  649. pt.convertTo(points.row(i), CV_32SC2, int_scale);
  650. }
  651. else if (data_type == CV_32FC2)
  652. {
  653. pt.copyTo(points.row(i));
  654. }
  655. else
  656. {
  657. FAIL() << "unsupported data type: " << data_type;
  658. }
  659. }
  660. RotatedRect res = cv::fitEllipse(points);
  661. if (data_type == CV_32SC2)
  662. {
  663. res.center /= int_scale;
  664. res.size = Size2f(res.size.width / int_scale, res.size.height / int_scale);
  665. }
  666. const bool sizeSwap = (res.size.width < res.size.height) != (ref_size.width < ref_size.height);
  667. if (sizeSwap)
  668. {
  669. std::swap(res.size.width, res.size.height);
  670. }
  671. EXPECT_FALSE(res.size.empty());
  672. EXPECT_POINT2_NEAR(res.center, ref_center, 0.01);
  673. const float sizeDiff = (data_type == CV_32FC2) ? 0.1f : 1.f;
  674. EXPECT_NEAR(min(res.size.width, res.size.height), min(ref_size.width, ref_size.height), sizeDiff);
  675. EXPECT_NEAR(max(res.size.width, res.size.height), max(ref_size.width, ref_size.height), sizeDiff);
  676. if (sizeSwap)
  677. {
  678. EXPECT_LE(angleDiff(ref_angle2, res.angle), 0.1);
  679. }
  680. else
  681. {
  682. EXPECT_LE(angleDiff(ref_angle1, res.angle), 0.1);
  683. }
  684. }
  685. }
  686. INSTANTIATE_TEST_CASE_P(/**/,
  687. fitEllipse_Modes,
  688. testing::Values(CV_32FC2, CV_32SC2));
  689. //==============================================================================
  690. TEST(fitEllipse, small)
  691. {
  692. Size sz(50, 50);
  693. vector<vector<Point> > c;
  694. c.push_back(vector<Point>());
  695. int scale = 1;
  696. Point ofs = Point(0,0);//sz.width/2, sz.height/2) - Point(4,4)*scale;
  697. c[0].push_back(Point(2, 0)*scale+ofs);
  698. c[0].push_back(Point(0, 2)*scale+ofs);
  699. c[0].push_back(Point(0, 6)*scale+ofs);
  700. c[0].push_back(Point(2, 8)*scale+ofs);
  701. c[0].push_back(Point(6, 8)*scale+ofs);
  702. c[0].push_back(Point(8, 6)*scale+ofs);
  703. c[0].push_back(Point(8, 2)*scale+ofs);
  704. c[0].push_back(Point(6, 0)*scale+ofs);
  705. RotatedRect e = cv::fitEllipse(c[0]);
  706. EXPECT_NEAR(e.center.x, 4, 1.f);
  707. EXPECT_NEAR(e.center.y, 4, 1.f);
  708. EXPECT_NEAR(e.size.width, 9, 1.);
  709. EXPECT_NEAR(e.size.height, 9, 1.f);
  710. }
  711. //==============================================================================
  712. // points stored in rows
  713. inline static int findPointInMat(const Mat & data, const Mat & point)
  714. {
  715. for (int i = 0; i < data.rows; ++i)
  716. if (cvtest::norm(data.row(i), point, NORM_L1) == 0)
  717. return i;
  718. return -1;
  719. }
  720. // > 0 - "pt" is to the right of AB
  721. // < 0 - "pt" is to the left of AB
  722. // points stored in rows
  723. inline static double getSide(const Mat & ptA, const Mat & ptB, const Mat & pt)
  724. {
  725. Mat d0 = pt - ptA, d1 = ptB - pt, prod;
  726. vconcat(d0, d1, prod);
  727. prod = prod.reshape(1);
  728. if (prod.depth() == CV_32S)
  729. prod.convertTo(prod, CV_32F);
  730. return determinant(prod);
  731. }
  732. typedef testing::TestWithParam<perf::MatDepth> convexHull_Modes;
  733. TEST_P(convexHull_Modes, accuracy)
  734. {
  735. const int data_type = CV_MAKE_TYPE(GetParam(), 2);
  736. RNG & rng = TS::ptr()->get_rng();
  737. for (int ITER = 0; ITER < 20; ++ITER)
  738. {
  739. SCOPED_TRACE(cv::format("iteration %d", ITER));
  740. const int NUM = cvtest::randomInt(5, 100);
  741. Mat points(NUM, 1, data_type, Scalar::all(0));
  742. cvtest::randUni(rng, points, Scalar(-10), Scalar::all(10));
  743. Mat hull, c_hull, indexes;
  744. cv::convexHull(points, hull, false, true); // default parameters
  745. cv::convexHull(points, c_hull, true, true); // counter-clockwise
  746. cv::convexHull(points, indexes, false, false); // point indexes
  747. ASSERT_EQ(hull.size().width, 1);
  748. ASSERT_GE(hull.size().height, 3);
  749. ASSERT_EQ(hull.size(), c_hull.size());
  750. ASSERT_EQ(hull.size(), indexes.size());
  751. // find shift between hull and counter-clockwise hull
  752. const int c_diff = findPointInMat(hull, c_hull.row(0));
  753. ASSERT_NE(c_diff, -1);
  754. const int sz = (int)hull.total();
  755. for (int i = 0; i < sz; ++i)
  756. {
  757. SCOPED_TRACE(cv::format("vertex %d", i));
  758. Mat prev = (i == 0) ? hull.row(sz - 1) : hull.row(i - 1);
  759. Mat cur = hull.row(i);
  760. Mat next = (i != sz - 1) ? hull.row(i + 1) : hull.row(0);
  761. // 1. "cur' is one of points
  762. EXPECT_NE(findPointInMat(points, cur), -1);
  763. // 2. convexity: "cur" is on right side of "prev - next" edge
  764. EXPECT_GE(getSide(prev, next, cur), 0);
  765. // 3. all points are inside polygon - on the left side of "cur - next" edge
  766. for (int j = 0; j < points.rows; ++j)
  767. {
  768. SCOPED_TRACE(cv::format("point %d", j));
  769. EXPECT_LE(getSide(cur, next, points.row(j)), 0);
  770. }
  771. // check counter-clockwise hull
  772. const int c_idx = (sz - i + c_diff) % sz;
  773. Mat c_cur = c_hull.row(c_idx);
  774. EXPECT_MAT_NEAR(cur, c_cur, 0);
  775. // check indexed hull
  776. const int pt_index = indexes.at<int>(i);
  777. EXPECT_MAT_NEAR(cur, points.row(pt_index), 0);
  778. }
  779. }
  780. }
  781. INSTANTIATE_TEST_CASE_P(/**/,
  782. convexHull_Modes,
  783. testing::Values(CV_32F, CV_32S));
  784. //==============================================================================
  785. typedef testing::TestWithParam<perf::MatDepth> minAreaRect_Modes;
  786. TEST_P(minAreaRect_Modes, accuracy)
  787. {
  788. const int data_type = CV_MAKE_TYPE(GetParam(), 2);
  789. RNG & rng = TS::ptr()->get_rng();
  790. for (int ITER = 0; ITER < 20; ++ITER)
  791. {
  792. SCOPED_TRACE(cv::format("iteration %d", ITER));
  793. const int NUM = cvtest::randomInt(5, 100);
  794. Mat points(NUM, 1, data_type, Scalar::all(0));
  795. cvtest::randUni(rng, points, Scalar(-10), Scalar::all(10));
  796. const RotatedRect res = cv::minAreaRect(points);
  797. Point2f box_pts[4] {};
  798. res.points(box_pts);
  799. // check that the box contains all the points - all on one side
  800. double common_side = 0.;
  801. bool edgeHasPoint[4] {0};
  802. for (int i = 0; i < 4; ++i)
  803. {
  804. const int j = (i == 3) ? 0 : i + 1;
  805. Mat cur(1, 1, CV_32FC2, box_pts + i);
  806. Mat next(1, 1, CV_32FC2, box_pts + j);
  807. for (int k = 0; k < points.rows; ++k)
  808. {
  809. SCOPED_TRACE(cv::format("point %d", j));
  810. Mat one_point;
  811. points.row(k).convertTo(one_point, CV_32FC2);
  812. const double side = getSide(cur, next, one_point);
  813. if (abs(side) < 0.01) // point on edge - no need to check
  814. {
  815. edgeHasPoint[i] = true;
  816. continue;
  817. }
  818. if (common_side == 0.) // initial state
  819. {
  820. common_side = side > 0 ? 1. : -1.; // only sign matters
  821. }
  822. else
  823. {
  824. EXPECT_EQ(common_side > 0, side > 0) << common_side << ", " << side;
  825. }
  826. }
  827. }
  828. EXPECT_TRUE(edgeHasPoint[0] && edgeHasPoint[1] && edgeHasPoint[2] && edgeHasPoint[3]);
  829. }
  830. }
  831. INSTANTIATE_TEST_CASE_P(/**/,
  832. minAreaRect_Modes,
  833. testing::Values(CV_32F, CV_32S));
  834. //==============================================================================
  835. // true if "point" is on one of hull's edges
  836. inline static bool isPointOnHull(const Mat &hull, const Mat &point, const double thresh = 0.01)
  837. {
  838. const int sz = hull.rows;
  839. for (int k = 0; k < sz; ++k)
  840. {
  841. const double side = getSide(hull.row(k), hull.row(k == sz - 1 ? 0 : k + 1), point);
  842. if (abs(side) < thresh)
  843. return true;
  844. }
  845. return false;
  846. }
  847. // true if one of hull's edges touches "A-B"
  848. inline static bool isEdgeOnHull(const Mat &hull, const Mat &ptA, const Mat &ptB, const double thresh = 0.01)
  849. {
  850. const int sz = hull.rows;
  851. double prev_side = getSide(ptA, ptB, hull.row(sz - 1));
  852. for (int k = 0; k < sz; ++k)
  853. {
  854. Mat cur = hull.row(k);
  855. const double cur_side = getSide(ptA, ptB, cur);
  856. if (abs(prev_side) < thresh && abs(cur_side) < thresh)
  857. return true;
  858. prev_side = cur_side;
  859. }
  860. return false;
  861. }
  862. typedef testing::TestWithParam<perf::MatDepth> minEnclosingTriangle_Modes;
  863. TEST_P(minEnclosingTriangle_Modes, accuracy)
  864. {
  865. const int data_type = CV_MAKETYPE(GetParam(), 2);
  866. RNG & rng = TS::ptr()->get_rng();
  867. for (int ITER = 0; ITER < 20; ++ITER)
  868. {
  869. SCOPED_TRACE(cv::format("iteration %d", ITER));
  870. const int NUM = cvtest::randomInt(5, 100);
  871. Mat points(NUM, 1, data_type, Scalar::all(0));
  872. cvtest::randUni(rng, points, Scalar::all(-100), Scalar::all(100));
  873. Mat triangle;
  874. const double area = cv::minEnclosingTriangle(points, triangle);
  875. ASSERT_GT(area, 0.0001);
  876. ASSERT_EQ(triangle.type(), CV_32FC2);
  877. triangle = triangle.reshape(2, 1);
  878. ASSERT_EQ(triangle.size(), Size(3, 1));
  879. Mat hull;
  880. cv::convexHull(points, hull);
  881. hull.convertTo(hull, CV_32FC2);
  882. // check that all points are enclosed by triangle sides
  883. double commonSide = 0.;
  884. bool hasEdgeOnHull = false;
  885. for (int i = 0; i < 3; ++i)
  886. {
  887. SCOPED_TRACE(cv::format("edge %d", i));
  888. const int j = (i == 2) ? 0 : i + 1;
  889. Mat cur = triangle.col(i);
  890. Mat next = triangle.col(j);
  891. for (int k = 0; k < points.rows; ++k)
  892. {
  893. SCOPED_TRACE(cv::format("point %d", k));
  894. Mat pt;
  895. points.row(k).convertTo(pt, CV_32FC2);
  896. const double side = getSide(cur, next, pt);
  897. if (abs(side) < 0.01) // point on edge - no need to check
  898. continue;
  899. if (commonSide == 0.f) // initial state
  900. {
  901. commonSide = side > 0 ? 1.f : -1.f; // only sign matters
  902. }
  903. else
  904. {
  905. // either on the same side or close to zero
  906. EXPECT_EQ(commonSide > 0, side > 0) << commonSide << ", side=" << side;
  907. }
  908. }
  909. // triangle mid-points must be on the hull edges
  910. const Mat midPoint = (cur + next) / 2;
  911. EXPECT_TRUE(isPointOnHull(hull, midPoint));
  912. // at least one of hull edges must be on triangle edge
  913. hasEdgeOnHull = hasEdgeOnHull || isEdgeOnHull(hull, cur, next);
  914. }
  915. EXPECT_TRUE(hasEdgeOnHull);
  916. }
  917. }
  918. INSTANTIATE_TEST_CASE_P(/**/,
  919. minEnclosingTriangle_Modes,
  920. testing::Values(CV_32F, CV_32S));
  921. //==============================================================================
  922. typedef testing::TestWithParam<perf::MatDepth> minEnclosingCircle_Modes;
  923. TEST_P(minEnclosingCircle_Modes, accuracy)
  924. {
  925. const int data_type = CV_MAKETYPE(GetParam(), 2);
  926. RNG & rng = TS::ptr()->get_rng();
  927. for (int ITER = 0; ITER < 20; ++ITER)
  928. {
  929. SCOPED_TRACE(cv::format("iteration %d", ITER));
  930. const int NUM = cvtest::randomInt(5, 100);
  931. Mat points(NUM, 1, data_type, Scalar::all(0)), fpoints;
  932. cvtest::randUni(rng, points, Scalar::all(-100), Scalar::all(100));
  933. points.convertTo(fpoints, CV_32FC2);
  934. Point2f center {};
  935. float radius = 0.f;
  936. cv::minEnclosingCircle(points, center, radius);
  937. vector<int> boundPts; // indexes
  938. for (int i = 0; i < NUM; ++i)
  939. {
  940. Point2f pt = fpoints.at<Point2f>(i);
  941. const double dist = cv::norm(pt - center);
  942. EXPECT_LE(dist, radius);
  943. if (abs(dist - radius) < 0.01)
  944. boundPts.push_back(i);
  945. }
  946. // 2 points on diameter or at least 3 points on circle
  947. EXPECT_GE(boundPts.size(), 2llu);
  948. // 2 points on diameter
  949. if (boundPts.size() == 2llu)
  950. {
  951. const Point2f diff = fpoints.at<Point2f>(boundPts[0]) - fpoints.at<Point2f>(boundPts[1]);
  952. EXPECT_NEAR(cv::norm(diff), 2 * radius, 0.001);
  953. }
  954. }
  955. }
  956. INSTANTIATE_TEST_CASE_P(/**/,
  957. minEnclosingCircle_Modes,
  958. testing::Values(CV_32F, CV_32S));
  959. //==============================================================================
  960. TEST(minEnclosingCircle, three_points)
  961. {
  962. RNG & rng = TS::ptr()->get_rng();
  963. Point2f center = Point2f(rng.uniform(0.0f, 1000.0f), rng.uniform(0.0f, 1000.0f));;
  964. float radius = rng.uniform(0.0f, 500.0f);
  965. float angle = (float)rng.uniform(0.0f, (float)(CV_2PI));
  966. vector<Point2f> pts;
  967. pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));
  968. angle += (float)CV_PI;
  969. pts.push_back(center + Point2f(radius * cos(angle), radius * sin(angle)));
  970. float radius2 = radius * radius;
  971. float x = rng.uniform(center.x - radius, center.x + radius);
  972. float deltaX = x - center.x;
  973. float upperBoundY = sqrt(radius2 - deltaX * deltaX);
  974. float y = rng.uniform(center.y - upperBoundY, center.y + upperBoundY);
  975. pts.push_back(Point2f(x, y));
  976. // Find the minimum area enclosing circle
  977. Point2f calcCenter;
  978. float calcRadius;
  979. cv::minEnclosingCircle(pts, calcCenter, calcRadius);
  980. const float delta = (float)cv::norm(calcCenter - center) + abs(calcRadius - radius);
  981. EXPECT_LE(delta, 1.f);
  982. }
  983. //============================ minEnclosingPolygon tests ============================
  984. TEST(minEnclosingPolygon, input_errors)
  985. {
  986. std::vector<cv::Point2f> kgon;
  987. std::vector<cv::Point2f> ngon {{0.0, 0.0}, {1.0, 1.0}};
  988. EXPECT_THROW(minEnclosingConvexPolygon(ngon, kgon, 3), cv::Exception);
  989. ngon = {{0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0}, {1.0, 1.0}};
  990. EXPECT_THROW(minEnclosingConvexPolygon(ngon, kgon, 2), cv::Exception);
  991. EXPECT_THROW(minEnclosingConvexPolygon(ngon, kgon, 5), cv::Exception);
  992. }
  993. TEST(minEnclosingPolygon, input_corner_cases)
  994. {
  995. double area = -1.0;
  996. std::vector<cv::Point2f> kgon;
  997. std::vector<cv::Point2f> ngon = {{0.0, 0.0}, {0.0, 0.0}, {1.0, 1.0}, {1.0, 1.0}};
  998. EXPECT_NO_THROW(area = minEnclosingConvexPolygon(ngon, kgon, 3))
  999. << "unexpected exception: not enough different points in input ngon (double points)";
  1000. EXPECT_LE(area, 0.);
  1001. EXPECT_TRUE(kgon.empty());
  1002. ngon = {{0.0, 0.0}, {1.0, 1.0}, {2.0, 2.0}, {3.0, 3.0}, {4.0, 4.0}};
  1003. EXPECT_NO_THROW(area = minEnclosingConvexPolygon(ngon, kgon, 3))
  1004. << "unexpected exception: all points on line";
  1005. EXPECT_LE(area, 0.);
  1006. EXPECT_TRUE(kgon.empty());
  1007. }
  1008. TEST(minEnclosingPolygon, unit_circle)
  1009. {
  1010. const int n = 64;
  1011. const int k = 7;
  1012. double area = -1.0;
  1013. std::vector<cv::Point2f> kgon;
  1014. std::vector<cv::Point2f> ngon(n);
  1015. for(int i = 0; i < n; i++)
  1016. {
  1017. ngon[i] = { cosf(float(i * 2.f * M_PI / n)), sinf(float(i * 2.f * M_PI / n)) };
  1018. }
  1019. EXPECT_NO_THROW(area = minEnclosingConvexPolygon(ngon, kgon, k));
  1020. EXPECT_GT(area, cv::contourArea(ngon));
  1021. EXPECT_EQ((int)kgon.size(), k);
  1022. }
  1023. TEST(minEnclosingPolygon, random_points)
  1024. {
  1025. const int n = 100;
  1026. const int k = 7;
  1027. double area = -1.0;
  1028. std::vector<cv::Point2f> kgon;
  1029. std::vector<cv::Point2f> ngon(n);
  1030. std::vector<cv::Point2f> ngonHull;
  1031. cv::randu(ngon, 1, 101);
  1032. cv::convexHull(ngon, ngonHull, true);
  1033. EXPECT_NO_THROW(area = minEnclosingConvexPolygon(ngon, kgon, k));
  1034. EXPECT_GT(area, cv::contourArea(ngonHull));
  1035. EXPECT_EQ(kgon.size(), (size_t)k);
  1036. }
  1037. TEST(minEnclosingPolygon, pentagon)
  1038. {
  1039. double area;
  1040. std::vector<cv::Point2f> kgon;
  1041. std::vector<cv::Point2f> expectedKgon;
  1042. std::vector<cv::Point2f> ngon;
  1043. ngon = {{1, 0}, {0, 8}, {4, 12}, {8, 8}, {7, 0}};
  1044. EXPECT_NO_THROW({
  1045. area = minEnclosingConvexPolygon(ngon, kgon, 4);
  1046. });
  1047. expectedKgon = {{1, 0}, {-0.5, 12}, {8.5, 12}, {7, 0}};
  1048. EXPECT_EQ(area, cv::contourArea(expectedKgon));
  1049. ASSERT_EQ((int)kgon.size(), 4);
  1050. for (size_t i = 0; i < 4; i++)
  1051. {
  1052. bool match = false;
  1053. for (size_t j = 0; j < 4; j++)
  1054. {
  1055. if(expectedKgon[i].x == kgon[j].x && expectedKgon[i].y == kgon[j].y)
  1056. {
  1057. match = true;
  1058. break;
  1059. }
  1060. }
  1061. EXPECT_EQ(match, true);
  1062. }
  1063. }
  1064. TEST(Imgproc_minAreaRect, reproducer_21482)
  1065. {
  1066. const int N = 4;
  1067. float pts_[N][2] = {
  1068. { 188.8991f, 12.400669f },
  1069. { 80.64467f, -49.644814f },
  1070. { 469.59897f, 173.28242f },
  1071. { 690.4597f, 299.86768f },
  1072. };
  1073. Mat contour(N, 1, CV_32FC2, (void*)pts_);
  1074. RotatedRect rr = cv::minAreaRect(contour);
  1075. EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
  1076. EXPECT_NEAR(min(rr.size.width, rr.size.height), 0, 1e-5);
  1077. EXPECT_GE(max(rr.size.width, rr.size.height), 702);
  1078. }
  1079. TEST(Imgproc_minAreaRect, reproducer_21482_small_values)
  1080. {
  1081. const int N = 4;
  1082. float pts_[N][2] = { { 0.f, 0.f }, { 1e-4f, 0.f }, { 1e-4f, 1e-4f }, { 0.f, 1e-4f },};
  1083. Mat contour(N, 1, CV_32FC2, (void*)pts_);
  1084. RotatedRect rr = cv::minAreaRect(contour);
  1085. EXPECT_TRUE(checkMinAreaRect(rr, contour)) << rr.center << " " << rr.size << " " << rr.angle;
  1086. EXPECT_EQ(rr.size.width, 1e-4f);
  1087. EXPECT_EQ(rr.size.height, 1e-4f);
  1088. }
  1089. typedef testing::TestWithParam<tuple<Point2f, Point2f, Point2f, Size2f, float>> minAreaRect_of_line;
  1090. TEST_P(minAreaRect_of_line, accuracy)
  1091. {
  1092. Point2f p1 = get<0>(GetParam());
  1093. Point2f p2 = get<1>(GetParam());
  1094. RotatedRect out = minAreaRect(std::vector<Point2f>{p1, p2});
  1095. EXPECT_EQ(out.center, get<2>(GetParam()));
  1096. EXPECT_EQ(out.size, get<3>(GetParam()));
  1097. EXPECT_NEAR(out.angle, get<4>(GetParam()), 1e-6);
  1098. }
  1099. INSTANTIATE_TEST_CASE_P(Imgproc, minAreaRect_of_line,
  1100. testing::Values(
  1101. std::make_tuple(Point2f(10, 15), Point2f(10, 25), Point2f(10, 20), Size2f(10, 0), -90.f),
  1102. std::make_tuple(Point2f(450, 500), Point2f(508, 500), Point2f(479, 500), Size2f(0, 58), -90.f),
  1103. std::make_tuple(Point2f(10, 20), Point2f(13, 16), Point2f(11.5, 18), Size2f(5, 0), -53.1301041f),
  1104. std::make_tuple(Point2f(9, 19), Point2f(4, 7), Point2f(6.5, 13), Size2f(0, 13), -22.6198654f)
  1105. ));
  1106. typedef testing::TestWithParam<tuple<tuple<std::vector<Point>, Mat>, bool> > convexHull_monotonous;
  1107. TEST_P(convexHull_monotonous, self_intersecting_contour)
  1108. {
  1109. std::vector<Point> contour = get<0>(get<0>(GetParam()));
  1110. Mat ref = get<1>(get<0>(GetParam())).clone();
  1111. bool clockwise = get<1>(GetParam());
  1112. if (!clockwise)
  1113. {
  1114. std::reverse(ref.begin<int>(), ref.end<int>());
  1115. }
  1116. Mat indices;
  1117. convexHull(contour, indices, clockwise, false);
  1118. Point minLoc;
  1119. minMaxLoc(indices, nullptr, nullptr, &minLoc);
  1120. std::rotate(indices.begin<int>(), indices.begin<int>() + minLoc.y, indices.end<int>());
  1121. minMaxLoc(ref, nullptr, nullptr, &minLoc);
  1122. std::rotate(ref.begin<int>(), ref.begin<int>() + minLoc.y, ref.end<int>());
  1123. ASSERT_EQ( cvtest::norm(indices, ref, NORM_INF), 0) << indices;
  1124. }
  1125. INSTANTIATE_TEST_CASE_P(Imgproc, convexHull_monotonous,
  1126. testing::Combine(
  1127. testing::Values(
  1128. std::make_tuple(
  1129. std::vector<Point>{
  1130. Point(3, 2), Point(3, 4), Point(2, 5), Point(1, 5),
  1131. Point(2, 5), Point(3, 4), Point(6, 4), Point(6, 2)
  1132. },
  1133. (Mat_<int>(5, 1) << 0, 3, 4, 6, 7)
  1134. ),
  1135. std::make_tuple(
  1136. std::vector<Point>{
  1137. Point(3, -2), Point(3, -4), Point(2, -5), Point(1, -5),
  1138. Point(2, -5), Point(3, -4), Point(6, -4), Point(6, -2)
  1139. },
  1140. (Mat_<int>(5, 1) << 3, 0, 7, 6, 4)
  1141. ),
  1142. std::make_tuple(
  1143. std::vector<Point>{
  1144. Point(1, 1), Point(1, 0), Point(0, 0), Point(1, 0), Point(0, 1)
  1145. },
  1146. (Mat_<int>(4, 1) << 0, 1, 2, 4)
  1147. )
  1148. ),
  1149. testing::Bool()
  1150. ));
  1151. }} // namespace
  1152. /* End of file. */