test_contours_new.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html
  4. #include "test_precomp.hpp"
  5. #include "opencv2/ts/ocl_test.hpp"
  6. #include "opencv2/imgproc/detail/legacy.hpp"
  7. #define CHECK_OLD 1
  8. namespace opencv_test { namespace {
  9. // debug function
  10. template <typename T>
  11. inline static void print_pts(const T& c)
  12. {
  13. for (const auto& one_pt : c)
  14. {
  15. cout << one_pt << " ";
  16. }
  17. cout << endl;
  18. }
  19. // debug function
  20. template <typename T>
  21. inline static void print_pts_2(vector<T>& cs)
  22. {
  23. int cnt = 0;
  24. cout << "Contours:" << endl;
  25. for (const auto& one_c : cs)
  26. {
  27. cout << cnt++ << " : ";
  28. print_pts(one_c);
  29. }
  30. };
  31. // draw 1-2 px blob with orientation defined by 'kind'
  32. template <typename T>
  33. inline static void drawSmallContour(Mat& img, Point pt, int kind, int color_)
  34. {
  35. const T color = static_cast<T>(color_);
  36. img.at<T>(pt) = color;
  37. switch (kind)
  38. {
  39. case 1: img.at<T>(pt + Point(1, 0)) = color; break;
  40. case 2: img.at<T>(pt + Point(1, -1)) = color; break;
  41. case 3: img.at<T>(pt + Point(0, -1)) = color; break;
  42. case 4: img.at<T>(pt + Point(-1, -1)) = color; break;
  43. case 5: img.at<T>(pt + Point(-1, 0)) = color; break;
  44. case 6: img.at<T>(pt + Point(-1, 1)) = color; break;
  45. case 7: img.at<T>(pt + Point(0, 1)) = color; break;
  46. case 8: img.at<T>(pt + Point(1, 1)) = color; break;
  47. default: break;
  48. }
  49. }
  50. inline static void drawContours(Mat& img,
  51. const vector<vector<Point>>& contours,
  52. const Scalar& color = Scalar::all(255))
  53. {
  54. for (const auto& contour : contours)
  55. {
  56. for (size_t n = 0, end = contour.size(); n < end; ++n)
  57. {
  58. size_t m = n + 1;
  59. if (n == end - 1)
  60. m = 0;
  61. line(img, contour[m], contour[n], color, 1, LINE_8);
  62. }
  63. }
  64. }
  65. //==================================================================================================
  66. // Test parameters - mode + method
  67. typedef testing::TestWithParam<tuple<int, int>> Imgproc_FindContours_Modes1;
  68. // Draw random rectangle and find contours
  69. //
  70. TEST_P(Imgproc_FindContours_Modes1, rectangle)
  71. {
  72. const int mode = get<0>(GetParam());
  73. const int method = get<1>(GetParam());
  74. const size_t ITER = 100;
  75. RNG rng = TS::ptr()->get_rng();
  76. for (size_t i = 0; i < ITER; ++i)
  77. {
  78. SCOPED_TRACE(cv::format("i=%zu", i));
  79. const Size sz(rng.uniform(640, 1920), rng.uniform(480, 1080));
  80. Mat img(sz, CV_8UC1, Scalar::all(0));
  81. Mat img32s(sz, CV_32SC1, Scalar::all(0));
  82. const Rect r(Point(rng.uniform(1, sz.width / 2 - 1), rng.uniform(1, sz.height / 2)),
  83. Point(rng.uniform(sz.width / 2 - 1, sz.width - 1),
  84. rng.uniform(sz.height / 2 - 1, sz.height - 1)));
  85. rectangle(img, r, Scalar::all(255));
  86. rectangle(img32s, r, Scalar::all(255), FILLED);
  87. const vector<Point> ext_ref {r.tl(),
  88. r.tl() + Point(0, r.height - 1),
  89. r.br() + Point(-1, -1),
  90. r.tl() + Point(r.width - 1, 0)};
  91. const vector<Point> int_ref {ext_ref[0] + Point(0, 1),
  92. ext_ref[0] + Point(1, 0),
  93. ext_ref[3] + Point(-1, 0),
  94. ext_ref[3] + Point(0, 1),
  95. ext_ref[2] + Point(0, -1),
  96. ext_ref[2] + Point(-1, 0),
  97. ext_ref[1] + Point(1, 0),
  98. ext_ref[1] + Point(0, -1)};
  99. const size_t ext_perimeter = r.width * 2 + r.height * 2;
  100. const size_t int_perimeter = ext_perimeter - 4;
  101. vector<vector<Point>> contours;
  102. vector<vector<schar>> chains;
  103. vector<Vec4i> hierarchy;
  104. // run functionn
  105. if (mode == RETR_FLOODFILL)
  106. if (method == 0)
  107. findContours(img32s, chains, hierarchy, mode, method);
  108. else
  109. findContours(img32s, contours, hierarchy, mode, method);
  110. else if (method == 0)
  111. findContours(img, chains, hierarchy, mode, method);
  112. else
  113. findContours(img, contours, hierarchy, mode, method);
  114. // verify results
  115. if (mode == RETR_EXTERNAL)
  116. {
  117. if (method == 0)
  118. {
  119. ASSERT_EQ(1U, chains.size());
  120. }
  121. else
  122. {
  123. ASSERT_EQ(1U, contours.size());
  124. if (method == CHAIN_APPROX_NONE)
  125. {
  126. EXPECT_EQ(int_perimeter, contours[0].size());
  127. }
  128. else if (method == CHAIN_APPROX_SIMPLE)
  129. {
  130. EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[0]), 0);
  131. }
  132. }
  133. }
  134. else
  135. {
  136. if (method == 0)
  137. {
  138. ASSERT_EQ(2U, chains.size());
  139. }
  140. else
  141. {
  142. ASSERT_EQ(2U, contours.size());
  143. if (mode == RETR_LIST)
  144. {
  145. if (method == CHAIN_APPROX_NONE)
  146. {
  147. EXPECT_EQ(int_perimeter - 4, contours[0].size());
  148. EXPECT_EQ(int_perimeter, contours[1].size());
  149. }
  150. else if (method == CHAIN_APPROX_SIMPLE)
  151. {
  152. EXPECT_MAT_NEAR(Mat(int_ref), Mat(contours[0]), 0);
  153. EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[1]), 0);
  154. }
  155. }
  156. else if (mode == RETR_CCOMP || mode == RETR_TREE)
  157. {
  158. if (method == CHAIN_APPROX_NONE)
  159. {
  160. EXPECT_EQ(int_perimeter, contours[0].size());
  161. EXPECT_EQ(int_perimeter - 4, contours[1].size());
  162. }
  163. else if (method == CHAIN_APPROX_SIMPLE)
  164. {
  165. EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[0]), 0);
  166. EXPECT_MAT_NEAR(Mat(int_ref), Mat(contours[1]), 0);
  167. }
  168. }
  169. else if (mode == RETR_FLOODFILL)
  170. {
  171. if (method == CHAIN_APPROX_NONE)
  172. {
  173. EXPECT_EQ(int_perimeter + 4, contours[0].size());
  174. }
  175. else if (method == CHAIN_APPROX_SIMPLE)
  176. {
  177. EXPECT_EQ(int_ref.size(), contours[0].size());
  178. EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[1]), 0);
  179. }
  180. }
  181. }
  182. }
  183. #if CHECK_OLD
  184. if (method != 0) // old doesn't support chain codes
  185. {
  186. if (mode != RETR_FLOODFILL)
  187. {
  188. vector<vector<Point>> contours_o;
  189. vector<Vec4i> hierarchy_o;
  190. findContours_legacy(img, contours_o, hierarchy_o, mode, method);
  191. ASSERT_EQ(contours.size(), contours_o.size());
  192. for (size_t j = 0; j < contours.size(); ++j)
  193. {
  194. SCOPED_TRACE(format("contour %zu", j));
  195. EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
  196. }
  197. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  198. }
  199. else
  200. {
  201. vector<vector<Point>> contours_o;
  202. vector<Vec4i> hierarchy_o;
  203. findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
  204. ASSERT_EQ(contours.size(), contours_o.size());
  205. for (size_t j = 0; j < contours.size(); ++j)
  206. {
  207. SCOPED_TRACE(format("contour %zu", j));
  208. EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
  209. }
  210. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  211. }
  212. }
  213. #endif
  214. }
  215. }
  216. // Draw many small 1-2px blobs and find contours
  217. //
  218. TEST_P(Imgproc_FindContours_Modes1, small)
  219. {
  220. const int mode = get<0>(GetParam());
  221. const int method = get<1>(GetParam());
  222. const size_t DIM = 1000;
  223. const Size sz(DIM, DIM);
  224. const int num = (DIM / 10) * (DIM / 10); // number of 10x10 squares
  225. Mat img(sz, CV_8UC1, Scalar::all(0));
  226. Mat img32s(sz, CV_32SC1, Scalar::all(0));
  227. vector<Point> pts;
  228. int extra_contours_32s = 0;
  229. for (int j = 0; j < num; ++j)
  230. {
  231. const int kind = j % 9;
  232. Point pt {(j % 100) * 10 + 4, (j / 100) * 10 + 4};
  233. drawSmallContour<uchar>(img, pt, kind, 255);
  234. drawSmallContour<int>(img32s, pt, kind, j + 1);
  235. pts.push_back(pt);
  236. // NOTE: for some reason these small diagonal contours (NW, SE)
  237. // result in 2 external contours for FLOODFILL mode
  238. if (kind == 8 || kind == 4)
  239. ++extra_contours_32s;
  240. }
  241. {
  242. vector<vector<Point>> contours;
  243. vector<vector<schar>> chains;
  244. vector<Vec4i> hierarchy;
  245. if (mode == RETR_FLOODFILL)
  246. {
  247. if (method == 0)
  248. {
  249. findContours(img32s, chains, hierarchy, mode, method);
  250. ASSERT_EQ(pts.size() * 2 + extra_contours_32s, chains.size());
  251. }
  252. else
  253. {
  254. findContours(img32s, contours, hierarchy, mode, method);
  255. ASSERT_EQ(pts.size() * 2 + extra_contours_32s, contours.size());
  256. #if CHECK_OLD
  257. vector<vector<Point>> contours_o;
  258. vector<Vec4i> hierarchy_o;
  259. findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
  260. ASSERT_EQ(contours.size(), contours_o.size());
  261. for (size_t i = 0; i < contours.size(); ++i)
  262. {
  263. SCOPED_TRACE(format("contour %zu", i));
  264. EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
  265. }
  266. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  267. #endif
  268. }
  269. }
  270. else
  271. {
  272. if (method == 0)
  273. {
  274. findContours(img, chains, hierarchy, mode, method);
  275. ASSERT_EQ(pts.size(), chains.size());
  276. }
  277. else
  278. {
  279. findContours(img, contours, hierarchy, mode, method);
  280. ASSERT_EQ(pts.size(), contours.size());
  281. #if CHECK_OLD
  282. vector<vector<Point>> contours_o;
  283. vector<Vec4i> hierarchy_o;
  284. findContours_legacy(img, contours_o, hierarchy_o, mode, method);
  285. ASSERT_EQ(contours.size(), contours_o.size());
  286. for (size_t i = 0; i < contours.size(); ++i)
  287. {
  288. SCOPED_TRACE(format("contour %zu", i));
  289. EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
  290. }
  291. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  292. #endif
  293. }
  294. }
  295. }
  296. }
  297. // Draw many nested rectangles and find contours
  298. //
  299. TEST_P(Imgproc_FindContours_Modes1, deep)
  300. {
  301. const int mode = get<0>(GetParam());
  302. const int method = get<1>(GetParam());
  303. const size_t DIM = 1000;
  304. const Size sz(DIM, DIM);
  305. const size_t NUM = 249U;
  306. Mat img(sz, CV_8UC1, Scalar::all(0));
  307. Mat img32s(sz, CV_32SC1, Scalar::all(0));
  308. Rect rect(1, 1, 998, 998);
  309. for (size_t i = 0; i < NUM; ++i)
  310. {
  311. rectangle(img, rect, Scalar::all(255));
  312. rectangle(img32s, rect, Scalar::all((double)i + 1), FILLED);
  313. rect.x += 2;
  314. rect.y += 2;
  315. rect.width -= 4;
  316. rect.height -= 4;
  317. }
  318. {
  319. vector<vector<Point>> contours {{{0, 0}, {1, 1}}};
  320. vector<vector<schar>> chains {{1, 2, 3}};
  321. vector<Vec4i> hierarchy;
  322. if (mode == RETR_FLOODFILL)
  323. {
  324. if (method == 0)
  325. {
  326. findContours(img32s, chains, hierarchy, mode, method);
  327. ASSERT_EQ(2 * NUM, chains.size());
  328. }
  329. else
  330. {
  331. findContours(img32s, contours, hierarchy, mode, method);
  332. ASSERT_EQ(2 * NUM, contours.size());
  333. #if CHECK_OLD
  334. vector<vector<Point>> contours_o;
  335. vector<Vec4i> hierarchy_o;
  336. findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
  337. ASSERT_EQ(contours.size(), contours_o.size());
  338. for (size_t i = 0; i < contours.size(); ++i)
  339. {
  340. SCOPED_TRACE(format("contour %zu", i));
  341. EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
  342. }
  343. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  344. #endif
  345. }
  346. }
  347. else
  348. {
  349. const size_t expected_count = (mode == RETR_EXTERNAL) ? 1U : 2 * NUM;
  350. if (method == 0)
  351. {
  352. findContours(img, chains, hierarchy, mode, method);
  353. ASSERT_EQ(expected_count, chains.size());
  354. }
  355. else
  356. {
  357. findContours(img, contours, hierarchy, mode, method);
  358. ASSERT_EQ(expected_count, contours.size());
  359. #if CHECK_OLD
  360. vector<vector<Point>> contours_o;
  361. vector<Vec4i> hierarchy_o;
  362. findContours_legacy(img, contours_o, hierarchy_o, mode, method);
  363. ASSERT_EQ(contours.size(), contours_o.size());
  364. for (size_t i = 0; i < contours.size(); ++i)
  365. {
  366. SCOPED_TRACE(format("contour %zu", i));
  367. EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
  368. }
  369. EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
  370. #endif
  371. }
  372. }
  373. }
  374. }
  375. INSTANTIATE_TEST_CASE_P(
  376. ,
  377. Imgproc_FindContours_Modes1,
  378. testing::Combine(
  379. testing::Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE, RETR_FLOODFILL),
  380. testing::Values(0,
  381. CHAIN_APPROX_NONE,
  382. CHAIN_APPROX_SIMPLE,
  383. CHAIN_APPROX_TC89_L1,
  384. CHAIN_APPROX_TC89_KCOS)));
  385. //==================================================================================================
  386. typedef testing::TestWithParam<tuple<int, int>> Imgproc_FindContours_Modes2;
  387. // Very approximate backport of an old accuracy test
  388. //
  389. TEST_P(Imgproc_FindContours_Modes2, new_accuracy)
  390. {
  391. const int mode = get<0>(GetParam());
  392. const int method = get<1>(GetParam());
  393. RNG& rng = TS::ptr()->get_rng();
  394. const int blob_count = rng.uniform(1, 10);
  395. const Size sz(rng.uniform(640, 1920), rng.uniform(480, 1080));
  396. const int blob_sz = 50;
  397. // prepare image
  398. Mat img(sz, CV_8UC1, Scalar::all(0));
  399. vector<RotatedRect> rects;
  400. for (int i = 0; i < blob_count; ++i)
  401. {
  402. const Point2f center((float)rng.uniform(blob_sz, sz.width - blob_sz),
  403. (float)rng.uniform(blob_sz, sz.height - blob_sz));
  404. const Size2f rsize((float)rng.uniform(1, blob_sz), (float)rng.uniform(1, blob_sz));
  405. RotatedRect rect(center, rsize, rng.uniform(0.f, 180.f));
  406. rects.push_back(rect);
  407. ellipse(img, rect, Scalar::all(100), FILLED);
  408. }
  409. // draw contours manually
  410. Mat cont_img(sz, CV_8UC1, Scalar::all(0));
  411. for (int y = 1; y < sz.height - 1; ++y)
  412. {
  413. for (int x = 1; x < sz.width - 1; ++x)
  414. {
  415. if (img.at<uchar>(y, x) != 0 &&
  416. ((img.at<uchar>(y - 1, x) == 0) || (img.at<uchar>(y + 1, x) == 0) ||
  417. (img.at<uchar>(y, x + 1) == 0) || (img.at<uchar>(y, x - 1) == 0)))
  418. {
  419. cont_img.at<uchar>(y, x) = 255;
  420. }
  421. }
  422. }
  423. // find contours
  424. vector<vector<Point>> contours;
  425. vector<Vec4i> hierarchy;
  426. findContours(img, contours, hierarchy, mode, method);
  427. // 0 < contours <= rects
  428. EXPECT_GT(contours.size(), 0U);
  429. EXPECT_GE(rects.size(), contours.size());
  430. // draw contours
  431. Mat res_img(sz, CV_8UC1, Scalar::all(0));
  432. drawContours(res_img, contours);
  433. // compare resulting drawn contours with manually drawn contours
  434. const double diff1 = cvtest::norm(cont_img, res_img, NORM_L1) / 255;
  435. if (method == CHAIN_APPROX_NONE || method == CHAIN_APPROX_SIMPLE)
  436. {
  437. EXPECT_EQ(0., diff1);
  438. }
  439. #if CHECK_OLD
  440. vector<vector<Point>> contours_o;
  441. vector<Vec4i> hierarchy_o;
  442. findContours(img, contours_o, hierarchy_o, mode, method);
  443. ASSERT_EQ(contours_o.size(), contours.size());
  444. for (size_t i = 0; i < contours_o.size(); ++i)
  445. {
  446. SCOPED_TRACE(format("contour = %zu", i));
  447. EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
  448. }
  449. EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
  450. #endif
  451. }
  452. TEST_P(Imgproc_FindContours_Modes2, approx)
  453. {
  454. const int mode = get<0>(GetParam());
  455. const int method = get<1>(GetParam());
  456. const Size sz {500, 500};
  457. Mat img = Mat::zeros(sz, CV_8UC1);
  458. for (int c = 0; c < 4; ++c)
  459. {
  460. if (c != 0)
  461. {
  462. // noise + filter + threshold
  463. RNG& rng = TS::ptr()->get_rng();
  464. cvtest::randUni(rng, img, 0, 255);
  465. Mat fimg;
  466. boxFilter(img, fimg, CV_8U, Size(5, 5));
  467. Mat timg;
  468. const int level = 44 + c * 42;
  469. // 'level' goes through:
  470. // 86 - some black speckles on white
  471. // 128 - 50/50 black/white
  472. // 170 - some white speckles on black
  473. cv::threshold(fimg, timg, level, 255, THRESH_BINARY);
  474. }
  475. else
  476. {
  477. // circle with cut
  478. const Point center {250, 250};
  479. const int r {20};
  480. const Point cut {r, r};
  481. circle(img, center, r, Scalar(255), FILLED);
  482. rectangle(img, center, center + cut, Scalar(0), FILLED);
  483. }
  484. vector<vector<Point>> contours;
  485. vector<Vec4i> hierarchy;
  486. findContours(img, contours, hierarchy, mode, method);
  487. #if CHECK_OLD
  488. // NOTE: old and new function results might not match when approximation mode is TC89.
  489. // Currently this test passes, but might fail for other random data.
  490. // See https://github.com/opencv/opencv/issues/25663 for details.
  491. vector<vector<Point>> contours_o;
  492. vector<Vec4i> hierarchy_o;
  493. findContours_legacy(img, contours_o, hierarchy_o, mode, method);
  494. ASSERT_EQ(contours_o.size(), contours.size());
  495. for (size_t i = 0; i < contours_o.size(); ++i)
  496. {
  497. SCOPED_TRACE(format("c = %d, contour = %zu", c, i));
  498. EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
  499. }
  500. EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
  501. #endif
  502. // TODO: check something
  503. }
  504. }
  505. // TODO: offset test
  506. // no RETR_FLOODFILL - no CV_32S input images
  507. INSTANTIATE_TEST_CASE_P(
  508. ,
  509. Imgproc_FindContours_Modes2,
  510. testing::Combine(testing::Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE),
  511. testing::Values(CHAIN_APPROX_NONE,
  512. CHAIN_APPROX_SIMPLE,
  513. CHAIN_APPROX_TC89_L1,
  514. CHAIN_APPROX_TC89_KCOS)));
  515. TEST(Imgproc_FindContours, link_runs)
  516. {
  517. const Size sz {500, 500};
  518. Mat img = Mat::zeros(sz, CV_8UC1);
  519. // noise + filter + threshold
  520. RNG& rng = TS::ptr()->get_rng();
  521. cvtest::randUni(rng, img, 0, 255);
  522. Mat fimg;
  523. boxFilter(img, fimg, CV_8U, Size(5, 5));
  524. const int level = 135;
  525. cv::threshold(fimg, img, level, 255, THRESH_BINARY);
  526. vector<vector<Point>> contours;
  527. vector<Vec4i> hierarchy;
  528. findContoursLinkRuns(img, contours, hierarchy);
  529. if (cvtest::debugLevel >= 10)
  530. {
  531. print_pts_2(contours);
  532. Mat res = Mat::zeros(sz, CV_8UC1);
  533. drawContours(res, contours);
  534. imshow("res", res);
  535. imshow("img", img);
  536. waitKey(0);
  537. }
  538. #if CHECK_OLD
  539. vector<vector<Point>> contours_o;
  540. vector<Vec4i> hierarchy_o;
  541. findContours_legacy(img, contours_o, hierarchy_o, 0, 5); // CV_LINK_RUNS method
  542. ASSERT_EQ(contours_o.size(), contours.size());
  543. for (size_t i = 0; i < contours_o.size(); ++i)
  544. {
  545. SCOPED_TRACE(format("contour = %zu", i));
  546. EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
  547. }
  548. EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
  549. #endif
  550. }
  551. }} // namespace opencv_test