Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/floodfill.cpp
16354 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
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
45
#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 8)
46
# pragma GCC diagnostic ignored "-Warray-bounds"
47
#endif
48
49
namespace cv
50
{
51
52
struct FFillSegment
53
{
54
ushort y;
55
ushort l;
56
ushort r;
57
ushort prevl;
58
ushort prevr;
59
short dir;
60
};
61
62
enum
63
{
64
UP = 1,
65
DOWN = -1
66
};
67
68
#define ICV_PUSH( Y, L, R, PREV_L, PREV_R, DIR ) \
69
{ \
70
tail->y = (ushort)(Y); \
71
tail->l = (ushort)(L); \
72
tail->r = (ushort)(R); \
73
tail->prevl = (ushort)(PREV_L); \
74
tail->prevr = (ushort)(PREV_R); \
75
tail->dir = (short)(DIR); \
76
if( ++tail == buffer_end ) \
77
{ \
78
buffer->resize(buffer->size() * 3/2); \
79
tail = &buffer->front() + (tail - head); \
80
head = &buffer->front(); \
81
buffer_end = head + buffer->size(); \
82
} \
83
}
84
85
#define ICV_POP( Y, L, R, PREV_L, PREV_R, DIR ) \
86
{ \
87
--tail; \
88
Y = tail->y; \
89
L = tail->l; \
90
R = tail->r; \
91
PREV_L = tail->prevl; \
92
PREV_R = tail->prevr; \
93
DIR = tail->dir; \
94
}
95
96
struct ConnectedComp
97
{
98
ConnectedComp();
99
Rect rect;
100
Point pt;
101
int threshold;
102
int label;
103
int area;
104
int harea;
105
int carea;
106
int perimeter;
107
int nholes;
108
int ninflections;
109
double mx;
110
double my;
111
Scalar avg;
112
Scalar sdv;
113
};
114
115
ConnectedComp::ConnectedComp()
116
{
117
rect = Rect(0, 0, 0, 0);
118
pt = Point(-1, -1);
119
threshold = -1;
120
label = -1;
121
area = harea = carea = perimeter = nholes = ninflections = 0;
122
mx = my = 0;
123
avg = sdv = Scalar::all(0);
124
}
125
126
// Simple Floodfill (repainting single-color connected component)
127
128
template<typename _Tp>
129
static void
130
floodFill_CnIR( Mat& image, Point seed,
131
_Tp newVal, ConnectedComp* region, int flags,
132
std::vector<FFillSegment>* buffer )
133
{
134
_Tp* img = image.ptr<_Tp>(seed.y);
135
Size roi = image.size();
136
int i, L, R;
137
int area = 0;
138
int XMin, XMax, YMin = seed.y, YMax = seed.y;
139
int _8_connectivity = (flags & 255) == 8;
140
FFillSegment* buffer_end = &buffer->front() + buffer->size(), *head = &buffer->front(), *tail = &buffer->front();
141
142
L = R = XMin = XMax = seed.x;
143
144
_Tp val0 = img[L];
145
img[L] = newVal;
146
147
while( ++R < roi.width && img[R] == val0 )
148
img[R] = newVal;
149
150
while( --L >= 0 && img[L] == val0 )
151
img[L] = newVal;
152
153
XMax = --R;
154
XMin = ++L;
155
156
ICV_PUSH( seed.y, L, R, R + 1, R, UP );
157
158
while( head != tail )
159
{
160
int k, YC, PL, PR, dir;
161
ICV_POP( YC, L, R, PL, PR, dir );
162
163
int data[][3] =
164
{
165
{-dir, L - _8_connectivity, R + _8_connectivity},
166
{dir, L - _8_connectivity, PL - 1},
167
{dir, PR + 1, R + _8_connectivity}
168
};
169
170
if( region )
171
{
172
area += R - L + 1;
173
174
if( XMax < R ) XMax = R;
175
if( XMin > L ) XMin = L;
176
if( YMax < YC ) YMax = YC;
177
if( YMin > YC ) YMin = YC;
178
}
179
180
for( k = 0; k < 3; k++ )
181
{
182
dir = data[k][0];
183
184
if( (unsigned)(YC + dir) >= (unsigned)roi.height )
185
continue;
186
187
img = image.ptr<_Tp>(YC + dir);
188
int left = data[k][1];
189
int right = data[k][2];
190
191
for( i = left; i <= right; i++ )
192
{
193
if( (unsigned)i < (unsigned)roi.width && img[i] == val0 )
194
{
195
int j = i;
196
img[i] = newVal;
197
while( --j >= 0 && img[j] == val0 )
198
img[j] = newVal;
199
200
while( ++i < roi.width && img[i] == val0 )
201
img[i] = newVal;
202
203
ICV_PUSH( YC + dir, j+1, i-1, L, R, -dir );
204
}
205
}
206
}
207
}
208
209
if( region )
210
{
211
region->pt = seed;
212
region->area = area;
213
region->rect.x = XMin;
214
region->rect.y = YMin;
215
region->rect.width = XMax - XMin + 1;
216
region->rect.height = YMax - YMin + 1;
217
}
218
}
219
220
/****************************************************************************************\
221
* Gradient Floodfill *
222
\****************************************************************************************/
223
224
struct Diff8uC1
225
{
226
Diff8uC1(uchar _lo, uchar _up) : lo(_lo), interval(_lo + _up) {}
227
bool operator()(const uchar* a, const uchar* b) const
228
{ return (unsigned)(a[0] - b[0] + lo) <= interval; }
229
unsigned lo, interval;
230
};
231
232
struct Diff8uC3
233
{
234
Diff8uC3(Vec3b _lo, Vec3b _up)
235
{
236
for( int k = 0; k < 3; k++ )
237
lo[k] = _lo[k], interval[k] = _lo[k] + _up[k];
238
}
239
bool operator()(const Vec3b* a, const Vec3b* b) const
240
{
241
return (unsigned)(a[0][0] - b[0][0] + lo[0]) <= interval[0] &&
242
(unsigned)(a[0][1] - b[0][1] + lo[1]) <= interval[1] &&
243
(unsigned)(a[0][2] - b[0][2] + lo[2]) <= interval[2];
244
}
245
unsigned lo[3], interval[3];
246
};
247
248
template<typename _Tp>
249
struct DiffC1
250
{
251
DiffC1(_Tp _lo, _Tp _up) : lo(-_lo), up(_up) {}
252
bool operator()(const _Tp* a, const _Tp* b) const
253
{
254
_Tp d = a[0] - b[0];
255
return lo <= d && d <= up;
256
}
257
_Tp lo, up;
258
};
259
260
template<typename _Tp>
261
struct DiffC3
262
{
263
DiffC3(_Tp _lo, _Tp _up) : lo(-_lo), up(_up) {}
264
bool operator()(const _Tp* a, const _Tp* b) const
265
{
266
_Tp d = *a - *b;
267
return lo[0] <= d[0] && d[0] <= up[0] &&
268
lo[1] <= d[1] && d[1] <= up[1] &&
269
lo[2] <= d[2] && d[2] <= up[2];
270
}
271
_Tp lo, up;
272
};
273
274
typedef DiffC1<int> Diff32sC1;
275
typedef DiffC3<Vec3i> Diff32sC3;
276
typedef DiffC1<float> Diff32fC1;
277
typedef DiffC3<Vec3f> Diff32fC3;
278
279
template<typename _Tp, typename _MTp, typename _WTp, class Diff>
280
static void
281
floodFillGrad_CnIR( Mat& image, Mat& msk,
282
Point seed, _Tp newVal, _MTp newMaskVal,
283
Diff diff, ConnectedComp* region, int flags,
284
std::vector<FFillSegment>* buffer )
285
{
286
int step = (int)image.step, maskStep = (int)msk.step;
287
uchar* pImage = image.ptr();
288
_Tp* img = (_Tp*)(pImage + step*seed.y);
289
uchar* pMask = msk.ptr() + maskStep + sizeof(_MTp);
290
_MTp* mask = (_MTp*)(pMask + maskStep*seed.y);
291
int i, L, R;
292
int area = 0;
293
int XMin, XMax, YMin = seed.y, YMax = seed.y;
294
int _8_connectivity = (flags & 255) == 8;
295
int fixedRange = flags & FLOODFILL_FIXED_RANGE;
296
int fillImage = (flags & FLOODFILL_MASK_ONLY) == 0;
297
FFillSegment* buffer_end = &buffer->front() + buffer->size(), *head = &buffer->front(), *tail = &buffer->front();
298
299
L = R = seed.x;
300
if( mask[L] )
301
return;
302
303
mask[L] = newMaskVal;
304
_Tp val0 = img[L];
305
306
if( fixedRange )
307
{
308
while( !mask[R + 1] && diff( img + (R+1), &val0 ))
309
mask[++R] = newMaskVal;
310
311
while( !mask[L - 1] && diff( img + (L-1), &val0 ))
312
mask[--L] = newMaskVal;
313
}
314
else
315
{
316
while( !mask[R + 1] && diff( img + (R+1), img + R ))
317
mask[++R] = newMaskVal;
318
319
while( !mask[L - 1] && diff( img + (L-1), img + L ))
320
mask[--L] = newMaskVal;
321
}
322
323
XMax = R;
324
XMin = L;
325
326
ICV_PUSH( seed.y, L, R, R + 1, R, UP );
327
328
while( head != tail )
329
{
330
int k, YC, PL, PR, dir;
331
ICV_POP( YC, L, R, PL, PR, dir );
332
333
int data[][3] =
334
{
335
{-dir, L - _8_connectivity, R + _8_connectivity},
336
{dir, L - _8_connectivity, PL - 1},
337
{dir, PR + 1, R + _8_connectivity}
338
};
339
340
unsigned length = (unsigned)(R-L);
341
342
if( region )
343
{
344
area += (int)length + 1;
345
346
if( XMax < R ) XMax = R;
347
if( XMin > L ) XMin = L;
348
if( YMax < YC ) YMax = YC;
349
if( YMin > YC ) YMin = YC;
350
}
351
352
for( k = 0; k < 3; k++ )
353
{
354
dir = data[k][0];
355
img = (_Tp*)(pImage + (YC + dir) * step);
356
_Tp* img1 = (_Tp*)(pImage + YC * step);
357
mask = (_MTp*)(pMask + (YC + dir) * maskStep);
358
int left = data[k][1];
359
int right = data[k][2];
360
361
if( fixedRange )
362
for( i = left; i <= right; i++ )
363
{
364
if( !mask[i] && diff( img + i, &val0 ))
365
{
366
int j = i;
367
mask[i] = newMaskVal;
368
while( !mask[--j] && diff( img + j, &val0 ))
369
mask[j] = newMaskVal;
370
371
while( !mask[++i] && diff( img + i, &val0 ))
372
mask[i] = newMaskVal;
373
374
ICV_PUSH( YC + dir, j+1, i-1, L, R, -dir );
375
}
376
}
377
else if( !_8_connectivity )
378
for( i = left; i <= right; i++ )
379
{
380
if( !mask[i] && diff( img + i, img1 + i ))
381
{
382
int j = i;
383
mask[i] = newMaskVal;
384
while( !mask[--j] && diff( img + j, img + (j+1) ))
385
mask[j] = newMaskVal;
386
387
while( !mask[++i] &&
388
(diff( img + i, img + (i-1) ) ||
389
(diff( img + i, img1 + i) && i <= R)))
390
mask[i] = newMaskVal;
391
392
ICV_PUSH( YC + dir, j+1, i-1, L, R, -dir );
393
}
394
}
395
else
396
for( i = left; i <= right; i++ )
397
{
398
int idx;
399
_Tp val;
400
401
if( !mask[i] &&
402
(((val = img[i],
403
(unsigned)(idx = i-L-1) <= length) &&
404
diff( &val, img1 + (i-1))) ||
405
((unsigned)(++idx) <= length &&
406
diff( &val, img1 + i )) ||
407
((unsigned)(++idx) <= length &&
408
diff( &val, img1 + (i+1) ))))
409
{
410
int j = i;
411
mask[i] = newMaskVal;
412
while( !mask[--j] && diff( img + j, img + (j+1) ))
413
mask[j] = newMaskVal;
414
415
while( !mask[++i] &&
416
((val = img[i],
417
diff( &val, img + (i-1) )) ||
418
(((unsigned)(idx = i-L-1) <= length &&
419
diff( &val, img1 + (i-1) ))) ||
420
((unsigned)(++idx) <= length &&
421
diff( &val, img1 + i )) ||
422
((unsigned)(++idx) <= length &&
423
diff( &val, img1 + (i+1) ))))
424
mask[i] = newMaskVal;
425
426
ICV_PUSH( YC + dir, j+1, i-1, L, R, -dir );
427
}
428
}
429
}
430
431
img = (_Tp*)(pImage + YC * step);
432
if( fillImage )
433
for( i = L; i <= R; i++ )
434
img[i] = newVal;
435
/*else if( region )
436
for( i = L; i <= R; i++ )
437
sum += img[i];*/
438
}
439
440
if( region )
441
{
442
region->pt = seed;
443
region->label = saturate_cast<int>(newMaskVal);
444
region->area = area;
445
region->rect.x = XMin;
446
region->rect.y = YMin;
447
region->rect.width = XMax - XMin + 1;
448
region->rect.height = YMax - YMin + 1;
449
}
450
}
451
452
}
453
454
/****************************************************************************************\
455
* External Functions *
456
\****************************************************************************************/
457
458
int cv::floodFill( InputOutputArray _image, InputOutputArray _mask,
459
Point seedPoint, Scalar newVal, Rect* rect,
460
Scalar loDiff, Scalar upDiff, int flags )
461
{
462
CV_INSTRUMENT_REGION();
463
464
ConnectedComp comp;
465
std::vector<FFillSegment> buffer;
466
467
if( rect )
468
*rect = Rect();
469
470
int i;
471
union {
472
uchar b[4];
473
int i[4];
474
float f[4];
475
double _[4];
476
} nv_buf;
477
nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0;
478
479
struct { Vec3b b; Vec3i i; Vec3f f; } ld_buf, ud_buf;
480
Mat img = _image.getMat(), mask;
481
if( !_mask.empty() )
482
mask = _mask.getMat();
483
Size size = img.size();
484
485
int type = img.type();
486
int depth = img.depth();
487
int cn = img.channels();
488
489
if ( (cn != 1) && (cn != 3) )
490
{
491
CV_Error( CV_StsBadArg, "Number of channels in input image must be 1 or 3" );
492
}
493
494
const int connectivity = flags & 255;
495
if( connectivity != 0 && connectivity != 4 && connectivity != 8 )
496
CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8" );
497
498
bool is_simple = mask.empty() && (flags & FLOODFILL_MASK_ONLY) == 0;
499
500
for( i = 0; i < cn; i++ )
501
{
502
if( loDiff[i] < 0 || upDiff[i] < 0 )
503
CV_Error( CV_StsBadArg, "lo_diff and up_diff must be non-negative" );
504
is_simple = is_simple && fabs(loDiff[i]) < DBL_EPSILON && fabs(upDiff[i]) < DBL_EPSILON;
505
}
506
507
if( (unsigned)seedPoint.x >= (unsigned)size.width ||
508
(unsigned)seedPoint.y >= (unsigned)size.height )
509
CV_Error( CV_StsOutOfRange, "Seed point is outside of image" );
510
511
scalarToRawData( newVal, &nv_buf, type, 0);
512
size_t buffer_size = MAX( size.width, size.height ) * 2;
513
buffer.resize( buffer_size );
514
515
if( is_simple )
516
{
517
size_t elem_size = img.elemSize();
518
const uchar* seed_ptr = img.ptr(seedPoint.y) + elem_size*seedPoint.x;
519
520
size_t k = 0;
521
for(; k < elem_size; k++)
522
if (seed_ptr[k] != nv_buf.b[k])
523
break;
524
525
if( k != elem_size )
526
{
527
if( type == CV_8UC1 )
528
floodFill_CnIR(img, seedPoint, nv_buf.b[0], &comp, flags, &buffer);
529
else if( type == CV_8UC3 )
530
floodFill_CnIR(img, seedPoint, Vec3b(nv_buf.b), &comp, flags, &buffer);
531
else if( type == CV_32SC1 )
532
floodFill_CnIR(img, seedPoint, nv_buf.i[0], &comp, flags, &buffer);
533
else if( type == CV_32FC1 )
534
floodFill_CnIR(img, seedPoint, nv_buf.f[0], &comp, flags, &buffer);
535
else if( type == CV_32SC3 )
536
floodFill_CnIR(img, seedPoint, Vec3i(nv_buf.i), &comp, flags, &buffer);
537
else if( type == CV_32FC3 )
538
floodFill_CnIR(img, seedPoint, Vec3f(nv_buf.f), &comp, flags, &buffer);
539
else
540
CV_Error( CV_StsUnsupportedFormat, "" );
541
if( rect )
542
*rect = comp.rect;
543
return comp.area;
544
}
545
}
546
547
if( mask.empty() )
548
{
549
Mat tempMask( size.height + 2, size.width + 2, CV_8UC1 );
550
tempMask.setTo(Scalar::all(0));
551
mask = tempMask;
552
}
553
else
554
{
555
CV_Assert( mask.rows == size.height+2 && mask.cols == size.width+2 );
556
CV_Assert( mask.type() == CV_8U );
557
}
558
559
memset( mask.ptr(), 1, mask.cols );
560
memset( mask.ptr(mask.rows-1), 1, mask.cols );
561
562
for( i = 1; i <= size.height; i++ )
563
{
564
mask.at<uchar>(i, 0) = mask.at<uchar>(i, mask.cols-1) = (uchar)1;
565
}
566
567
if( depth == CV_8U )
568
for( i = 0; i < cn; i++ )
569
{
570
ld_buf.b[i] = saturate_cast<uchar>(cvFloor(loDiff[i]));
571
ud_buf.b[i] = saturate_cast<uchar>(cvFloor(upDiff[i]));
572
}
573
else if( depth == CV_32S )
574
for( i = 0; i < cn; i++ )
575
{
576
ld_buf.i[i] = cvFloor(loDiff[i]);
577
ud_buf.i[i] = cvFloor(upDiff[i]);
578
}
579
else if( depth == CV_32F )
580
for( i = 0; i < cn; i++ )
581
{
582
ld_buf.f[i] = (float)loDiff[i];
583
ud_buf.f[i] = (float)upDiff[i];
584
}
585
else
586
CV_Error( CV_StsUnsupportedFormat, "" );
587
588
uchar newMaskVal = (uchar)((flags & 0xff00) == 0 ? 1 : ((flags >> 8) & 255));
589
590
if( type == CV_8UC1 )
591
floodFillGrad_CnIR<uchar, uchar, int, Diff8uC1>(
592
img, mask, seedPoint, nv_buf.b[0], newMaskVal,
593
Diff8uC1(ld_buf.b[0], ud_buf.b[0]),
594
&comp, flags, &buffer);
595
else if( type == CV_8UC3 )
596
floodFillGrad_CnIR<Vec3b, uchar, Vec3i, Diff8uC3>(
597
img, mask, seedPoint, Vec3b(nv_buf.b), newMaskVal,
598
Diff8uC3(ld_buf.b, ud_buf.b),
599
&comp, flags, &buffer);
600
else if( type == CV_32SC1 )
601
floodFillGrad_CnIR<int, uchar, int, Diff32sC1>(
602
img, mask, seedPoint, nv_buf.i[0], newMaskVal,
603
Diff32sC1(ld_buf.i[0], ud_buf.i[0]),
604
&comp, flags, &buffer);
605
else if( type == CV_32SC3 )
606
floodFillGrad_CnIR<Vec3i, uchar, Vec3i, Diff32sC3>(
607
img, mask, seedPoint, Vec3i(nv_buf.i), newMaskVal,
608
Diff32sC3(ld_buf.i, ud_buf.i),
609
&comp, flags, &buffer);
610
else if( type == CV_32FC1 )
611
floodFillGrad_CnIR<float, uchar, float, Diff32fC1>(
612
img, mask, seedPoint, nv_buf.f[0], newMaskVal,
613
Diff32fC1(ld_buf.f[0], ud_buf.f[0]),
614
&comp, flags, &buffer);
615
else if( type == CV_32FC3 )
616
floodFillGrad_CnIR<Vec3f, uchar, Vec3f, Diff32fC3>(
617
img, mask, seedPoint, Vec3f(nv_buf.f), newMaskVal,
618
Diff32fC3(ld_buf.f, ud_buf.f),
619
&comp, flags, &buffer);
620
else
621
CV_Error(CV_StsUnsupportedFormat, "");
622
623
if( rect )
624
*rect = comp.rect;
625
return comp.area;
626
}
627
628
629
int cv::floodFill( InputOutputArray _image, Point seedPoint,
630
Scalar newVal, Rect* rect,
631
Scalar loDiff, Scalar upDiff, int flags )
632
{
633
CV_INSTRUMENT_REGION();
634
635
return floodFill(_image, Mat(), seedPoint, newVal, rect, loDiff, upDiff, flags);
636
}
637
638
639
CV_IMPL void
640
cvFloodFill( CvArr* arr, CvPoint seed_point,
641
CvScalar newVal, CvScalar lo_diff, CvScalar up_diff,
642
CvConnectedComp* comp, int flags, CvArr* maskarr )
643
{
644
if( comp )
645
memset( comp, 0, sizeof(*comp) );
646
647
cv::Mat img = cv::cvarrToMat(arr), mask = cv::cvarrToMat(maskarr);
648
int area = cv::floodFill(img, mask, seed_point, newVal,
649
comp ? (cv::Rect*)&comp->rect : 0,
650
lo_diff, up_diff, flags );
651
if( comp )
652
{
653
comp->area = area;
654
comp->value = newVal;
655
}
656
}
657
658
/* End of file. */
659
660