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 
44 namespace opencv_test { namespace {
45 
46 class CV_FloodFillTest : public cvtest::ArrayTest
47 {
48 public:
49     CV_FloodFillTest();
50 
51 protected:
52     void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
53     double get_success_error_level( int test_case_idx, int i, int j );
54     void run_func();
55     void prepare_to_validation( int );
56 
57     void fill_array( int test_case_idx, int i, int j, Mat& arr );
58 
59     /*int write_default_params(CvFileStorage* fs);
60     void get_timing_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types
61                                                 CvSize** whole_sizes, bool *are_images );
62     void print_timing_params( int test_case_idx, char* ptr, int params_left );*/
63     Point seed_pt;
64     Scalar new_val;
65     Scalar l_diff, u_diff;
66     int connectivity;
67     bool use_mask, mask_only;
68     int range_type;
69     int new_mask_val;
70     bool test_cpp;
71 };
72 
73 
CV_FloodFillTest()74 CV_FloodFillTest::CV_FloodFillTest()
75 {
76     test_array[INPUT_OUTPUT].push_back(NULL);
77     test_array[INPUT_OUTPUT].push_back(NULL);
78     test_array[REF_INPUT_OUTPUT].push_back(NULL);
79     test_array[REF_INPUT_OUTPUT].push_back(NULL);
80     test_array[OUTPUT].push_back(NULL);
81     test_array[REF_OUTPUT].push_back(NULL);
82     optional_mask = false;
83     element_wise_relative_error = true;
84 
85     test_cpp = false;
86 }
87 
88 
get_test_array_types_and_sizes(int test_case_idx,vector<vector<Size>> & sizes,vector<vector<int>> & types)89 void CV_FloodFillTest::get_test_array_types_and_sizes( int test_case_idx,
90                                                        vector<vector<Size> >& sizes,
91                                                        vector<vector<int> >& types )
92 {
93     RNG& rng = ts->get_rng();
94     int depth, cn;
95     int i;
96     double buff[8];
97     cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types );
98 
99     depth = cvtest::randInt(rng) % 3;
100     depth = depth == 0 ? CV_8U : depth == 1 ? CV_32S : CV_32F;
101     cn = cvtest::randInt(rng) & 1 ? 3 : 1;
102 
103     use_mask = (cvtest::randInt(rng) & 1) != 0;
104     connectivity = (cvtest::randInt(rng) & 1) ? 4 : 8;
105     mask_only = use_mask && (cvtest::randInt(rng) & 1) != 0;
106     new_mask_val = cvtest::randInt(rng) & 255;
107     range_type = cvtest::randInt(rng) % 3;
108 
109     types[INPUT_OUTPUT][0] = types[REF_INPUT_OUTPUT][0] = CV_MAKETYPE(depth, cn);
110     types[INPUT_OUTPUT][1] = types[REF_INPUT_OUTPUT][1] = CV_8UC1;
111     types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_64FC1;
112     sizes[OUTPUT][0] = sizes[REF_OUTPUT][0] = cvSize(9,1);
113 
114     if( !use_mask )
115         sizes[INPUT_OUTPUT][1] = sizes[REF_INPUT_OUTPUT][1] = cvSize(0,0);
116     else
117     {
118         Size sz = sizes[INPUT_OUTPUT][0];
119         sizes[INPUT_OUTPUT][1] = sizes[REF_INPUT_OUTPUT][1] = Size(sz.width+2,sz.height+2);
120     }
121 
122     seed_pt.x = cvtest::randInt(rng) % sizes[INPUT_OUTPUT][0].width;
123     seed_pt.y = cvtest::randInt(rng) % sizes[INPUT_OUTPUT][0].height;
124 
125     if( range_type == 0 )
126         l_diff = u_diff = Scalar::all(0.);
127     else
128     {
129         Mat m( 1, 8, CV_16S, buff );
130         rng.fill( m, RNG::NORMAL, Scalar::all(0), Scalar::all(32) );
131         for( i = 0; i < 4; i++ )
132         {
133             l_diff.val[i] = fabs(m.at<short>(i)/16.);
134             u_diff.val[i] = fabs(m.at<short>(i+4)/16.);
135         }
136     }
137 
138     new_val = Scalar::all(0.);
139     for( i = 0; i < cn; i++ )
140         new_val.val[i] = cvtest::randReal(rng)*255;
141 
142     test_cpp = (cvtest::randInt(rng) & 256) == 0;
143 }
144 
145 
get_success_error_level(int,int i,int j)146 double CV_FloodFillTest::get_success_error_level( int /*test_case_idx*/, int i, int j )
147 {
148     return i == OUTPUT ? FLT_EPSILON : j == 0 ? FLT_EPSILON : 0;
149 }
150 
151 
fill_array(int test_case_idx,int i,int j,Mat & arr)152 void CV_FloodFillTest::fill_array( int test_case_idx, int i, int j, Mat& arr )
153 {
154     RNG& rng = ts->get_rng();
155 
156     if( i != INPUT && i != INPUT_OUTPUT )
157     {
158         cvtest::ArrayTest::fill_array( test_case_idx, i, j, arr );
159         return;
160     }
161 
162     if( j == 0 )
163     {
164         Mat tmp = arr;
165         Scalar m = Scalar::all(128);
166         Scalar s = Scalar::all(10);
167 
168         if( arr.depth() == CV_32FC1 )
169             tmp.create(arr.size(), CV_MAKETYPE(CV_8U, arr.channels()));
170 
171         if( range_type == 0 )
172             s = Scalar::all(2);
173 
174         rng.fill(tmp, RNG::NORMAL, m, s );
175         if( arr.data != tmp.data )
176             cvtest::convert(tmp, arr, arr.type());
177     }
178     else
179     {
180         Scalar l = Scalar::all(-2);
181         Scalar u = Scalar::all(2);
182         cvtest::randUni(rng, arr, l, u );
183         rectangle( arr, Point(0,0), Point(arr.cols-1,arr.rows-1), Scalar::all(1), 1, 8, 0 );
184     }
185 }
186 
187 
run_func()188 void CV_FloodFillTest::run_func()
189 {
190     int flags = connectivity + (mask_only ? CV_FLOODFILL_MASK_ONLY : 0) +
191         (range_type == 1 ? CV_FLOODFILL_FIXED_RANGE : 0) + (new_mask_val << 8);
192     double* odata = test_mat[OUTPUT][0].ptr<double>();
193 
194     if(!test_cpp)
195     {
196         CvConnectedComp comp;
197         cvFloodFill( test_array[INPUT_OUTPUT][0], cvPoint(seed_pt), cvScalar(new_val), cvScalar(l_diff), cvScalar(u_diff), &comp,
198                      flags, test_array[INPUT_OUTPUT][1] );
199         odata[0] = comp.area;
200         odata[1] = comp.rect.x;
201         odata[2] = comp.rect.y;
202         odata[3] = comp.rect.width;
203         odata[4] = comp.rect.height;
204         odata[5] = comp.value.val[0];
205         odata[6] = comp.value.val[1];
206         odata[7] = comp.value.val[2];
207         odata[8] = comp.value.val[3];
208     }
209     else
210     {
211         cv::Mat img = cv::cvarrToMat(test_array[INPUT_OUTPUT][0]),
212             mask = test_array[INPUT_OUTPUT][1] ? cv::cvarrToMat(test_array[INPUT_OUTPUT][1]) : cv::Mat();
213         cv::Rect rect;
214         int area;
215         if( mask.empty() )
216             area = cv::floodFill( img, seed_pt, new_val, &rect, l_diff, u_diff, flags );
217         else
218             area = cv::floodFill( img, mask, seed_pt, new_val, &rect, l_diff, u_diff, flags );
219         odata[0] = area;
220         odata[1] = rect.x;
221         odata[2] = rect.y;
222         odata[3] = rect.width;
223         odata[4] = rect.height;
224         odata[5] = odata[6] = odata[7] = odata[8] = 0;
225     }
226 }
227 
228 
229 typedef struct ff_offset_pair_t
230 {
231     int mofs, iofs;
232 }
233 ff_offset_pair_t;
234 
235 static void
cvTsFloodFill(CvMat * _img,CvPoint seed_pt,CvScalar new_val,CvScalar l_diff,CvScalar u_diff,CvMat * _mask,double * comp,int connectivity,int range_type,int new_mask_val,bool mask_only)236 cvTsFloodFill( CvMat* _img, CvPoint seed_pt, CvScalar new_val,
237                CvScalar l_diff, CvScalar u_diff, CvMat* _mask,
238                double* comp, int connectivity, int range_type,
239                int new_mask_val, bool mask_only )
240 {
241     CvMemStorage* st = cvCreateMemStorage();
242     ff_offset_pair_t p0, p;
243     CvSeq* seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(p0), st );
244     CvMat* tmp = _img;
245     CvMat* mask;
246     CvRect r = cvRect( 0, 0, -1, -1 );
247     int area = 0;
248     int i, j;
249     ushort* m;
250     float* img;
251     int mstep, step;
252     int cn = CV_MAT_CN(_img->type);
253     int mdelta[8], idelta[8], ncount;
254     int cols = _img->cols, rows = _img->rows;
255     int u0 = 0, u1 = 0, u2 = 0;
256     double s0 = 0, s1 = 0, s2 = 0;
257 
258     if( CV_MAT_DEPTH(_img->type) == CV_8U || CV_MAT_DEPTH(_img->type) == CV_32S )
259     {
260         tmp = cvCreateMat( rows, cols, CV_MAKETYPE(CV_32F,CV_MAT_CN(_img->type)) );
261         cvtest::convert(cvarrToMat(_img), cvarrToMat(tmp), -1);
262     }
263 
264     mask = cvCreateMat( rows + 2, cols + 2, CV_16UC1 );
265 
266     if( _mask )
267         cvtest::convert(cvarrToMat(_mask), cvarrToMat(mask), -1);
268     else
269     {
270         Mat m_mask = cvarrToMat(mask);
271         cvtest::set( m_mask, Scalar::all(0), Mat() );
272         cvRectangle( mask, cvPoint(0,0), cvPoint(mask->cols-1,mask->rows-1), cvScalar(Scalar::all(1.)), 1, 8, 0 );
273     }
274 
275     new_mask_val = (new_mask_val != 0 ? new_mask_val : 1) << 8;
276 
277     m = (ushort*)(mask->data.ptr + mask->step) + 1;
278     mstep = mask->step / sizeof(m[0]);
279     img = tmp->data.fl;
280     step = tmp->step / sizeof(img[0]);
281 
282     p0.mofs = seed_pt.y*mstep + seed_pt.x;
283     p0.iofs = seed_pt.y*step + seed_pt.x*cn;
284 
285     if( m[p0.mofs] )
286         goto _exit_;
287 
288     cvSeqPush( seq, &p0 );
289     m[p0.mofs] = (ushort)new_mask_val;
290 
291     if( connectivity == 4 )
292     {
293         ncount = 4;
294         mdelta[0] = -mstep; idelta[0] = -step;
295         mdelta[1] = -1; idelta[1] = -cn;
296         mdelta[2] = 1; idelta[2] = cn;
297         mdelta[3] = mstep; idelta[3] = step;
298     }
299     else
300     {
301         ncount = 8;
302         mdelta[0] = -mstep-1; mdelta[1] = -mstep; mdelta[2] = -mstep+1;
303         idelta[0] = -step-cn; idelta[1] = -step; idelta[2] = -step+cn;
304 
305         mdelta[3] = -1; mdelta[4] = 1;
306         idelta[3] = -cn; idelta[4] = cn;
307 
308         mdelta[5] = mstep-1; mdelta[6] = mstep; mdelta[7] = mstep+1;
309         idelta[5] = step-cn; idelta[6] = step; idelta[7] = step+cn;
310     }
311 
312     if( cn == 1 )
313     {
314         float a0 = (float)-l_diff.val[0];
315         float b0 = (float)u_diff.val[0];
316 
317         s0 = img[p0.iofs];
318 
319         if( range_type < 2 )
320         {
321             a0 += (float)s0; b0 += (float)s0;
322         }
323 
324         while( seq->total )
325         {
326             cvSeqPop( seq, &p0 );
327             float a = a0, b = b0;
328             float* ptr = img + p0.iofs;
329             ushort* mptr = m + p0.mofs;
330 
331             if( range_type == 2 )
332                 a += ptr[0], b += ptr[0];
333 
334             for( i = 0; i < ncount; i++ )
335             {
336                 int md = mdelta[i], id = idelta[i];
337                 float v;
338                 if( !mptr[md] && a <= (v = ptr[id]) && v <= b )
339                 {
340                     mptr[md] = (ushort)new_mask_val;
341                     p.mofs = p0.mofs + md;
342                     p.iofs = p0.iofs + id;
343                     cvSeqPush( seq, &p );
344                 }
345             }
346         }
347     }
348     else
349     {
350         float a0 = (float)-l_diff.val[0];
351         float a1 = (float)-l_diff.val[1];
352         float a2 = (float)-l_diff.val[2];
353         float b0 = (float)u_diff.val[0];
354         float b1 = (float)u_diff.val[1];
355         float b2 = (float)u_diff.val[2];
356 
357         s0 = img[p0.iofs];
358         s1 = img[p0.iofs + 1];
359         s2 = img[p0.iofs + 2];
360 
361         if( range_type < 2 )
362         {
363             a0 += (float)s0; b0 += (float)s0;
364             a1 += (float)s1; b1 += (float)s1;
365             a2 += (float)s2; b2 += (float)s2;
366         }
367 
368         while( seq->total )
369         {
370             cvSeqPop( seq, &p0 );
371             float _a0 = a0, _a1 = a1, _a2 = a2;
372             float _b0 = b0, _b1 = b1, _b2 = b2;
373             float* ptr = img + p0.iofs;
374             ushort* mptr = m + p0.mofs;
375 
376             if( range_type == 2 )
377             {
378                 _a0 += ptr[0]; _b0 += ptr[0];
379                 _a1 += ptr[1]; _b1 += ptr[1];
380                 _a2 += ptr[2]; _b2 += ptr[2];
381             }
382 
383             for( i = 0; i < ncount; i++ )
384             {
385                 int md = mdelta[i], id = idelta[i];
386                 float v;
387                 if( !mptr[md] &&
388                     _a0 <= (v = ptr[id]) && v <= _b0 &&
389                     _a1 <= (v = ptr[id+1]) && v <= _b1 &&
390                     _a2 <= (v = ptr[id+2]) && v <= _b2 )
391                 {
392                     mptr[md] = (ushort)new_mask_val;
393                     p.mofs = p0.mofs + md;
394                     p.iofs = p0.iofs + id;
395                     cvSeqPush( seq, &p );
396                 }
397             }
398         }
399     }
400 
401     r.x = r.width = seed_pt.x;
402     r.y = r.height = seed_pt.y;
403 
404     if( !mask_only )
405     {
406         s0 = new_val.val[0];
407         s1 = new_val.val[1];
408         s2 = new_val.val[2];
409 
410         if( tmp != _img )
411         {
412             u0 = saturate_cast<uchar>(s0);
413             u1 = saturate_cast<uchar>(s1);
414             u2 = saturate_cast<uchar>(s2);
415 
416             s0 = u0;
417             s1 = u1;
418             s2 = u2;
419         }
420     }
421     else
422         s0 = s1 = s2 = 0;
423 
424     new_mask_val >>= 8;
425 
426     for( i = 0; i < rows; i++ )
427     {
428         float* ptr = img + i*step;
429         ushort* mptr = m + i*mstep;
430         uchar* dmptr = _mask ? _mask->data.ptr + (i+1)*_mask->step + 1 : 0;
431         double area0 = area;
432 
433         for( j = 0; j < cols; j++ )
434         {
435             if( mptr[j] > 255 )
436             {
437                 if( dmptr )
438                     dmptr[j] = (uchar)new_mask_val;
439                 if( !mask_only )
440                 {
441                     if( cn == 1 )
442                         ptr[j] = (float)s0;
443                     else
444                     {
445                         ptr[j*3] = (float)s0;
446                         ptr[j*3+1] = (float)s1;
447                         ptr[j*3+2] = (float)s2;
448                     }
449                 }
450                 else
451                 {
452                     if( cn == 1 )
453                         s0 += ptr[j];
454                     else
455                     {
456                         s0 += ptr[j*3];
457                         s1 += ptr[j*3+1];
458                         s2 += ptr[j*3+2];
459                     }
460                 }
461 
462                 area++;
463                 if( r.x > j )
464                     r.x = j;
465                 if( r.width < j )
466                     r.width = j;
467             }
468         }
469 
470         if( area != area0 )
471         {
472             if( r.y > i )
473                 r.y = i;
474             if( r.height < i )
475                 r.height = i;
476         }
477     }
478 
479 _exit_:
480     cvReleaseMat( &mask );
481     if( tmp != _img )
482     {
483         if( !mask_only )
484             cvtest::convert(cvarrToMat(tmp), cvarrToMat(_img), -1);
485         cvReleaseMat( &tmp );
486     }
487 
488     comp[0] = area;
489     comp[1] = r.x;
490     comp[2] = r.y;
491     comp[3] = r.width - r.x + 1;
492     comp[4] = r.height - r.y + 1;
493 #if 0
494     if( mask_only )
495     {
496         double t = area ? 1./area : 0;
497         s0 *= t;
498         s1 *= t;
499         s2 *= t;
500     }
501     comp[5] = s0;
502     comp[6] = s1;
503     comp[7] = s2;
504 #else
505     comp[5] = new_val.val[0];
506     comp[6] = new_val.val[1];
507     comp[7] = new_val.val[2];
508 #endif
509     comp[8] = 0;
510 
511     cvReleaseMemStorage(&st);
512 }
513 
514 
prepare_to_validation(int)515 void CV_FloodFillTest::prepare_to_validation( int /*test_case_idx*/ )
516 {
517     double* comp = test_mat[REF_OUTPUT][0].ptr<double>();
518     CvMat _input = cvMat(test_mat[REF_INPUT_OUTPUT][0]);
519     CvMat _mask = cvMat(test_mat[REF_INPUT_OUTPUT][1]);
520     cvTsFloodFill( &_input, cvPoint(seed_pt), cvScalar(new_val), cvScalar(l_diff), cvScalar(u_diff),
521                    _mask.data.ptr ? &_mask : 0,
522                    comp, connectivity, range_type,
523                    new_mask_val, mask_only );
524     if(test_cpp)
525         comp[5] = comp[6] = comp[7] = comp[8] = 0;
526 }
527 
TEST(Imgproc_FloodFill,accuracy)528 TEST(Imgproc_FloodFill, accuracy) { CV_FloodFillTest test; test.safe_run(); }
529 
TEST(Imgproc_FloodFill,maskValue)530 TEST(Imgproc_FloodFill, maskValue)
531 {
532     const int n = 50;
533     Mat img = Mat::zeros(n, n, CV_8U);
534     Mat mask = Mat::zeros(n + 2, n + 2, CV_8U);
535 
536     circle(img, Point(n/2, n/2), 20, Scalar(100), 4);
537 
538     int flags = 4 + CV_FLOODFILL_MASK_ONLY;
539     floodFill(img, mask, Point(n/2 + 13, n/2), Scalar(100), NULL, Scalar(),  Scalar(), flags);
540 
541     ASSERT_EQ(1, cvtest::norm(mask.rowRange(1, n-1).colRange(1, n-1), NORM_INF));
542 }
543 
544 }} // namespace
545 /* End of file. */
546