Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/src/contours.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
// 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 "precomp.hpp"
42
#include "opencv2/core/hal/intrin.hpp"
43
44
/* initializes 8-element array for fast access to 3x3 neighborhood of a pixel */
45
#define CV_INIT_3X3_DELTAS( deltas, step, nch ) \
46
((deltas)[0] = (nch), (deltas)[1] = -(step) + (nch), \
47
(deltas)[2] = -(step), (deltas)[3] = -(step) - (nch), \
48
(deltas)[4] = -(nch), (deltas)[5] = (step) - (nch), \
49
(deltas)[6] = (step), (deltas)[7] = (step) + (nch))
50
51
static const CvPoint icvCodeDeltas[8] =
52
{ {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} };
53
54
CV_IMPL void
55
cvStartReadChainPoints( CvChain * chain, CvChainPtReader * reader )
56
{
57
int i;
58
59
if( !chain || !reader )
60
CV_Error( CV_StsNullPtr, "" );
61
62
if( chain->elem_size != 1 || chain->header_size < (int)sizeof(CvChain))
63
CV_Error( CV_StsBadSize, "" );
64
65
cvStartReadSeq( (CvSeq *) chain, (CvSeqReader *) reader, 0 );
66
67
reader->pt = chain->origin;
68
for( i = 0; i < 8; i++ )
69
{
70
reader->deltas[i][0] = (schar) icvCodeDeltas[i].x;
71
reader->deltas[i][1] = (schar) icvCodeDeltas[i].y;
72
}
73
}
74
75
76
/* retrieves next point of the chain curve and updates reader */
77
CV_IMPL CvPoint
78
cvReadChainPoint( CvChainPtReader * reader )
79
{
80
if( !reader )
81
CV_Error( CV_StsNullPtr, "" );
82
83
cv::Point2i pt = reader->pt;
84
85
schar *ptr = reader->ptr;
86
if (ptr)
87
{
88
int code = *ptr++;
89
90
if( ptr >= reader->block_max )
91
{
92
cvChangeSeqBlock( (CvSeqReader *) reader, 1 );
93
ptr = reader->ptr;
94
}
95
96
reader->ptr = ptr;
97
reader->code = (schar)code;
98
assert( (code & ~7) == 0 );
99
reader->pt.x = pt.x + icvCodeDeltas[code].x;
100
reader->pt.y = pt.y + icvCodeDeltas[code].y;
101
}
102
103
return cvPoint(pt);
104
}
105
106
107
/****************************************************************************************\
108
* Raster->Chain Tree (Suzuki algorithms) *
109
\****************************************************************************************/
110
111
typedef struct _CvContourInfo
112
{
113
int flags;
114
struct _CvContourInfo *next; /* next contour with the same mark value */
115
struct _CvContourInfo *parent; /* information about parent contour */
116
CvSeq *contour; /* corresponding contour (may be 0, if rejected) */
117
CvRect rect; /* bounding rectangle */
118
CvPoint origin; /* origin point (where the contour was traced from) */
119
int is_hole; /* hole flag */
120
}
121
_CvContourInfo;
122
123
124
/*
125
Structure that is used for sequential retrieving contours from the image.
126
It supports both hierarchical and plane variants of Suzuki algorithm.
127
*/
128
typedef struct _CvContourScanner
129
{
130
CvMemStorage *storage1; /* contains fetched contours */
131
CvMemStorage *storage2; /* contains approximated contours
132
(!=storage1 if approx_method2 != approx_method1) */
133
CvMemStorage *cinfo_storage; /* contains _CvContourInfo nodes */
134
CvSet *cinfo_set; /* set of _CvContourInfo nodes */
135
CvMemStoragePos initial_pos; /* starting storage pos */
136
CvMemStoragePos backup_pos; /* beginning of the latest approx. contour */
137
CvMemStoragePos backup_pos2; /* ending of the latest approx. contour */
138
schar *img0; /* image origin */
139
schar *img; /* current image row */
140
int img_step; /* image step */
141
CvSize img_size; /* ROI size */
142
CvPoint offset; /* ROI offset: coordinates, added to each contour point */
143
CvPoint pt; /* current scanner position */
144
CvPoint lnbd; /* position of the last met contour */
145
int nbd; /* current mark val */
146
_CvContourInfo *l_cinfo; /* information about latest approx. contour */
147
_CvContourInfo cinfo_temp; /* temporary var which is used in simple modes */
148
_CvContourInfo frame_info; /* information about frame */
149
CvSeq frame; /* frame itself */
150
int approx_method1; /* approx method when tracing */
151
int approx_method2; /* final approx method */
152
int mode; /* contour scanning mode:
153
0 - external only
154
1 - all the contours w/o any hierarchy
155
2 - connected components (i.e. two-level structure -
156
external contours and holes),
157
3 - full hierarchy;
158
4 - connected components of a multi-level image
159
*/
160
int subst_flag;
161
int seq_type1; /* type of fetched contours */
162
int header_size1; /* hdr size of fetched contours */
163
int elem_size1; /* elem size of fetched contours */
164
int seq_type2; /* */
165
int header_size2; /* the same for approx. contours */
166
int elem_size2; /* */
167
_CvContourInfo *cinfo_table[128];
168
}
169
_CvContourScanner;
170
171
#define _CV_FIND_CONTOURS_FLAGS_EXTERNAL_ONLY 1
172
#define _CV_FIND_CONTOURS_FLAGS_HIERARCHIC 2
173
174
/*
175
Initializes scanner structure.
176
Prepare image for scanning ( clear borders and convert all pixels to 0-1.
177
*/
178
static CvContourScanner
179
cvStartFindContours_Impl( void* _img, CvMemStorage* storage,
180
int header_size, int mode,
181
int method, CvPoint offset, int needFillBorder )
182
{
183
if( !storage )
184
CV_Error( CV_StsNullPtr, "" );
185
186
CvMat stub, *mat = cvGetMat( _img, &stub );
187
188
if( CV_MAT_TYPE(mat->type) == CV_32SC1 && mode == CV_RETR_CCOMP )
189
mode = CV_RETR_FLOODFILL;
190
191
if( !((CV_IS_MASK_ARR( mat ) && mode < CV_RETR_FLOODFILL) ||
192
(CV_MAT_TYPE(mat->type) == CV_32SC1 && mode == CV_RETR_FLOODFILL)) )
193
CV_Error( CV_StsUnsupportedFormat,
194
"[Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL "
195
"otherwise supports CV_32SC1 images only" );
196
197
CvSize size = cvSize( mat->width, mat->height );
198
int step = mat->step;
199
uchar* img = (uchar*)(mat->data.ptr);
200
201
if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS )
202
CV_Error( CV_StsOutOfRange, "" );
203
204
if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour )))
205
CV_Error( CV_StsBadSize, "" );
206
207
CvContourScanner scanner = (CvContourScanner)cvAlloc( sizeof( *scanner ));
208
memset( scanner, 0, sizeof(*scanner) );
209
210
scanner->storage1 = scanner->storage2 = storage;
211
scanner->img0 = (schar *) img;
212
scanner->img = (schar *) (img + step);
213
scanner->img_step = step;
214
scanner->img_size.width = size.width - 1; /* exclude rightest column */
215
scanner->img_size.height = size.height - 1; /* exclude bottomost row */
216
scanner->mode = mode;
217
scanner->offset = offset;
218
scanner->pt.x = scanner->pt.y = 1;
219
scanner->lnbd.x = 0;
220
scanner->lnbd.y = 1;
221
scanner->nbd = 2;
222
scanner->frame_info.contour = &(scanner->frame);
223
scanner->frame_info.is_hole = 1;
224
scanner->frame_info.next = 0;
225
scanner->frame_info.parent = 0;
226
scanner->frame_info.rect = cvRect( 0, 0, size.width, size.height );
227
scanner->l_cinfo = 0;
228
scanner->subst_flag = 0;
229
230
scanner->frame.flags = CV_SEQ_FLAG_HOLE;
231
232
scanner->approx_method2 = scanner->approx_method1 = method;
233
234
if( method == CV_CHAIN_APPROX_TC89_L1 || method == CV_CHAIN_APPROX_TC89_KCOS )
235
scanner->approx_method1 = CV_CHAIN_CODE;
236
237
if( scanner->approx_method1 == CV_CHAIN_CODE )
238
{
239
scanner->seq_type1 = CV_SEQ_CHAIN_CONTOUR;
240
scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ?
241
header_size : sizeof( CvChain );
242
scanner->elem_size1 = sizeof( char );
243
}
244
else
245
{
246
scanner->seq_type1 = CV_SEQ_POLYGON;
247
scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ?
248
header_size : sizeof( CvContour );
249
scanner->elem_size1 = sizeof( CvPoint );
250
}
251
252
scanner->header_size2 = header_size;
253
254
if( scanner->approx_method2 == CV_CHAIN_CODE )
255
{
256
scanner->seq_type2 = scanner->seq_type1;
257
scanner->elem_size2 = scanner->elem_size1;
258
}
259
else
260
{
261
scanner->seq_type2 = CV_SEQ_POLYGON;
262
scanner->elem_size2 = sizeof( CvPoint );
263
}
264
265
scanner->seq_type1 = scanner->approx_method1 == CV_CHAIN_CODE ?
266
CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON;
267
268
scanner->seq_type2 = scanner->approx_method2 == CV_CHAIN_CODE ?
269
CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON;
270
271
cvSaveMemStoragePos( storage, &(scanner->initial_pos) );
272
273
if( method > CV_CHAIN_APPROX_SIMPLE )
274
{
275
scanner->storage1 = cvCreateChildMemStorage( scanner->storage2 );
276
}
277
278
if( mode > CV_RETR_LIST )
279
{
280
scanner->cinfo_storage = cvCreateChildMemStorage( scanner->storage2 );
281
scanner->cinfo_set = cvCreateSet( 0, sizeof( CvSet ), sizeof( _CvContourInfo ),
282
scanner->cinfo_storage );
283
}
284
285
CV_Assert(step >= 0);
286
CV_Assert(size.height >= 1);
287
288
/* make zero borders */
289
if(needFillBorder)
290
{
291
int esz = CV_ELEM_SIZE(mat->type);
292
memset( img, 0, size.width*esz );
293
memset( img + static_cast<size_t>(step) * (size.height - 1), 0, size.width*esz );
294
295
img += step;
296
for( int y = 1; y < size.height - 1; y++, img += step )
297
{
298
for( int k = 0; k < esz; k++ )
299
img[k] = img[(size.width - 1)*esz + k] = (schar)0;
300
}
301
}
302
303
/* converts all pixels to 0 or 1 */
304
if( CV_MAT_TYPE(mat->type) != CV_32S )
305
cvThreshold( mat, mat, 0, 1, CV_THRESH_BINARY );
306
307
return scanner;
308
}
309
310
CV_IMPL CvContourScanner
311
cvStartFindContours( void* _img, CvMemStorage* storage,
312
int header_size, int mode,
313
int method, CvPoint offset )
314
{
315
return cvStartFindContours_Impl(_img, storage, header_size, mode, method, offset, 1);
316
}
317
318
/*
319
Final stage of contour processing.
320
Three variants possible:
321
1. Contour, which was retrieved using border following, is added to
322
the contour tree. It is the case when the icvSubstituteContour function
323
was not called after retrieving the contour.
324
325
2. New contour, assigned by icvSubstituteContour function, is added to the
326
tree. The retrieved contour itself is removed from the storage.
327
Here two cases are possible:
328
2a. If one deals with plane variant of algorithm
329
(hierarchical structure is not reconstructed),
330
the contour is removed completely.
331
2b. In hierarchical case, the header of the contour is not removed.
332
It's marked as "link to contour" and h_next pointer of it is set to
333
new, substituting contour.
334
335
3. The similar to 2, but when NULL pointer was assigned by
336
icvSubstituteContour function. In this case, the function removes
337
retrieved contour completely if plane case and
338
leaves header if hierarchical (but doesn't mark header as "link").
339
------------------------------------------------------------------------
340
The 1st variant can be used to retrieve and store all the contours from the image
341
(with optional conversion from chains to contours using some approximation from
342
restricted set of methods). Some characteristics of contour can be computed in the
343
same pass.
344
345
The usage scheme can look like:
346
347
icvContourScanner scanner;
348
CvMemStorage* contour_storage;
349
CvSeq* first_contour;
350
CvStatus result;
351
352
...
353
354
icvCreateMemStorage( &contour_storage, block_size/0 );
355
356
...
357
358
cvStartFindContours
359
( img, contour_storage,
360
header_size, approx_method,
361
[external_only,]
362
&scanner );
363
364
for(;;)
365
{
366
[CvSeq* contour;]
367
result = icvFindNextContour( &scanner, &contour/0 );
368
369
if( result != CV_OK ) break;
370
371
// calculate some characteristics
372
...
373
}
374
375
if( result < 0 ) goto error_processing;
376
377
cvEndFindContours( &scanner, &first_contour );
378
...
379
380
-----------------------------------------------------------------
381
382
Second variant is more complex and can be used when someone wants store not
383
the retrieved contours but transformed ones. (e.g. approximated with some
384
non-default algorithm ).
385
386
The scheme can be the as following:
387
388
icvContourScanner scanner;
389
CvMemStorage* contour_storage;
390
CvMemStorage* temp_storage;
391
CvSeq* first_contour;
392
CvStatus result;
393
394
...
395
396
icvCreateMemStorage( &contour_storage, block_size/0 );
397
icvCreateMemStorage( &temp_storage, block_size/0 );
398
399
...
400
401
icvStartFindContours8uC1R
402
( <img_params>, temp_storage,
403
header_size, approx_method,
404
[retrival_mode],
405
&scanner );
406
407
for(;;)
408
{
409
CvSeq* temp_contour;
410
CvSeq* new_contour;
411
result = icvFindNextContour( scanner, &temp_contour );
412
413
if( result != CV_OK ) break;
414
415
<approximation_function>( temp_contour, contour_storage,
416
&new_contour, <parameters...> );
417
418
icvSubstituteContour( scanner, new_contour );
419
...
420
}
421
422
if( result < 0 ) goto error_processing;
423
424
cvEndFindContours( &scanner, &first_contour );
425
...
426
427
----------------------------------------------------------------------------
428
Third method to retrieve contours may be applied if contours are irrelevant
429
themselves but some characteristics of them are used only.
430
The usage is similar to second except slightly different internal loop
431
432
for(;;)
433
{
434
CvSeq* temp_contour;
435
result = icvFindNextContour( &scanner, &temp_contour );
436
437
if( result != CV_OK ) break;
438
439
// calculate some characteristics of temp_contour
440
441
icvSubstituteContour( scanner, 0 );
442
...
443
}
444
445
new_storage variable is not needed here.
446
447
Note, that the second and the third methods can interleave. I.e. it is possible to
448
retain contours that satisfy with some criteria and reject others.
449
In hierarchic case the resulting tree is the part of original tree with
450
some nodes absent. But in the resulting tree the contour1 is a child
451
(may be indirect) of contour2 iff in the original tree the contour1
452
is a child (may be indirect) of contour2.
453
*/
454
static void
455
icvEndProcessContour( CvContourScanner scanner )
456
{
457
_CvContourInfo *l_cinfo = scanner->l_cinfo;
458
459
if( l_cinfo )
460
{
461
if( scanner->subst_flag )
462
{
463
CvMemStoragePos temp;
464
465
cvSaveMemStoragePos( scanner->storage2, &temp );
466
467
if( temp.top == scanner->backup_pos2.top &&
468
temp.free_space == scanner->backup_pos2.free_space )
469
{
470
cvRestoreMemStoragePos( scanner->storage2, &scanner->backup_pos );
471
}
472
scanner->subst_flag = 0;
473
}
474
475
if( l_cinfo->contour )
476
{
477
cvInsertNodeIntoTree( l_cinfo->contour, l_cinfo->parent->contour,
478
&(scanner->frame) );
479
}
480
scanner->l_cinfo = 0;
481
}
482
}
483
484
/* replaces one contour with another */
485
CV_IMPL void
486
cvSubstituteContour( CvContourScanner scanner, CvSeq * new_contour )
487
{
488
_CvContourInfo *l_cinfo;
489
490
if( !scanner )
491
CV_Error( CV_StsNullPtr, "" );
492
493
l_cinfo = scanner->l_cinfo;
494
if( l_cinfo && l_cinfo->contour && l_cinfo->contour != new_contour )
495
{
496
l_cinfo->contour = new_contour;
497
scanner->subst_flag = 1;
498
}
499
}
500
501
static const int MAX_SIZE = 16;
502
503
/*
504
marks domain border with +/-<constant> and stores the contour into CvSeq.
505
method:
506
<0 - chain
507
==0 - direct
508
>0 - simple approximation
509
*/
510
static void
511
icvFetchContour( schar *ptr,
512
int step,
513
CvPoint pt,
514
CvSeq* contour,
515
int _method )
516
{
517
const schar nbd = 2;
518
int deltas[MAX_SIZE];
519
CvSeqWriter writer;
520
schar *i0 = ptr, *i1, *i3, *i4 = 0;
521
int prev_s = -1, s, s_end;
522
int method = _method - 1;
523
524
CV_DbgAssert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
525
526
/* initialize local state */
527
CV_INIT_3X3_DELTAS( deltas, step, 1 );
528
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
529
530
/* initialize writer */
531
cvStartAppendToSeq( contour, &writer );
532
533
if( method < 0 )
534
((CvChain *) contour)->origin = pt;
535
536
s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
537
538
do
539
{
540
s = (s - 1) & 7;
541
i1 = i0 + deltas[s];
542
}
543
while( *i1 == 0 && s != s_end );
544
545
if( s == s_end ) /* single pixel domain */
546
{
547
*i0 = (schar) (nbd | -128);
548
if( method >= 0 )
549
{
550
CV_WRITE_SEQ_ELEM( pt, writer );
551
}
552
}
553
else
554
{
555
i3 = i0;
556
prev_s = s ^ 4;
557
558
/* follow border */
559
for( ;; )
560
{
561
CV_Assert(i3 != NULL);
562
s_end = s;
563
s = std::min(s, MAX_SIZE - 1);
564
565
while( s < MAX_SIZE - 1 )
566
{
567
i4 = i3 + deltas[++s];
568
CV_Assert(i4 != NULL);
569
if( *i4 != 0 )
570
break;
571
}
572
s &= 7;
573
574
/* check "right" bound */
575
if( (unsigned) (s - 1) < (unsigned) s_end )
576
{
577
*i3 = (schar) (nbd | -128);
578
}
579
else if( *i3 == 1 )
580
{
581
*i3 = nbd;
582
}
583
584
if( method < 0 )
585
{
586
schar _s = (schar) s;
587
588
CV_WRITE_SEQ_ELEM( _s, writer );
589
}
590
else
591
{
592
if( s != prev_s || method == 0 )
593
{
594
CV_WRITE_SEQ_ELEM( pt, writer );
595
prev_s = s;
596
}
597
598
pt.x += icvCodeDeltas[s].x;
599
pt.y += icvCodeDeltas[s].y;
600
601
}
602
603
if( i4 == i0 && i3 == i1 )
604
break;
605
606
i3 = i4;
607
s = (s + 4) & 7;
608
} /* end of border following loop */
609
}
610
611
cvEndWriteSeq( &writer );
612
613
if( _method != CV_CHAIN_CODE )
614
cvBoundingRect( contour, 1 );
615
616
CV_DbgAssert( (writer.seq->total == 0 && writer.seq->first == 0) ||
617
writer.seq->total > writer.seq->first->count ||
618
(writer.seq->first->prev == writer.seq->first &&
619
writer.seq->first->next == writer.seq->first) );
620
}
621
622
623
624
/*
625
trace contour until certain point is met.
626
returns 1 if met, 0 else.
627
*/
628
static int
629
icvTraceContour( schar *ptr, int step, schar *stop_ptr, int is_hole )
630
{
631
int deltas[MAX_SIZE];
632
schar *i0 = ptr, *i1, *i3, *i4 = NULL;
633
int s, s_end;
634
635
/* initialize local state */
636
CV_INIT_3X3_DELTAS( deltas, step, 1 );
637
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
638
639
CV_DbgAssert( (*i0 & -2) != 0 );
640
641
s_end = s = is_hole ? 0 : 4;
642
643
do
644
{
645
s = (s - 1) & 7;
646
i1 = i0 + deltas[s];
647
}
648
while( *i1 == 0 && s != s_end );
649
650
i3 = i0;
651
652
/* check single pixel domain */
653
if( s != s_end )
654
{
655
/* follow border */
656
for( ;; )
657
{
658
CV_Assert(i3 != NULL);
659
660
s = std::min(s, MAX_SIZE - 1);
661
while( s < MAX_SIZE - 1 )
662
{
663
i4 = i3 + deltas[++s];
664
CV_Assert(i4 != NULL);
665
if( *i4 != 0 )
666
break;
667
}
668
669
if( i3 == stop_ptr || (i4 == i0 && i3 == i1) )
670
break;
671
672
i3 = i4;
673
s = (s + 4) & 7;
674
} /* end of border following loop */
675
}
676
return i3 == stop_ptr;
677
}
678
679
680
static void
681
icvFetchContourEx( schar* ptr,
682
int step,
683
CvPoint pt,
684
CvSeq* contour,
685
int _method,
686
int nbd,
687
CvRect* _rect )
688
{
689
int deltas[MAX_SIZE];
690
CvSeqWriter writer;
691
schar *i0 = ptr, *i1, *i3, *i4 = NULL;
692
cv::Rect rect;
693
int prev_s = -1, s, s_end;
694
int method = _method - 1;
695
696
CV_DbgAssert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
697
CV_DbgAssert( 1 < nbd && nbd < 128 );
698
699
/* initialize local state */
700
CV_INIT_3X3_DELTAS( deltas, step, 1 );
701
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
702
703
/* initialize writer */
704
cvStartAppendToSeq( contour, &writer );
705
706
if( method < 0 )
707
((CvChain *)contour)->origin = pt;
708
709
rect.x = rect.width = pt.x;
710
rect.y = rect.height = pt.y;
711
712
s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
713
714
do
715
{
716
s = (s - 1) & 7;
717
i1 = i0 + deltas[s];
718
}
719
while( *i1 == 0 && s != s_end );
720
721
if( s == s_end ) /* single pixel domain */
722
{
723
*i0 = (schar) (nbd | 0x80);
724
if( method >= 0 )
725
{
726
CV_WRITE_SEQ_ELEM( pt, writer );
727
}
728
}
729
else
730
{
731
i3 = i0;
732
733
prev_s = s ^ 4;
734
735
/* follow border */
736
for( ;; )
737
{
738
CV_Assert(i3 != NULL);
739
s_end = s;
740
s = std::min(s, MAX_SIZE - 1);
741
742
while( s < MAX_SIZE - 1 )
743
{
744
i4 = i3 + deltas[++s];
745
CV_Assert(i4 != NULL);
746
if( *i4 != 0 )
747
break;
748
}
749
s &= 7;
750
751
/* check "right" bound */
752
if( (unsigned) (s - 1) < (unsigned) s_end )
753
{
754
*i3 = (schar) (nbd | 0x80);
755
}
756
else if( *i3 == 1 )
757
{
758
*i3 = (schar) nbd;
759
}
760
761
if( method < 0 )
762
{
763
schar _s = (schar) s;
764
CV_WRITE_SEQ_ELEM( _s, writer );
765
}
766
else if( s != prev_s || method == 0 )
767
{
768
CV_WRITE_SEQ_ELEM( pt, writer );
769
}
770
771
if( s != prev_s )
772
{
773
/* update bounds */
774
if( pt.x < rect.x )
775
rect.x = pt.x;
776
else if( pt.x > rect.width )
777
rect.width = pt.x;
778
779
if( pt.y < rect.y )
780
rect.y = pt.y;
781
else if( pt.y > rect.height )
782
rect.height = pt.y;
783
}
784
785
prev_s = s;
786
pt.x += icvCodeDeltas[s].x;
787
pt.y += icvCodeDeltas[s].y;
788
789
if( i4 == i0 && i3 == i1 ) break;
790
791
i3 = i4;
792
s = (s + 4) & 7;
793
} /* end of border following loop */
794
}
795
796
rect.width -= rect.x - 1;
797
rect.height -= rect.y - 1;
798
799
cvEndWriteSeq( &writer );
800
801
if( _method != CV_CHAIN_CODE )
802
((CvContour*)contour)->rect = cvRect(rect);
803
804
CV_DbgAssert( (writer.seq->total == 0 && writer.seq->first == 0) ||
805
writer.seq->total > writer.seq->first->count ||
806
(writer.seq->first->prev == writer.seq->first &&
807
writer.seq->first->next == writer.seq->first) );
808
809
if( _rect ) *_rect = cvRect(rect);
810
}
811
812
813
static int
814
icvTraceContour_32s( int *ptr, int step, int *stop_ptr, int is_hole )
815
{
816
CV_Assert(ptr != NULL);
817
int deltas[MAX_SIZE];
818
int *i0 = ptr, *i1, *i3, *i4 = NULL;
819
int s, s_end;
820
const int right_flag = INT_MIN;
821
const int new_flag = (int)((unsigned)INT_MIN >> 1);
822
const int value_mask = ~(right_flag | new_flag);
823
const int ccomp_val = *i0 & value_mask;
824
825
/* initialize local state */
826
CV_INIT_3X3_DELTAS( deltas, step, 1 );
827
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
828
829
s_end = s = is_hole ? 0 : 4;
830
831
do
832
{
833
s = (s - 1) & 7;
834
i1 = i0 + deltas[s];
835
}
836
while( (*i1 & value_mask) != ccomp_val && s != s_end );
837
838
i3 = i0;
839
840
/* check single pixel domain */
841
if( s != s_end )
842
{
843
/* follow border */
844
for( ;; )
845
{
846
CV_Assert(i3 != NULL);
847
s = std::min(s, MAX_SIZE - 1);
848
849
while( s < MAX_SIZE - 1 )
850
{
851
i4 = i3 + deltas[++s];
852
CV_Assert(i4 != NULL);
853
if( (*i4 & value_mask) == ccomp_val )
854
break;
855
}
856
857
if( i3 == stop_ptr || (i4 == i0 && i3 == i1) )
858
break;
859
860
i3 = i4;
861
s = (s + 4) & 7;
862
} /* end of border following loop */
863
}
864
return i3 == stop_ptr;
865
}
866
867
868
static void
869
icvFetchContourEx_32s( int* ptr,
870
int step,
871
CvPoint pt,
872
CvSeq* contour,
873
int _method,
874
CvRect* _rect )
875
{
876
CV_Assert(ptr != NULL);
877
int deltas[MAX_SIZE];
878
CvSeqWriter writer;
879
int *i0 = ptr, *i1, *i3, *i4;
880
cv::Rect rect;
881
int prev_s = -1, s, s_end;
882
int method = _method - 1;
883
const int right_flag = INT_MIN;
884
const int new_flag = (int)((unsigned)INT_MIN >> 1);
885
const int value_mask = ~(right_flag | new_flag);
886
const int ccomp_val = *i0 & value_mask;
887
const int nbd0 = ccomp_val | new_flag;
888
const int nbd1 = nbd0 | right_flag;
889
890
CV_DbgAssert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
891
892
/* initialize local state */
893
CV_INIT_3X3_DELTAS( deltas, step, 1 );
894
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
895
896
/* initialize writer */
897
cvStartAppendToSeq( contour, &writer );
898
899
if( method < 0 )
900
((CvChain *)contour)->origin = pt;
901
902
rect.x = rect.width = pt.x;
903
rect.y = rect.height = pt.y;
904
905
s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
906
907
do
908
{
909
s = (s - 1) & 7;
910
i1 = i0 + deltas[s];
911
}
912
while( (*i1 & value_mask) != ccomp_val && s != s_end && ( s < MAX_SIZE - 1 ) );
913
914
if( s == s_end ) /* single pixel domain */
915
{
916
*i0 = nbd1;
917
if( method >= 0 )
918
{
919
CV_WRITE_SEQ_ELEM( pt, writer );
920
}
921
}
922
else
923
{
924
i3 = i0;
925
prev_s = s ^ 4;
926
927
/* follow border */
928
for( ;; )
929
{
930
CV_Assert(i3 != NULL);
931
s_end = s;
932
933
do
934
{
935
i4 = i3 + deltas[++s];
936
CV_Assert(i4 != NULL);
937
}
938
while( (*i4 & value_mask) != ccomp_val && ( s < MAX_SIZE - 1 ) );
939
s &= 7;
940
941
/* check "right" bound */
942
if( (unsigned) (s - 1) < (unsigned) s_end )
943
{
944
*i3 = nbd1;
945
}
946
else if( *i3 == ccomp_val )
947
{
948
*i3 = nbd0;
949
}
950
951
if( method < 0 )
952
{
953
schar _s = (schar) s;
954
CV_WRITE_SEQ_ELEM( _s, writer );
955
}
956
else if( s != prev_s || method == 0 )
957
{
958
CV_WRITE_SEQ_ELEM( pt, writer );
959
}
960
961
if( s != prev_s )
962
{
963
/* update bounds */
964
if( pt.x < rect.x )
965
rect.x = pt.x;
966
else if( pt.x > rect.width )
967
rect.width = pt.x;
968
969
if( pt.y < rect.y )
970
rect.y = pt.y;
971
else if( pt.y > rect.height )
972
rect.height = pt.y;
973
}
974
975
prev_s = s;
976
pt.x += icvCodeDeltas[s].x;
977
pt.y += icvCodeDeltas[s].y;
978
979
if( i4 == i0 && i3 == i1 ) break;
980
981
i3 = i4;
982
s = (s + 4) & 7;
983
} /* end of border following loop */
984
}
985
986
rect.width -= rect.x - 1;
987
rect.height -= rect.y - 1;
988
989
cvEndWriteSeq( &writer );
990
991
if( _method != CV_CHAIN_CODE )
992
((CvContour*)contour)->rect = cvRect(rect);
993
994
CV_DbgAssert( (writer.seq->total == 0 && writer.seq->first == 0) ||
995
writer.seq->total > writer.seq->first->count ||
996
(writer.seq->first->prev == writer.seq->first &&
997
writer.seq->first->next == writer.seq->first) );
998
999
if (_rect) *_rect = cvRect(rect);
1000
}
1001
1002
1003
CvSeq *
1004
cvFindNextContour( CvContourScanner scanner )
1005
{
1006
if( !scanner )
1007
CV_Error( CV_StsNullPtr, "" );
1008
1009
#if CV_SSE2
1010
bool haveSIMD = cv::checkHardwareSupport(CPU_SSE2);
1011
#endif
1012
1013
CV_Assert(scanner->img_step >= 0);
1014
1015
icvEndProcessContour( scanner );
1016
1017
/* initialize local state */
1018
schar* img0 = scanner->img0;
1019
schar* img = scanner->img;
1020
int step = scanner->img_step;
1021
int step_i = step / sizeof(int);
1022
int x = scanner->pt.x;
1023
int y = scanner->pt.y;
1024
int width = scanner->img_size.width;
1025
int height = scanner->img_size.height;
1026
int mode = scanner->mode;
1027
cv::Point2i lnbd = scanner->lnbd;
1028
int nbd = scanner->nbd;
1029
int prev = img[x - 1];
1030
int new_mask = -2;
1031
1032
if( mode == CV_RETR_FLOODFILL )
1033
{
1034
prev = ((int*)img)[x - 1];
1035
new_mask = INT_MIN / 2;
1036
}
1037
1038
for( ; y < height; y++, img += step )
1039
{
1040
int* img0_i = 0;
1041
int* img_i = 0;
1042
int p = 0;
1043
1044
if( mode == CV_RETR_FLOODFILL )
1045
{
1046
img0_i = (int*)img0;
1047
img_i = (int*)img;
1048
}
1049
1050
for( ; x < width; x++ )
1051
{
1052
if( img_i )
1053
{
1054
for( ; x < width && ((p = img_i[x]) == prev || (p & ~new_mask) == (prev & ~new_mask)); x++ )
1055
prev = p;
1056
}
1057
else
1058
{
1059
#if CV_SSE2
1060
if ((p = img[x]) != prev) {
1061
goto _next_contour;
1062
} else if (haveSIMD) {
1063
1064
__m128i v_prev = _mm_set1_epi8((char)prev);
1065
int v_size = width - 32;
1066
1067
for (; x <= v_size; x += 32) {
1068
__m128i v_p1 = _mm_loadu_si128((const __m128i*)(img + x));
1069
__m128i v_p2 = _mm_loadu_si128((const __m128i*)(img + x + 16));
1070
1071
__m128i v_cmp1 = _mm_cmpeq_epi8(v_p1, v_prev);
1072
__m128i v_cmp2 = _mm_cmpeq_epi8(v_p2, v_prev);
1073
1074
unsigned int mask1 = _mm_movemask_epi8(v_cmp1);
1075
unsigned int mask2 = _mm_movemask_epi8(v_cmp2);
1076
1077
mask1 ^= 0x0000ffff;
1078
mask2 ^= 0x0000ffff;
1079
1080
if (mask1) {
1081
p = img[(x += cv::trailingZeros32(mask1))];
1082
goto _next_contour;
1083
}
1084
1085
if (mask2) {
1086
p = img[(x += cv::trailingZeros32(mask2 << 16))];
1087
goto _next_contour;
1088
}
1089
}
1090
1091
if(x <= width - 16) {
1092
__m128i v_p = _mm_loadu_si128((__m128i*)(img + x));
1093
1094
unsigned int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(v_p, v_prev)) ^ 0x0000ffff;
1095
1096
if (mask) {
1097
p = img[(x += cv::trailingZeros32(mask))];
1098
goto _next_contour;
1099
}
1100
x += 16;
1101
}
1102
}
1103
#endif
1104
for( ; x < width && (p = img[x]) == prev; x++ )
1105
;
1106
}
1107
1108
if( x >= width )
1109
break;
1110
#if CV_SSE2
1111
_next_contour:
1112
#endif
1113
{
1114
_CvContourInfo *par_info = 0;
1115
CvSeq *seq = 0;
1116
int is_hole = 0;
1117
cv::Point2i origin;
1118
1119
/* if not external contour */
1120
if( (!img_i && !(prev == 0 && p == 1)) ||
1121
(img_i && !(((prev & new_mask) != 0 || prev == 0) && (p & new_mask) == 0)) )
1122
{
1123
/* check hole */
1124
if( (!img_i && (p != 0 || prev < 1)) ||
1125
(img_i && ((prev & new_mask) != 0 || (p & new_mask) != 0)))
1126
goto resume_scan;
1127
1128
if( prev & new_mask )
1129
{
1130
lnbd.x = x - 1;
1131
}
1132
is_hole = 1;
1133
}
1134
1135
if( mode == 0 && (is_hole || img0[lnbd.y * static_cast<size_t>(step) + lnbd.x] > 0) )
1136
goto resume_scan;
1137
1138
origin.y = y;
1139
origin.x = x - is_hole;
1140
1141
/* find contour parent */
1142
if( mode <= 1 || (!is_hole && (mode == CV_RETR_CCOMP || mode == CV_RETR_FLOODFILL)) || lnbd.x <= 0 )
1143
{
1144
par_info = &(scanner->frame_info);
1145
}
1146
else
1147
{
1148
int lval = (img0_i ?
1149
img0_i[lnbd.y * static_cast<size_t>(step_i) + lnbd.x] :
1150
(int)img0[lnbd.y * static_cast<size_t>(step) + lnbd.x]) & 0x7f;
1151
_CvContourInfo *cur = scanner->cinfo_table[lval];
1152
1153
/* find the first bounding contour */
1154
while( cur )
1155
{
1156
if( (unsigned) (lnbd.x - cur->rect.x) < (unsigned) cur->rect.width &&
1157
(unsigned) (lnbd.y - cur->rect.y) < (unsigned) cur->rect.height )
1158
{
1159
if( par_info )
1160
{
1161
if( (img0_i &&
1162
icvTraceContour_32s( img0_i + par_info->origin.y * static_cast<size_t>(step_i) +
1163
par_info->origin.x, step_i, img_i + lnbd.x,
1164
par_info->is_hole ) > 0) ||
1165
(!img0_i &&
1166
icvTraceContour( img0 + par_info->origin.y * static_cast<size_t>(step) +
1167
par_info->origin.x, step, img + lnbd.x,
1168
par_info->is_hole ) > 0) )
1169
break;
1170
}
1171
par_info = cur;
1172
}
1173
cur = cur->next;
1174
}
1175
1176
CV_Assert( par_info != 0 );
1177
1178
/* if current contour is a hole and previous contour is a hole or
1179
current contour is external and previous contour is external then
1180
the parent of the contour is the parent of the previous contour else
1181
the parent is the previous contour itself. */
1182
if( par_info->is_hole == is_hole )
1183
{
1184
par_info = par_info->parent;
1185
/* every contour must have a parent
1186
(at least, the frame of the image) */
1187
if( !par_info )
1188
par_info = &(scanner->frame_info);
1189
}
1190
1191
/* hole flag of the parent must differ from the flag of the contour */
1192
assert( par_info->is_hole != is_hole );
1193
if( par_info->contour == 0 ) /* removed contour */
1194
goto resume_scan;
1195
}
1196
1197
lnbd.x = x - is_hole;
1198
1199
cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos) );
1200
1201
seq = cvCreateSeq( scanner->seq_type1, scanner->header_size1,
1202
scanner->elem_size1, scanner->storage1 );
1203
seq->flags |= is_hole ? CV_SEQ_FLAG_HOLE : 0;
1204
1205
/* initialize header */
1206
_CvContourInfo *l_cinfo = 0;
1207
if( mode <= 1 )
1208
{
1209
l_cinfo = &(scanner->cinfo_temp);
1210
icvFetchContour( img + x - is_hole, step,
1211
cvPoint( origin.x + scanner->offset.x,
1212
origin.y + scanner->offset.y),
1213
seq, scanner->approx_method1 );
1214
}
1215
else
1216
{
1217
cvSetAdd(scanner->cinfo_set, 0, (CvSetElem**)&l_cinfo);
1218
CV_Assert(l_cinfo);
1219
int lval;
1220
1221
if( img_i )
1222
{
1223
lval = img_i[x - is_hole] & 127;
1224
icvFetchContourEx_32s(img_i + x - is_hole, step_i,
1225
cvPoint( origin.x + scanner->offset.x,
1226
origin.y + scanner->offset.y),
1227
seq, scanner->approx_method1,
1228
&(l_cinfo->rect) );
1229
}
1230
else
1231
{
1232
lval = nbd;
1233
// change nbd
1234
nbd = (nbd + 1) & 127;
1235
nbd += nbd == 0 ? 3 : 0;
1236
icvFetchContourEx( img + x - is_hole, step,
1237
cvPoint( origin.x + scanner->offset.x,
1238
origin.y + scanner->offset.y),
1239
seq, scanner->approx_method1,
1240
lval, &(l_cinfo->rect) );
1241
}
1242
l_cinfo->rect.x -= scanner->offset.x;
1243
l_cinfo->rect.y -= scanner->offset.y;
1244
1245
l_cinfo->next = scanner->cinfo_table[lval];
1246
scanner->cinfo_table[lval] = l_cinfo;
1247
}
1248
1249
l_cinfo->is_hole = is_hole;
1250
l_cinfo->contour = seq;
1251
l_cinfo->origin = cvPoint(origin);
1252
l_cinfo->parent = par_info;
1253
1254
if( scanner->approx_method1 != scanner->approx_method2 )
1255
{
1256
l_cinfo->contour = icvApproximateChainTC89( (CvChain *) seq,
1257
scanner->header_size2,
1258
scanner->storage2,
1259
scanner->approx_method2 );
1260
cvClearMemStorage( scanner->storage1 );
1261
}
1262
1263
l_cinfo->contour->v_prev = l_cinfo->parent->contour;
1264
1265
if( par_info->contour == 0 )
1266
{
1267
l_cinfo->contour = 0;
1268
if( scanner->storage1 == scanner->storage2 )
1269
{
1270
cvRestoreMemStoragePos( scanner->storage1, &(scanner->backup_pos) );
1271
}
1272
else
1273
{
1274
cvClearMemStorage( scanner->storage1 );
1275
}
1276
p = img[x];
1277
goto resume_scan;
1278
}
1279
1280
cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) );
1281
scanner->l_cinfo = l_cinfo;
1282
scanner->pt.x = !img_i ? x + 1 : x + 1 - is_hole;
1283
scanner->pt.y = y;
1284
scanner->lnbd = cvPoint(lnbd);
1285
scanner->img = (schar *) img;
1286
scanner->nbd = nbd;
1287
return l_cinfo->contour;
1288
}
1289
resume_scan:
1290
{
1291
prev = p;
1292
/* update lnbd */
1293
if( prev & -2 )
1294
{
1295
lnbd.x = x;
1296
}
1297
}
1298
} /* end of loop on x */
1299
1300
lnbd.x = 0;
1301
lnbd.y = y + 1;
1302
x = 1;
1303
prev = 0;
1304
} /* end of loop on y */
1305
1306
return 0;
1307
}
1308
1309
1310
/*
1311
The function add to tree the last retrieved/substituted contour,
1312
releases temp_storage, restores state of dst_storage (if needed), and
1313
returns pointer to root of the contour tree */
1314
CV_IMPL CvSeq *
1315
cvEndFindContours( CvContourScanner * _scanner )
1316
{
1317
CvContourScanner scanner;
1318
CvSeq *first = 0;
1319
1320
if( !_scanner )
1321
CV_Error( CV_StsNullPtr, "" );
1322
scanner = *_scanner;
1323
1324
if( scanner )
1325
{
1326
icvEndProcessContour( scanner );
1327
1328
if( scanner->storage1 != scanner->storage2 )
1329
cvReleaseMemStorage( &(scanner->storage1) );
1330
1331
if( scanner->cinfo_storage )
1332
cvReleaseMemStorage( &(scanner->cinfo_storage) );
1333
1334
first = scanner->frame.v_next;
1335
cvFree( _scanner );
1336
}
1337
1338
return first;
1339
}
1340
1341
1342
#define ICV_SINGLE 0
1343
#define ICV_CONNECTING_ABOVE 1
1344
#define ICV_CONNECTING_BELOW -1
1345
1346
#define CV_GET_WRITTEN_ELEM( writer ) ((writer).ptr - (writer).seq->elem_size)
1347
1348
typedef struct CvLinkedRunPoint
1349
{
1350
struct CvLinkedRunPoint* link;
1351
struct CvLinkedRunPoint* next;
1352
CvPoint pt;
1353
}
1354
CvLinkedRunPoint;
1355
1356
inline int findStartContourPoint(uchar *src_data, CvSize img_size, int j, bool haveSIMD) {
1357
#if CV_SSE2
1358
if (haveSIMD) {
1359
__m128i v_zero = _mm_setzero_si128();
1360
int v_size = img_size.width - 32;
1361
1362
for (; j <= v_size; j += 32) {
1363
__m128i v_p1 = _mm_loadu_si128((const __m128i*)(src_data + j));
1364
__m128i v_p2 = _mm_loadu_si128((const __m128i*)(src_data + j + 16));
1365
1366
__m128i v_cmp1 = _mm_cmpeq_epi8(v_p1, v_zero);
1367
__m128i v_cmp2 = _mm_cmpeq_epi8(v_p2, v_zero);
1368
1369
unsigned int mask1 = _mm_movemask_epi8(v_cmp1);
1370
unsigned int mask2 = _mm_movemask_epi8(v_cmp2);
1371
1372
mask1 ^= 0x0000ffff;
1373
mask2 ^= 0x0000ffff;
1374
1375
if (mask1) {
1376
j += cv::trailingZeros32(mask1);
1377
return j;
1378
}
1379
1380
if (mask2) {
1381
j += cv::trailingZeros32(mask2 << 16);
1382
return j;
1383
}
1384
}
1385
1386
if (j <= img_size.width - 16) {
1387
__m128i v_p = _mm_loadu_si128((const __m128i*)(src_data + j));
1388
1389
unsigned int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(v_p, v_zero)) ^ 0x0000ffff;
1390
1391
if (mask) {
1392
j += cv::trailingZeros32(mask);
1393
return j;
1394
}
1395
j += 16;
1396
}
1397
}
1398
#else
1399
CV_UNUSED(haveSIMD);
1400
#endif
1401
for (; j < img_size.width && !src_data[j]; ++j)
1402
;
1403
return j;
1404
}
1405
1406
inline int findEndContourPoint(uchar *src_data, CvSize img_size, int j, bool haveSIMD) {
1407
#if CV_SSE2
1408
if (j < img_size.width && !src_data[j]) {
1409
return j;
1410
} else if (haveSIMD) {
1411
__m128i v_zero = _mm_setzero_si128();
1412
int v_size = img_size.width - 32;
1413
1414
for (; j <= v_size; j += 32) {
1415
__m128i v_p1 = _mm_loadu_si128((const __m128i*)(src_data + j));
1416
__m128i v_p2 = _mm_loadu_si128((const __m128i*)(src_data + j + 16));
1417
1418
__m128i v_cmp1 = _mm_cmpeq_epi8(v_p1, v_zero);
1419
__m128i v_cmp2 = _mm_cmpeq_epi8(v_p2, v_zero);
1420
1421
unsigned int mask1 = _mm_movemask_epi8(v_cmp1);
1422
unsigned int mask2 = _mm_movemask_epi8(v_cmp2);
1423
1424
if (mask1) {
1425
j += cv::trailingZeros32(mask1);
1426
return j;
1427
}
1428
1429
if (mask2) {
1430
j += cv::trailingZeros32(mask2 << 16);
1431
return j;
1432
}
1433
}
1434
1435
if (j <= img_size.width - 16) {
1436
__m128i v_p = _mm_loadu_si128((const __m128i*)(src_data + j));
1437
1438
unsigned int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(v_p, v_zero));
1439
1440
if (mask) {
1441
j += cv::trailingZeros32(mask);
1442
return j;
1443
}
1444
j += 16;
1445
}
1446
}
1447
#else
1448
CV_UNUSED(haveSIMD);
1449
#endif
1450
for (; j < img_size.width && src_data[j]; ++j)
1451
;
1452
1453
return j;
1454
}
1455
1456
static int
1457
icvFindContoursInInterval( const CvArr* src,
1458
/*int minValue, int maxValue,*/
1459
CvMemStorage* storage,
1460
CvSeq** result,
1461
int contourHeaderSize )
1462
{
1463
int count = 0;
1464
cv::Ptr<CvMemStorage> storage00;
1465
cv::Ptr<CvMemStorage> storage01;
1466
CvSeq* first = 0;
1467
1468
int j, k, n;
1469
1470
uchar* src_data = 0;
1471
int img_step = 0;
1472
cv::Size img_size;
1473
1474
int connect_flag;
1475
int lower_total;
1476
int upper_total;
1477
int all_total;
1478
bool haveSIMD = false;
1479
1480
CvSeq* runs;
1481
CvLinkedRunPoint tmp;
1482
CvLinkedRunPoint* tmp_prev;
1483
CvLinkedRunPoint* upper_line = 0;
1484
CvLinkedRunPoint* lower_line = 0;
1485
CvLinkedRunPoint* last_elem;
1486
1487
CvLinkedRunPoint* upper_run = 0;
1488
CvLinkedRunPoint* lower_run = 0;
1489
CvLinkedRunPoint* prev_point = 0;
1490
1491
CvSeqWriter writer_ext;
1492
CvSeqWriter writer_int;
1493
CvSeqWriter writer;
1494
CvSeqReader reader;
1495
1496
CvSeq* external_contours;
1497
CvSeq* internal_contours;
1498
CvSeq* prev = 0;
1499
1500
if( !storage )
1501
CV_Error( CV_StsNullPtr, "NULL storage pointer" );
1502
1503
if( !result )
1504
CV_Error( CV_StsNullPtr, "NULL double CvSeq pointer" );
1505
1506
if( contourHeaderSize < (int)sizeof(CvContour))
1507
CV_Error( CV_StsBadSize, "Contour header size must be >= sizeof(CvContour)" );
1508
#if CV_SSE2
1509
haveSIMD = cv::checkHardwareSupport(CPU_SSE2);
1510
#endif
1511
storage00.reset(cvCreateChildMemStorage(storage));
1512
storage01.reset(cvCreateChildMemStorage(storage));
1513
1514
CvMat stub, *mat;
1515
1516
mat = cvGetMat( src, &stub );
1517
if( !CV_IS_MASK_ARR(mat))
1518
CV_Error( CV_StsBadArg, "Input array must be 8uC1 or 8sC1" );
1519
src_data = mat->data.ptr;
1520
img_step = mat->step;
1521
img_size = cvGetMatSize(mat);
1522
1523
// Create temporary sequences
1524
runs = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvLinkedRunPoint), storage00 );
1525
cvStartAppendToSeq( runs, &writer );
1526
1527
cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_ext );
1528
cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_int );
1529
1530
tmp_prev = &(tmp);
1531
tmp_prev->next = 0;
1532
tmp_prev->link = 0;
1533
1534
// First line. None of runs is binded
1535
tmp.pt.x = 0;
1536
tmp.pt.y = 0;
1537
CV_WRITE_SEQ_ELEM( tmp, writer );
1538
upper_line = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1539
1540
tmp_prev = upper_line;
1541
for( j = 0; j < img_size.width; )
1542
{
1543
j = findStartContourPoint(src_data, cvSize(img_size), j, haveSIMD);
1544
1545
if( j == img_size.width )
1546
break;
1547
1548
tmp.pt.x = j;
1549
CV_WRITE_SEQ_ELEM( tmp, writer );
1550
tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1551
tmp_prev = tmp_prev->next;
1552
1553
j = findEndContourPoint(src_data, cvSize(img_size), j + 1, haveSIMD);
1554
1555
tmp.pt.x = j - 1;
1556
CV_WRITE_SEQ_ELEM( tmp, writer );
1557
tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1558
tmp_prev->link = tmp_prev->next;
1559
// First point of contour
1560
CV_WRITE_SEQ_ELEM( tmp_prev, writer_ext );
1561
tmp_prev = tmp_prev->next;
1562
}
1563
cvFlushSeqWriter( &writer );
1564
upper_line = upper_line->next;
1565
upper_total = runs->total - 1;
1566
last_elem = tmp_prev;
1567
tmp_prev->next = 0;
1568
1569
for( int i = 1; i < img_size.height; i++ )
1570
{
1571
//------// Find runs in next line
1572
src_data += img_step;
1573
tmp.pt.y = i;
1574
all_total = runs->total;
1575
for( j = 0; j < img_size.width; )
1576
{
1577
j = findStartContourPoint(src_data, cvSize(img_size), j, haveSIMD);
1578
1579
if( j == img_size.width ) break;
1580
1581
tmp.pt.x = j;
1582
CV_WRITE_SEQ_ELEM( tmp, writer );
1583
tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1584
tmp_prev = tmp_prev->next;
1585
1586
j = findEndContourPoint(src_data, cvSize(img_size), j + 1, haveSIMD);
1587
1588
tmp.pt.x = j - 1;
1589
CV_WRITE_SEQ_ELEM( tmp, writer );
1590
tmp_prev = tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1591
}//j
1592
cvFlushSeqWriter( &writer );
1593
lower_line = last_elem->next;
1594
lower_total = runs->total - all_total;
1595
last_elem = tmp_prev;
1596
tmp_prev->next = 0;
1597
//------//
1598
//------// Find links between runs of lower_line and upper_line
1599
upper_run = upper_line;
1600
lower_run = lower_line;
1601
connect_flag = ICV_SINGLE;
1602
1603
for( k = 0, n = 0; k < upper_total/2 && n < lower_total/2; )
1604
{
1605
switch( connect_flag )
1606
{
1607
case ICV_SINGLE:
1608
if( upper_run->next->pt.x < lower_run->next->pt.x )
1609
{
1610
if( upper_run->next->pt.x >= lower_run->pt.x -1 )
1611
{
1612
lower_run->link = upper_run;
1613
connect_flag = ICV_CONNECTING_ABOVE;
1614
prev_point = upper_run->next;
1615
}
1616
else
1617
upper_run->next->link = upper_run;
1618
k++;
1619
upper_run = upper_run->next->next;
1620
}
1621
else
1622
{
1623
if( upper_run->pt.x <= lower_run->next->pt.x +1 )
1624
{
1625
lower_run->link = upper_run;
1626
connect_flag = ICV_CONNECTING_BELOW;
1627
prev_point = lower_run->next;
1628
}
1629
else
1630
{
1631
lower_run->link = lower_run->next;
1632
// First point of contour
1633
CV_WRITE_SEQ_ELEM( lower_run, writer_ext );
1634
}
1635
n++;
1636
lower_run = lower_run->next->next;
1637
}
1638
break;
1639
case ICV_CONNECTING_ABOVE:
1640
if( upper_run->pt.x > lower_run->next->pt.x +1 )
1641
{
1642
prev_point->link = lower_run->next;
1643
connect_flag = ICV_SINGLE;
1644
n++;
1645
lower_run = lower_run->next->next;
1646
}
1647
else
1648
{
1649
prev_point->link = upper_run;
1650
if( upper_run->next->pt.x < lower_run->next->pt.x )
1651
{
1652
k++;
1653
prev_point = upper_run->next;
1654
upper_run = upper_run->next->next;
1655
}
1656
else
1657
{
1658
connect_flag = ICV_CONNECTING_BELOW;
1659
prev_point = lower_run->next;
1660
n++;
1661
lower_run = lower_run->next->next;
1662
}
1663
}
1664
break;
1665
case ICV_CONNECTING_BELOW:
1666
if( lower_run->pt.x > upper_run->next->pt.x +1 )
1667
{
1668
upper_run->next->link = prev_point;
1669
connect_flag = ICV_SINGLE;
1670
k++;
1671
upper_run = upper_run->next->next;
1672
}
1673
else
1674
{
1675
// First point of contour
1676
CV_WRITE_SEQ_ELEM( lower_run, writer_int );
1677
1678
lower_run->link = prev_point;
1679
if( lower_run->next->pt.x < upper_run->next->pt.x )
1680
{
1681
n++;
1682
prev_point = lower_run->next;
1683
lower_run = lower_run->next->next;
1684
}
1685
else
1686
{
1687
connect_flag = ICV_CONNECTING_ABOVE;
1688
k++;
1689
prev_point = upper_run->next;
1690
upper_run = upper_run->next->next;
1691
}
1692
}
1693
break;
1694
}
1695
}// k, n
1696
1697
for( ; n < lower_total/2; n++ )
1698
{
1699
if( connect_flag != ICV_SINGLE )
1700
{
1701
prev_point->link = lower_run->next;
1702
connect_flag = ICV_SINGLE;
1703
lower_run = lower_run->next->next;
1704
continue;
1705
}
1706
lower_run->link = lower_run->next;
1707
1708
//First point of contour
1709
CV_WRITE_SEQ_ELEM( lower_run, writer_ext );
1710
1711
lower_run = lower_run->next->next;
1712
}
1713
1714
for( ; k < upper_total/2; k++ )
1715
{
1716
if( connect_flag != ICV_SINGLE )
1717
{
1718
upper_run->next->link = prev_point;
1719
connect_flag = ICV_SINGLE;
1720
upper_run = upper_run->next->next;
1721
continue;
1722
}
1723
upper_run->next->link = upper_run;
1724
upper_run = upper_run->next->next;
1725
}
1726
upper_line = lower_line;
1727
upper_total = lower_total;
1728
}//i
1729
1730
upper_run = upper_line;
1731
1732
//the last line of image
1733
for( k = 0; k < upper_total/2; k++ )
1734
{
1735
upper_run->next->link = upper_run;
1736
upper_run = upper_run->next->next;
1737
}
1738
1739
//------//
1740
//------//Find end read contours
1741
external_contours = cvEndWriteSeq( &writer_ext );
1742
internal_contours = cvEndWriteSeq( &writer_int );
1743
1744
for( k = 0; k < 2; k++ )
1745
{
1746
CvSeq* contours = k == 0 ? external_contours : internal_contours;
1747
1748
cvStartReadSeq( contours, &reader );
1749
1750
for( j = 0; j < contours->total; j++, count++ )
1751
{
1752
CvLinkedRunPoint* p_temp;
1753
CvLinkedRunPoint* p00;
1754
CvLinkedRunPoint* p01;
1755
CvSeq* contour;
1756
1757
CV_READ_SEQ_ELEM( p00, reader );
1758
p01 = p00;
1759
1760
if( !p00->link )
1761
continue;
1762
1763
cvStartWriteSeq( CV_SEQ_ELTYPE_POINT | CV_SEQ_POLYLINE | CV_SEQ_FLAG_CLOSED,
1764
contourHeaderSize, sizeof(CvPoint), storage, &writer );
1765
do
1766
{
1767
CV_WRITE_SEQ_ELEM( p00->pt, writer );
1768
p_temp = p00;
1769
p00 = p00->link;
1770
p_temp->link = 0;
1771
}
1772
while( p00 != p01 );
1773
1774
contour = cvEndWriteSeq( &writer );
1775
cvBoundingRect( contour, 1 );
1776
1777
if( k != 0 )
1778
contour->flags |= CV_SEQ_FLAG_HOLE;
1779
1780
if( !first )
1781
prev = first = contour;
1782
else
1783
{
1784
contour->h_prev = prev;
1785
prev = prev->h_next = contour;
1786
}
1787
}
1788
}
1789
1790
if( !first )
1791
count = -1;
1792
1793
if( result )
1794
*result = first;
1795
1796
return count;
1797
}
1798
1799
static int
1800
cvFindContours_Impl( void* img, CvMemStorage* storage,
1801
CvSeq** firstContour, int cntHeaderSize,
1802
int mode,
1803
int method, CvPoint offset, int needFillBorder )
1804
{
1805
CvContourScanner scanner = 0;
1806
CvSeq *contour = 0;
1807
int count = -1;
1808
1809
if( !firstContour )
1810
CV_Error( CV_StsNullPtr, "NULL double CvSeq pointer" );
1811
1812
*firstContour = 0;
1813
1814
if( method == CV_LINK_RUNS )
1815
{
1816
if( offset.x != 0 || offset.y != 0 )
1817
CV_Error( CV_StsOutOfRange,
1818
"Nonzero offset is not supported in CV_LINK_RUNS yet" );
1819
1820
count = icvFindContoursInInterval( img, storage, firstContour, cntHeaderSize );
1821
}
1822
else
1823
{
1824
CV_TRY
1825
{
1826
scanner = cvStartFindContours_Impl( img, storage, cntHeaderSize, mode, method, offset,
1827
needFillBorder);
1828
1829
do
1830
{
1831
count++;
1832
contour = cvFindNextContour( scanner );
1833
}
1834
while( contour != 0 );
1835
}
1836
CV_CATCH_ALL
1837
{
1838
if( scanner )
1839
cvEndFindContours(&scanner);
1840
CV_RETHROW();
1841
}
1842
1843
*firstContour = cvEndFindContours( &scanner );
1844
}
1845
1846
return count;
1847
}
1848
1849
/*F///////////////////////////////////////////////////////////////////////////////////////
1850
// Name: cvFindContours
1851
// Purpose:
1852
// Finds all the contours on the bi-level image.
1853
// Context:
1854
// Parameters:
1855
// img - source image.
1856
// Non-zero pixels are considered as 1-pixels
1857
// and zero pixels as 0-pixels.
1858
// step - full width of source image in bytes.
1859
// size - width and height of the image in pixels
1860
// storage - pointer to storage where will the output contours be placed.
1861
// header_size - header size of resulting contours
1862
// mode - mode of contour retrieval.
1863
// method - method of approximation that is applied to contours
1864
// first_contour - pointer to first contour pointer
1865
// Returns:
1866
// CV_OK or error code
1867
// Notes:
1868
//F*/
1869
CV_IMPL int
1870
cvFindContours( void* img, CvMemStorage* storage,
1871
CvSeq** firstContour, int cntHeaderSize,
1872
int mode,
1873
int method, CvPoint offset )
1874
{
1875
return cvFindContours_Impl(img, storage, firstContour, cntHeaderSize, mode, method, offset, 1);
1876
}
1877
1878
void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
1879
OutputArray _hierarchy, int mode, int method, Point offset )
1880
{
1881
CV_INSTRUMENT_REGION();
1882
1883
// Sanity check: output must be of type vector<vector<Point>>
1884
CV_Assert((_contours.kind() == _InputArray::STD_VECTOR_VECTOR || _contours.kind() == _InputArray::STD_VECTOR_MAT ||
1885
_contours.kind() == _InputArray::STD_VECTOR_UMAT));
1886
1887
CV_Assert(_contours.empty() || (_contours.channels() == 2 && _contours.depth() == CV_32S));
1888
1889
Mat image0 = _image.getMat(), image;
1890
Point offset0(0, 0);
1891
if(method != CV_LINK_RUNS)
1892
{
1893
offset0 = Point(-1, -1);
1894
copyMakeBorder(image0, image, 1, 1, 1, 1, BORDER_CONSTANT | BORDER_ISOLATED, Scalar(0));
1895
}
1896
else
1897
{
1898
image = image0;
1899
}
1900
MemStorage storage(cvCreateMemStorage());
1901
CvMat _cimage = cvMat(image);
1902
CvSeq* _ccontours = 0;
1903
if( _hierarchy.needed() )
1904
_hierarchy.clear();
1905
cvFindContours_Impl(&_cimage, storage, &_ccontours, sizeof(CvContour), mode, method, cvPoint(offset0 + offset), 0);
1906
if( !_ccontours )
1907
{
1908
_contours.clear();
1909
return;
1910
}
1911
Seq<CvSeq*> all_contours(cvTreeToNodeSeq( _ccontours, sizeof(CvSeq), storage ));
1912
int i, total = (int)all_contours.size();
1913
_contours.create(total, 1, 0, -1, true);
1914
SeqIterator<CvSeq*> it = all_contours.begin();
1915
for( i = 0; i < total; i++, ++it )
1916
{
1917
CvSeq* c = *it;
1918
((CvContour*)c)->color = (int)i;
1919
_contours.create((int)c->total, 1, CV_32SC2, i, true);
1920
Mat ci = _contours.getMat(i);
1921
CV_Assert( ci.isContinuous() );
1922
cvCvtSeqToArray(c, ci.ptr());
1923
}
1924
1925
if( _hierarchy.needed() )
1926
{
1927
_hierarchy.create(1, total, CV_32SC4, -1, true);
1928
Vec4i* hierarchy = _hierarchy.getMat().ptr<Vec4i>();
1929
1930
it = all_contours.begin();
1931
for( i = 0; i < total; i++, ++it )
1932
{
1933
CvSeq* c = *it;
1934
int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
1935
int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
1936
int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
1937
int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
1938
hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
1939
}
1940
}
1941
}
1942
1943
void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
1944
int mode, int method, Point offset)
1945
{
1946
CV_INSTRUMENT_REGION();
1947
1948
findContours(_image, _contours, noArray(), mode, method, offset);
1949
}
1950
1951
/* End of file. */
1952
1953