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