test_contours.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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 "test_precomp.hpp"
  42. #include <opencv2/highgui.hpp>
  43. namespace opencv_test { namespace {
  44. class CV_FindContourTest : public cvtest::BaseTest
  45. {
  46. public:
  47. enum { NUM_IMG = 4 };
  48. CV_FindContourTest();
  49. ~CV_FindContourTest();
  50. void clear();
  51. protected:
  52. int read_params( const cv::FileStorage& fs );
  53. int prepare_test_case( int test_case_idx );
  54. int validate_test_results( int test_case_idx );
  55. void run_func();
  56. int min_blob_size, max_blob_size;
  57. int blob_count, max_log_blob_count;
  58. int retr_mode, approx_method;
  59. int min_log_img_width, max_log_img_width;
  60. int min_log_img_height, max_log_img_height;
  61. Size img_size;
  62. int count, count2;
  63. IplImage* img[NUM_IMG];
  64. CvMemStorage* storage;
  65. CvSeq *contours, *contours2, *chain;
  66. static const bool useVeryWideImages =
  67. #if SIZE_MAX <= 0xffffffff
  68. // 32-bit: don't even try the very wide images
  69. false
  70. #else
  71. // 64-bit: test with very wide images
  72. true
  73. #endif
  74. ;
  75. };
  76. CV_FindContourTest::CV_FindContourTest()
  77. {
  78. int i;
  79. test_case_count = useVeryWideImages ? 10 : 300;
  80. min_blob_size = 1;
  81. max_blob_size = 50;
  82. max_log_blob_count = 10;
  83. min_log_img_width = useVeryWideImages ? 17 : 3;
  84. max_log_img_width = useVeryWideImages ? 17 : 10;
  85. min_log_img_height = 3;
  86. max_log_img_height = 10;
  87. for( i = 0; i < NUM_IMG; i++ )
  88. img[i] = 0;
  89. storage = 0;
  90. }
  91. CV_FindContourTest::~CV_FindContourTest()
  92. {
  93. clear();
  94. }
  95. void CV_FindContourTest::clear()
  96. {
  97. int i;
  98. cvtest::BaseTest::clear();
  99. for( i = 0; i < NUM_IMG; i++ )
  100. cvReleaseImage( &img[i] );
  101. cvReleaseMemStorage( &storage );
  102. }
  103. int CV_FindContourTest::read_params( const cv::FileStorage& fs )
  104. {
  105. int t;
  106. int code = cvtest::BaseTest::read_params( fs );
  107. if( code < 0 )
  108. return code;
  109. read( find_param( fs, "min_blob_size" ), min_blob_size, min_blob_size );
  110. read( find_param( fs, "max_blob_size" ), max_blob_size, max_blob_size );
  111. read( find_param( fs, "max_log_blob_count" ), max_log_blob_count, max_log_blob_count );
  112. read( find_param( fs, "min_log_img_width" ), min_log_img_width, min_log_img_width );
  113. read( find_param( fs, "max_log_img_width" ), max_log_img_width, max_log_img_width );
  114. read( find_param( fs, "min_log_img_height"), min_log_img_height, min_log_img_height );
  115. read( find_param( fs, "max_log_img_height"), max_log_img_height, max_log_img_height );
  116. min_blob_size = cvtest::clipInt( min_blob_size, 1, 100 );
  117. max_blob_size = cvtest::clipInt( max_blob_size, 1, 100 );
  118. if( min_blob_size > max_blob_size )
  119. CV_SWAP( min_blob_size, max_blob_size, t );
  120. max_log_blob_count = cvtest::clipInt( max_log_blob_count, 1, 10 );
  121. min_log_img_width = cvtest::clipInt( min_log_img_width, 1, useVeryWideImages ? 17 : 10 );
  122. min_log_img_width = cvtest::clipInt( max_log_img_width, 1, useVeryWideImages ? 17 : 10 );
  123. min_log_img_height = cvtest::clipInt( min_log_img_height, 1, 10 );
  124. min_log_img_height = cvtest::clipInt( max_log_img_height, 1, 10 );
  125. if( min_log_img_width > max_log_img_width )
  126. std::swap( min_log_img_width, max_log_img_width );
  127. if (min_log_img_height > max_log_img_height)
  128. std::swap(min_log_img_height, max_log_img_height);
  129. return 0;
  130. }
  131. static void
  132. cvTsGenerateBlobImage( IplImage* img, int min_blob_size, int max_blob_size,
  133. int blob_count, int min_brightness, int max_brightness,
  134. RNG& rng )
  135. {
  136. int i;
  137. Size size;
  138. CV_Assert(img->depth == IPL_DEPTH_8U && img->nChannels == 1);
  139. cvZero( img );
  140. // keep the border clear
  141. cvSetImageROI( img, cvRect(1,1,img->width-2,img->height-2) );
  142. size = cvGetSize( img );
  143. for( i = 0; i < blob_count; i++ )
  144. {
  145. Point center;
  146. Size axes;
  147. int angle = cvtest::randInt(rng) % 180;
  148. int brightness = cvtest::randInt(rng) %
  149. (max_brightness - min_brightness) + min_brightness;
  150. center.x = cvtest::randInt(rng) % size.width;
  151. center.y = cvtest::randInt(rng) % size.height;
  152. axes.width = (cvtest::randInt(rng) %
  153. (max_blob_size - min_blob_size) + min_blob_size + 1)/2;
  154. axes.height = (cvtest::randInt(rng) %
  155. (max_blob_size - min_blob_size) + min_blob_size + 1)/2;
  156. cvEllipse( img, cvPoint(center), cvSize(axes), angle, 0, 360, cvScalar(brightness), CV_FILLED );
  157. }
  158. cvResetImageROI( img );
  159. }
  160. static void
  161. cvTsMarkContours( IplImage* img, int val )
  162. {
  163. int i, j;
  164. int step = img->widthStep;
  165. CV_Assert( img->depth == IPL_DEPTH_8U && img->nChannels == 1 && (val&1) != 0);
  166. for( i = 1; i < img->height - 1; i++ )
  167. for( j = 1; j < img->width - 1; j++ )
  168. {
  169. uchar* t = (uchar*)(img->imageData + img->widthStep*i + j);
  170. if( *t == 1 && (t[-step] == 0 || t[-1] == 0 || t[1] == 0 || t[step] == 0))
  171. *t = (uchar)val;
  172. }
  173. cvThreshold( img, img, val - 2, val, CV_THRESH_BINARY );
  174. }
  175. int CV_FindContourTest::prepare_test_case( int test_case_idx )
  176. {
  177. RNG& rng = ts->get_rng();
  178. const int min_brightness = 0, max_brightness = 2;
  179. int i, code = cvtest::BaseTest::prepare_test_case( test_case_idx );
  180. if( code < 0 )
  181. return code;
  182. clear();
  183. blob_count = cvRound(exp(cvtest::randReal(rng)*max_log_blob_count*CV_LOG2));
  184. img_size.width = cvRound(exp((cvtest::randReal(rng)*
  185. (max_log_img_width - min_log_img_width) + min_log_img_width)*CV_LOG2));
  186. img_size.height = cvRound(exp((cvtest::randReal(rng)*
  187. (max_log_img_height - min_log_img_height) + min_log_img_height)*CV_LOG2));
  188. approx_method = cvtest::randInt( rng ) % 4 + 1;
  189. retr_mode = cvtest::randInt( rng ) % 4;
  190. storage = cvCreateMemStorage( 1 << 10 );
  191. for( i = 0; i < NUM_IMG; i++ )
  192. img[i] = cvCreateImage( cvSize(img_size), 8, 1 );
  193. cvTsGenerateBlobImage( img[0], min_blob_size, max_blob_size,
  194. blob_count, min_brightness, max_brightness, rng );
  195. cvCopy( img[0], img[1] );
  196. cvCopy( img[0], img[2] );
  197. cvTsMarkContours( img[1], 255 );
  198. return 1;
  199. }
  200. void CV_FindContourTest::run_func()
  201. {
  202. contours = contours2 = chain = 0;
  203. count = cvFindContours( img[2], storage, &contours, sizeof(CvContour), retr_mode, approx_method );
  204. cvZero( img[3] );
  205. if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )
  206. cvDrawContours( img[3], contours, cvScalar(255), cvScalar(255), INT_MAX, -1 );
  207. cvCopy( img[0], img[2] );
  208. count2 = cvFindContours( img[2], storage, &chain, sizeof(CvChain), retr_mode, CV_CHAIN_CODE );
  209. if( chain )
  210. contours2 = cvApproxChains( chain, storage, approx_method, 0, 0, 1 );
  211. cvZero( img[2] );
  212. if( contours && retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )
  213. cvDrawContours( img[2], contours2, cvScalar(255), cvScalar(255), INT_MAX );
  214. }
  215. // the whole testing is done here, run_func() is not utilized in this test
  216. int CV_FindContourTest::validate_test_results( int /*test_case_idx*/ )
  217. {
  218. int code = cvtest::TS::OK;
  219. cvCmpS( img[0], 0, img[0], CV_CMP_GT );
  220. if( count != count2 )
  221. {
  222. ts->printf( cvtest::TS::LOG, "The number of contours retrieved with different "
  223. "approximation methods is not the same\n"
  224. "(%d contour(s) for method %d vs %d contour(s) for method %d)\n",
  225. count, approx_method, count2, CV_CHAIN_CODE );
  226. code = cvtest::TS::FAIL_INVALID_OUTPUT;
  227. }
  228. if( retr_mode != CV_RETR_EXTERNAL && approx_method < CV_CHAIN_APPROX_TC89_L1 )
  229. {
  230. Mat _img[4];
  231. for( int i = 0; i < 4; i++ )
  232. _img[i] = cvarrToMat(img[i]);
  233. code = cvtest::cmpEps2(ts, _img[0], _img[3], 0, true, "Comparing original image with the map of filled contours" );
  234. if( code < 0 )
  235. goto _exit_;
  236. code = cvtest::cmpEps2( ts, _img[1], _img[2], 0, true,
  237. "Comparing contour outline vs manually produced edge map" );
  238. if( code < 0 )
  239. goto _exit_;
  240. }
  241. if( contours )
  242. {
  243. CvTreeNodeIterator iterator1;
  244. CvTreeNodeIterator iterator2;
  245. int count3;
  246. for(int i = 0; i < 2; i++ )
  247. {
  248. CvTreeNodeIterator iterator;
  249. cvInitTreeNodeIterator( &iterator, i == 0 ? contours : contours2, INT_MAX );
  250. for( count3 = 0; cvNextTreeNode( &iterator ) != 0; count3++ )
  251. ;
  252. if( count3 != count )
  253. {
  254. ts->printf( cvtest::TS::LOG,
  255. "The returned number of retrieved contours (using the approx_method = %d) does not match\n"
  256. "to the actual number of contours in the tree/list (returned %d, actual %d)\n",
  257. i == 0 ? approx_method : 0, count, count3 );
  258. code = cvtest::TS::FAIL_INVALID_OUTPUT;
  259. goto _exit_;
  260. }
  261. }
  262. cvInitTreeNodeIterator( &iterator1, contours, INT_MAX );
  263. cvInitTreeNodeIterator( &iterator2, contours2, INT_MAX );
  264. for( count3 = 0; count3 < count; count3++ )
  265. {
  266. CvSeq* seq1 = (CvSeq*)cvNextTreeNode( &iterator1 );
  267. CvSeq* seq2 = (CvSeq*)cvNextTreeNode( &iterator2 );
  268. CvSeqReader reader1;
  269. CvSeqReader reader2;
  270. if( !seq1 || !seq2 )
  271. {
  272. ts->printf( cvtest::TS::LOG,
  273. "There are NULL pointers in the original contour tree or the "
  274. "tree produced by cvApproxChains\n" );
  275. code = cvtest::TS::FAIL_INVALID_OUTPUT;
  276. goto _exit_;
  277. }
  278. cvStartReadSeq( seq1, &reader1 );
  279. cvStartReadSeq( seq2, &reader2 );
  280. if( seq1->total != seq2->total )
  281. {
  282. ts->printf( cvtest::TS::LOG,
  283. "The original contour #%d has %d points, while the corresponding contour has %d point\n",
  284. count3, seq1->total, seq2->total );
  285. code = cvtest::TS::FAIL_INVALID_OUTPUT;
  286. goto _exit_;
  287. }
  288. for(int i = 0; i < seq1->total; i++ )
  289. {
  290. CvPoint pt1 = {0, 0};
  291. CvPoint pt2 = {0, 0};
  292. CV_READ_SEQ_ELEM( pt1, reader1 );
  293. CV_READ_SEQ_ELEM( pt2, reader2 );
  294. if( pt1.x != pt2.x || pt1.y != pt2.y )
  295. {
  296. ts->printf( cvtest::TS::LOG,
  297. "The point #%d in the contour #%d is different from the corresponding point "
  298. "in the approximated chain ((%d,%d) vs (%d,%d)", count3, i, pt1.x, pt1.y, pt2.x, pt2.y );
  299. code = cvtest::TS::FAIL_INVALID_OUTPUT;
  300. goto _exit_;
  301. }
  302. }
  303. }
  304. }
  305. _exit_:
  306. if( code < 0 )
  307. {
  308. #if 0
  309. cvNamedWindow( "test", 0 );
  310. cvShowImage( "test", img[0] );
  311. cvWaitKey();
  312. #endif
  313. ts->set_failed_test_info( code );
  314. }
  315. return code;
  316. }
  317. TEST(Imgproc_FindContours, accuracy) { CV_FindContourTest test; test.safe_run(); }
  318. //rotate/flip a quadrant appropriately
  319. static void rot(int n, int *x, int *y, int rx, int ry)
  320. {
  321. if (ry == 0) {
  322. if (rx == 1) {
  323. *x = n-1 - *x;
  324. *y = n-1 - *y;
  325. }
  326. //Swap x and y
  327. int t = *x;
  328. *x = *y;
  329. *y = t;
  330. }
  331. }
  332. static void d2xy(int n, int d, int *x, int *y)
  333. {
  334. int rx, ry, s, t=d;
  335. *x = *y = 0;
  336. for (s=1; s<n; s*=2)
  337. {
  338. rx = 1 & (t/2);
  339. ry = 1 & (t ^ rx);
  340. rot(s, x, y, rx, ry);
  341. *x += s * rx;
  342. *y += s * ry;
  343. t /= 4;
  344. }
  345. }
  346. TEST(Imgproc_FindContours, hilbert)
  347. {
  348. int n = 64, n2 = n*n, scale = 10, w = (n + 2)*scale;
  349. Point ofs(scale, scale);
  350. Mat img(w, w, CV_8U);
  351. img.setTo(Scalar::all(0));
  352. Point p(0,0);
  353. for( int i = 0; i < n2; i++ )
  354. {
  355. Point q(0,0);
  356. d2xy(n2, i, &q.x, &q.y);
  357. line(img, p*scale + ofs, q*scale + ofs, Scalar::all(255));
  358. p = q;
  359. }
  360. dilate(img, img, Mat());
  361. vector<vector<Point> > contours;
  362. findContours(img, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
  363. printf("ncontours = %d, contour[0].npoints=%d\n", (int)contours.size(), (int)contours[0].size());
  364. img.setTo(Scalar::all(0));
  365. drawContours(img, contours, 0, Scalar::all(255), 1);
  366. ASSERT_EQ(1, (int)contours.size());
  367. ASSERT_EQ(9832, (int)contours[0].size());
  368. }
  369. TEST(Imgproc_FindContours, border)
  370. {
  371. Mat img;
  372. cv::copyMakeBorder(Mat::zeros(8, 10, CV_8U), img, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(1));
  373. std::vector<std::vector<cv::Point> > contours;
  374. findContours(img, contours, RETR_LIST, CHAIN_APPROX_NONE);
  375. Mat img_draw_contours = Mat::zeros(img.size(), CV_8U);
  376. for (size_t cpt = 0; cpt < contours.size(); cpt++)
  377. {
  378. drawContours(img_draw_contours, contours, static_cast<int>(cpt), cv::Scalar(1));
  379. }
  380. ASSERT_EQ(0, cvtest::norm(img, img_draw_contours, NORM_INF));
  381. }
  382. TEST(Imgproc_FindContours, regression_4363_shared_nbd)
  383. {
  384. // Create specific test image
  385. Mat1b img(12, 69, (const uchar&)0);
  386. img(1, 1) = 1;
  387. // Vertical rectangle with hole sharing the same NBD
  388. for (int r = 1; r <= 10; ++r) {
  389. for (int c = 3; c <= 5; ++c) {
  390. img(r, c) = 1;
  391. }
  392. }
  393. img(9, 4) = 0;
  394. // 124 small CCs
  395. for (int r = 1; r <= 7; r += 2) {
  396. for (int c = 7; c <= 67; c += 2) {
  397. img(r, c) = 1;
  398. }
  399. }
  400. // Last CC
  401. img(9, 7) = 1;
  402. vector< vector<Point> > contours;
  403. vector<Vec4i> hierarchy;
  404. findContours(img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
  405. bool found = false;
  406. size_t index = 0;
  407. for (vector< vector<Point> >::const_iterator i = contours.begin(); i != contours.end(); ++i)
  408. {
  409. const vector<Point>& c = *i;
  410. if (!c.empty() && c[0] == Point(7, 9))
  411. {
  412. found = true;
  413. index = (size_t)(i - contours.begin());
  414. break;
  415. }
  416. }
  417. EXPECT_TRUE(found) << "Desired result: point (7,9) is a contour - Actual result: point (7,9) is not a contour";
  418. if (found)
  419. {
  420. EXPECT_LT(hierarchy[index][3], 0) << "Desired result: (7,9) has no parent - Actual result: parent of (7,9) is another contour. index = " << index;
  421. }
  422. }
  423. TEST(Imgproc_PointPolygonTest, regression_10222)
  424. {
  425. vector<Point> contour;
  426. contour.push_back(Point(0, 0));
  427. contour.push_back(Point(0, 100000));
  428. contour.push_back(Point(100000, 100000));
  429. contour.push_back(Point(100000, 50000));
  430. contour.push_back(Point(100000, 0));
  431. const Point2f point(40000, 40000);
  432. const double result = cv::pointPolygonTest(contour, point, false);
  433. EXPECT_GT(result, 0) << "Desired result: point is inside polygon - actual result: point is not inside polygon";
  434. }
  435. }} // namespace
  436. /* End of file. */