Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/test/test_floodfill.cpp
16339 views
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
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
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
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
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
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
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
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
528
TEST(Imgproc_FloodFill, accuracy) { CV_FloodFillTest test; test.safe_run(); }
529
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
547