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 "_cv.h"
42 
43 /* initializes 8-element array for fast access to 3x3 neighborhood of a pixel */
44 #define  CV_INIT_3X3_DELTAS( deltas, step, nch )            \
45     ((deltas)[0] =  (nch),  (deltas)[1] = -(step) + (nch),  \
46      (deltas)[2] = -(step), (deltas)[3] = -(step) - (nch),  \
47      (deltas)[4] = -(nch),  (deltas)[5] =  (step) - (nch),  \
48      (deltas)[6] =  (step), (deltas)[7] =  (step) + (nch))
49 
50 static const CvPoint icvCodeDeltas[8] =
51     { {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} };
52 
53 CV_IMPL void
cvStartReadChainPoints(CvChain * chain,CvChainPtReader * reader)54 cvStartReadChainPoints( CvChain * chain, CvChainPtReader * reader )
55 {
56     int i;
57 
58     CV_FUNCNAME( "cvStartReadChainPoints" );
59 
60     __BEGIN__;
61 
62     if( !chain || !reader )
63         CV_ERROR( CV_StsNullPtr, "" );
64 
65     if( chain->elem_size != 1 || chain->header_size < (int)sizeof(CvChain))
66         CV_ERROR_FROM_STATUS( CV_BADSIZE_ERR );
67 
68     cvStartReadSeq( (CvSeq *) chain, (CvSeqReader *) reader, 0 );
69     CV_CHECK();
70 
71     reader->pt = chain->origin;
72 
73     for( i = 0; i < 8; i++ )
74     {
75         reader->deltas[i][0] = (schar) icvCodeDeltas[i].x;
76         reader->deltas[i][1] = (schar) icvCodeDeltas[i].y;
77     }
78 
79     __END__;
80 }
81 
82 
83 /* retrieves next point of the chain curve and updates reader */
84 CV_IMPL CvPoint
cvReadChainPoint(CvChainPtReader * reader)85 cvReadChainPoint( CvChainPtReader * reader )
86 {
87     schar *ptr;
88     int code;
89     CvPoint pt = { 0, 0 };
90 
91     CV_FUNCNAME( "cvReadChainPoint" );
92 
93     __BEGIN__;
94 
95     if( !reader )
96         CV_ERROR( CV_StsNullPtr, "" );
97 
98     pt = reader->pt;
99 
100     ptr = reader->ptr;
101     if( ptr )
102     {
103         code = *ptr++;
104 
105         if( ptr >= reader->block_max )
106         {
107             cvChangeSeqBlock( (CvSeqReader *) reader, 1 );
108             ptr = reader->ptr;
109         }
110 
111         reader->ptr = ptr;
112         reader->code = (schar)code;
113         assert( (code & ~7) == 0 );
114         reader->pt.x = pt.x + icvCodeDeltas[code].x;
115         reader->pt.y = pt.y + icvCodeDeltas[code].y;
116     }
117 
118     __END__;
119 
120     return pt;
121 }
122 
123 
124 /****************************************************************************************\
125 *                         Raster->Chain Tree (Suzuki algorithms)                         *
126 \****************************************************************************************/
127 
128 typedef struct _CvContourInfo
129 {
130     int flags;
131     struct _CvContourInfo *next;        /* next contour with the same mark value */
132     struct _CvContourInfo *parent;      /* information about parent contour */
133     CvSeq *contour;             /* corresponding contour (may be 0, if rejected) */
134     CvRect rect;                /* bounding rectangle */
135     CvPoint origin;             /* origin point (where the contour was traced from) */
136     int is_hole;                /* hole flag */
137 }
138 _CvContourInfo;
139 
140 
141 /*
142   Structure that is used for sequental retrieving contours from the image.
143   It supports both hierarchical and plane variants of Suzuki algorithm.
144 */
145 typedef struct _CvContourScanner
146 {
147     CvMemStorage *storage1;     /* contains fetched contours */
148     CvMemStorage *storage2;     /* contains approximated contours
149                                    (!=storage1 if approx_method2 != approx_method1) */
150     CvMemStorage *cinfo_storage;        /* contains _CvContourInfo nodes */
151     CvSet *cinfo_set;           /* set of _CvContourInfo nodes */
152     CvMemStoragePos initial_pos;        /* starting storage pos */
153     CvMemStoragePos backup_pos; /* beginning of the latest approx. contour */
154     CvMemStoragePos backup_pos2;        /* ending of the latest approx. contour */
155     schar *img0;                /* image origin */
156     schar *img;                 /* current image row */
157     int img_step;               /* image step */
158     CvSize img_size;            /* ROI size */
159     CvPoint offset;             /* ROI offset: coordinates, added to each contour point */
160     CvPoint pt;                 /* current scanner position */
161     CvPoint lnbd;               /* position of the last met contour */
162     int nbd;                    /* current mark val */
163     _CvContourInfo *l_cinfo;    /* information about latest approx. contour */
164     _CvContourInfo cinfo_temp;  /* temporary var which is used in simple modes */
165     _CvContourInfo frame_info;  /* information about frame */
166     CvSeq frame;                /* frame itself */
167     int approx_method1;         /* approx method when tracing */
168     int approx_method2;         /* final approx method */
169     int mode;                   /* contour scanning mode:
170                                    0 - external only
171                                    1 - all the contours w/o any hierarchy
172                                    2 - connected components (i.e. two-level structure -
173                                    external contours and holes) */
174     int subst_flag;
175     int seq_type1;              /* type of fetched contours */
176     int header_size1;           /* hdr size of fetched contours */
177     int elem_size1;             /* elem size of fetched contours */
178     int seq_type2;              /*                                       */
179     int header_size2;           /*        the same for approx. contours  */
180     int elem_size2;             /*                                       */
181     _CvContourInfo *cinfo_table[126];
182 }
183 _CvContourScanner;
184 
185 #define _CV_FIND_CONTOURS_FLAGS_EXTERNAL_ONLY    1
186 #define _CV_FIND_CONTOURS_FLAGS_HIERARCHIC       2
187 
188 /*
189    Initializes scanner structure.
190    Prepare image for scanning ( clear borders and convert all pixels to 0-1.
191 */
192 CV_IMPL CvContourScanner
cvStartFindContours(void * _img,CvMemStorage * storage,int header_size,int mode,int method,CvPoint offset)193 cvStartFindContours( void* _img, CvMemStorage* storage,
194                      int  header_size, int mode,
195                      int  method, CvPoint offset )
196 {
197     int y;
198     int step;
199     CvSize size;
200     uchar *img = 0;
201     CvContourScanner scanner = 0;
202     CvMat stub, *mat = (CvMat*)_img;
203 
204     CV_FUNCNAME( "cvStartFindContours" );
205 
206     __BEGIN__;
207 
208     if( !storage )
209         CV_ERROR( CV_StsNullPtr, "" );
210 
211     CV_CALL( mat = cvGetMat( mat, &stub ));
212 
213     if( !CV_IS_MASK_ARR( mat ))
214         CV_ERROR( CV_StsUnsupportedFormat, "[Start]FindContours support only 8uC1 images" );
215 
216     size = cvSize( mat->width, mat->height );
217     step = mat->step;
218     img = (uchar*)(mat->data.ptr);
219 
220     if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS )
221         CV_ERROR_FROM_STATUS( CV_BADRANGE_ERR );
222 
223     if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour )))
224         CV_ERROR_FROM_STATUS( CV_BADSIZE_ERR );
225 
226     scanner = (CvContourScanner)cvAlloc( sizeof( *scanner ));
227     if( !scanner )
228         CV_ERROR_FROM_STATUS( CV_OUTOFMEM_ERR );
229 
230     memset( scanner, 0, sizeof( *scanner ));
231 
232     scanner->storage1 = scanner->storage2 = storage;
233     scanner->img0 = (schar *) img;
234     scanner->img = (schar *) (img + step);
235     scanner->img_step = step;
236     scanner->img_size.width = size.width - 1;   /* exclude rightest column */
237     scanner->img_size.height = size.height - 1; /* exclude bottomost row */
238     scanner->mode = mode;
239     scanner->offset = offset;
240     scanner->pt.x = scanner->pt.y = 1;
241     scanner->lnbd.x = 0;
242     scanner->lnbd.y = 1;
243     scanner->nbd = 2;
244     scanner->mode = (int) mode;
245     scanner->frame_info.contour = &(scanner->frame);
246     scanner->frame_info.is_hole = 1;
247     scanner->frame_info.next = 0;
248     scanner->frame_info.parent = 0;
249     scanner->frame_info.rect = cvRect( 0, 0, size.width, size.height );
250     scanner->l_cinfo = 0;
251     scanner->subst_flag = 0;
252 
253     scanner->frame.flags = CV_SEQ_FLAG_HOLE;
254 
255     scanner->approx_method2 = scanner->approx_method1 = method;
256 
257     if( method == CV_CHAIN_APPROX_TC89_L1 || method == CV_CHAIN_APPROX_TC89_KCOS )
258         scanner->approx_method1 = CV_CHAIN_CODE;
259 
260     if( scanner->approx_method1 == CV_CHAIN_CODE )
261     {
262         scanner->seq_type1 = CV_SEQ_CHAIN_CONTOUR;
263         scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ?
264             header_size : sizeof( CvChain );
265         scanner->elem_size1 = sizeof( char );
266     }
267     else
268     {
269         scanner->seq_type1 = CV_SEQ_POLYGON;
270         scanner->header_size1 = scanner->approx_method1 == scanner->approx_method2 ?
271             header_size : sizeof( CvContour );
272         scanner->elem_size1 = sizeof( CvPoint );
273     }
274 
275     scanner->header_size2 = header_size;
276 
277     if( scanner->approx_method2 == CV_CHAIN_CODE )
278     {
279         scanner->seq_type2 = scanner->seq_type1;
280         scanner->elem_size2 = scanner->elem_size1;
281     }
282     else
283     {
284         scanner->seq_type2 = CV_SEQ_POLYGON;
285         scanner->elem_size2 = sizeof( CvPoint );
286     }
287 
288     scanner->seq_type1 = scanner->approx_method1 == CV_CHAIN_CODE ?
289         CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON;
290 
291     scanner->seq_type2 = scanner->approx_method2 == CV_CHAIN_CODE ?
292         CV_SEQ_CHAIN_CONTOUR : CV_SEQ_POLYGON;
293 
294     cvSaveMemStoragePos( storage, &(scanner->initial_pos) );
295 
296     if( method > CV_CHAIN_APPROX_SIMPLE )
297     {
298         scanner->storage1 = cvCreateChildMemStorage( scanner->storage2 );
299     }
300 
301     if( mode > CV_RETR_LIST )
302     {
303         scanner->cinfo_storage = cvCreateChildMemStorage( scanner->storage2 );
304         scanner->cinfo_set = cvCreateSet( 0, sizeof( CvSet ), sizeof( _CvContourInfo ),
305                                           scanner->cinfo_storage );
306         if( scanner->cinfo_storage == 0 || scanner->cinfo_set == 0 )
307             CV_ERROR_FROM_STATUS( CV_OUTOFMEM_ERR );
308     }
309 
310     /* make zero borders */
311     memset( img, 0, size.width );
312     memset( img + step * (size.height - 1), 0, size.width );
313 
314     for( y = 1, img += step; y < size.height - 1; y++, img += step )
315     {
316         img[0] = img[size.width - 1] = 0;
317     }
318 
319     /* converts all pixels to 0 or 1 */
320     cvThreshold( mat, mat, 0, 1, CV_THRESH_BINARY );
321     CV_CHECK();
322 
323     __END__;
324 
325     if( cvGetErrStatus() < 0 )
326         cvFree( &scanner );
327 
328     return scanner;
329 }
330 
331 /*
332    Final stage of contour processing.
333    Three variants possible:
334       1. Contour, which was retrieved using border following, is added to
335          the contour tree. It is the case when the icvSubstituteContour function
336          was not called after retrieving the contour.
337 
338       2. New contour, assigned by icvSubstituteContour function, is added to the
339          tree. The retrieved contour itself is removed from the storage.
340          Here two cases are possible:
341             2a. If one deals with plane variant of algorithm
342                 (hierarchical strucutre is not reconstructed),
343                 the contour is removed completely.
344             2b. In hierarchical case, the header of the contour is not removed.
345                 It's marked as "link to contour" and h_next pointer of it is set to
346                 new, substituting contour.
347 
348       3. The similar to 2, but when NULL pointer was assigned by
349          icvSubstituteContour function. In this case, the function removes
350          retrieved contour completely if plane case and
351          leaves header if hierarchical (but doesn't mark header as "link").
352       ------------------------------------------------------------------------
353       The 1st variant can be used to retrieve and store all the contours from the image
354       (with optional convertion from chains to contours using some approximation from
355       restriced set of methods). Some characteristics of contour can be computed in the
356       same pass.
357 
358       The usage scheme can look like:
359 
360       icvContourScanner scanner;
361       CvMemStorage*  contour_storage;
362       CvSeq*  first_contour;
363       CvStatus  result;
364 
365       ...
366 
367       icvCreateMemStorage( &contour_storage, block_size/0 );
368 
369       ...
370 
371       cvStartFindContours
372               ( img, contour_storage,
373                 header_size, approx_method,
374                 [external_only,]
375                 &scanner );
376 
377       for(;;)
378       {
379           [CvSeq* contour;]
380           result = icvFindNextContour( &scanner, &contour/0 );
381 
382           if( result != CV_OK ) break;
383 
384           // calculate some characteristics
385           ...
386       }
387 
388       if( result < 0 ) goto error_processing;
389 
390       cvEndFindContours( &scanner, &first_contour );
391       ...
392 
393       -----------------------------------------------------------------
394 
395       Second variant is more complex and can be used when someone wants store not
396       the retrieved contours but transformed ones. (e.g. approximated with some
397       non-default algorithm ).
398 
399       The scheme can be the as following:
400 
401       icvContourScanner scanner;
402       CvMemStorage*  contour_storage;
403       CvMemStorage*  temp_storage;
404       CvSeq*  first_contour;
405       CvStatus  result;
406 
407       ...
408 
409       icvCreateMemStorage( &contour_storage, block_size/0 );
410       icvCreateMemStorage( &temp_storage, block_size/0 );
411 
412       ...
413 
414       icvStartFindContours8uC1R
415               ( <img_params>, temp_storage,
416                 header_size, approx_method,
417                 [retrival_mode],
418                 &scanner );
419 
420       for(;;)
421       {
422           CvSeq* temp_contour;
423           CvSeq* new_contour;
424           result = icvFindNextContour( scanner, &temp_contour );
425 
426           if( result != CV_OK ) break;
427 
428           <approximation_function>( temp_contour, contour_storage,
429                                     &new_contour, <parameters...> );
430 
431           icvSubstituteContour( scanner, new_contour );
432           ...
433       }
434 
435       if( result < 0 ) goto error_processing;
436 
437       cvEndFindContours( &scanner, &first_contour );
438       ...
439 
440       ----------------------------------------------------------------------------
441       Third method to retrieve contours may be applied if contours are irrelevant
442       themselves but some characteristics of them are used only.
443       The usage is similar to second except slightly different internal loop
444 
445       for(;;)
446       {
447           CvSeq* temp_contour;
448           result = icvFindNextContour( &scanner, &temp_contour );
449 
450           if( result != CV_OK ) break;
451 
452           // calculate some characteristics of temp_contour
453 
454           icvSubstituteContour( scanner, 0 );
455           ...
456       }
457 
458       new_storage variable is not needed here.
459 
460       Two notes.
461       1. Second and third method can interleave. I.e. it is possible to
462          remain contours that satisfy with some criteria and reject others.
463          In hierarchic case the resulting tree is the part of original tree with
464          some nodes absent. But in the resulting tree the contour1 is a child
465          (may be indirect) of contour2 iff in the original tree the contour1
466          is a child (may be indirect) of contour2.
467 */
468 static void
icvEndProcessContour(CvContourScanner scanner)469 icvEndProcessContour( CvContourScanner scanner )
470 {
471     _CvContourInfo *l_cinfo = scanner->l_cinfo;
472 
473     if( l_cinfo )
474     {
475         if( scanner->subst_flag )
476         {
477             CvMemStoragePos temp;
478 
479             cvSaveMemStoragePos( scanner->storage2, &temp );
480 
481             if( temp.top == scanner->backup_pos2.top &&
482                 temp.free_space == scanner->backup_pos2.free_space )
483             {
484                 cvRestoreMemStoragePos( scanner->storage2, &scanner->backup_pos );
485             }
486             scanner->subst_flag = 0;
487         }
488 
489         if( l_cinfo->contour )
490         {
491             cvInsertNodeIntoTree( l_cinfo->contour, l_cinfo->parent->contour,
492                                   &(scanner->frame) );
493         }
494         scanner->l_cinfo = 0;
495     }
496 }
497 
498 /* replaces one contour with another */
499 CV_IMPL void
cvSubstituteContour(CvContourScanner scanner,CvSeq * new_contour)500 cvSubstituteContour( CvContourScanner scanner, CvSeq * new_contour )
501 {
502     _CvContourInfo *l_cinfo;
503 
504     CV_FUNCNAME( "cvSubstituteContour" );
505 
506     __BEGIN__;
507 
508     if( !scanner )
509         CV_ERROR( CV_StsNullPtr, "" );
510 
511     l_cinfo = scanner->l_cinfo;
512     if( l_cinfo && l_cinfo->contour && l_cinfo->contour != new_contour )
513     {
514         l_cinfo->contour = new_contour;
515         scanner->subst_flag = 1;
516     }
517 
518     __END__;
519 }
520 
521 
522 /*
523     marks domain border with +/-<constant> and stores the contour into CvSeq.
524         method:
525             <0  - chain
526             ==0 - direct
527             >0  - simple approximation
528 */
529 static CvStatus
icvFetchContour(schar * ptr,int step,CvPoint pt,CvSeq * contour,int _method)530 icvFetchContour( schar                  *ptr,
531                  int                    step,
532                  CvPoint                pt,
533                  CvSeq*                 contour,
534                  int    _method )
535 {
536     const schar     nbd = 2;
537     int             deltas[16];
538     CvSeqWriter     writer;
539     schar           *i0 = ptr, *i1, *i3, *i4 = 0;
540     int             prev_s = -1, s, s_end;
541     int             method = _method - 1;
542 
543     assert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
544 
545     /* initialize local state */
546     CV_INIT_3X3_DELTAS( deltas, step, 1 );
547     memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
548 
549     /* initialize writer */
550     cvStartAppendToSeq( contour, &writer );
551 
552     if( method < 0 )
553         ((CvChain *) contour)->origin = pt;
554 
555     s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
556 
557     do
558     {
559         s = (s - 1) & 7;
560         i1 = i0 + deltas[s];
561         if( *i1 != 0 )
562             break;
563     }
564     while( s != s_end );
565 
566     if( s == s_end )            /* single pixel domain */
567     {
568         *i0 = (schar) (nbd | -128);
569         if( method >= 0 )
570         {
571             CV_WRITE_SEQ_ELEM( pt, writer );
572         }
573     }
574     else
575     {
576         i3 = i0;
577         prev_s = s ^ 4;
578 
579         /* follow border */
580         for( ;; )
581         {
582             s_end = s;
583 
584             for( ;; )
585             {
586                 i4 = i3 + deltas[++s];
587                 if( *i4 != 0 )
588                     break;
589             }
590             s &= 7;
591 
592             /* check "right" bound */
593             if( (unsigned) (s - 1) < (unsigned) s_end )
594             {
595                 *i3 = (schar) (nbd | -128);
596             }
597             else if( *i3 == 1 )
598             {
599                 *i3 = nbd;
600             }
601 
602             if( method < 0 )
603             {
604                 schar _s = (schar) s;
605 
606                 CV_WRITE_SEQ_ELEM( _s, writer );
607             }
608             else
609             {
610                 if( s != prev_s || method == 0 )
611                 {
612                     CV_WRITE_SEQ_ELEM( pt, writer );
613                     prev_s = s;
614                 }
615 
616                 pt.x += icvCodeDeltas[s].x;
617                 pt.y += icvCodeDeltas[s].y;
618 
619             }
620 
621             if( i4 == i0 && i3 == i1 )
622                 break;
623 
624             i3 = i4;
625             s = (s + 4) & 7;
626         }                       /* end of border following loop */
627     }
628 
629     cvEndWriteSeq( &writer );
630 
631     if( _method != CV_CHAIN_CODE )
632         cvBoundingRect( contour, 1 );
633 
634     assert( writer.seq->total == 0 && writer.seq->first == 0 ||
635             writer.seq->total > writer.seq->first->count ||
636             (writer.seq->first->prev == writer.seq->first &&
637              writer.seq->first->next == writer.seq->first) );
638 
639     return CV_OK;
640 }
641 
642 
643 
644 /*
645    trace contour until certain point is met.
646    returns 1 if met, 0 else.
647 */
648 static int
icvTraceContour(schar * ptr,int step,schar * stop_ptr,int is_hole)649 icvTraceContour( schar *ptr, int step, schar *stop_ptr, int is_hole )
650 {
651     int deltas[16];
652     schar *i0 = ptr, *i1, *i3, *i4;
653     int s, s_end;
654 
655     /* initialize local state */
656     CV_INIT_3X3_DELTAS( deltas, step, 1 );
657     memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
658 
659     assert( (*i0 & -2) != 0 );
660 
661     s_end = s = is_hole ? 0 : 4;
662 
663     do
664     {
665         s = (s - 1) & 7;
666         i1 = i0 + deltas[s];
667         if( *i1 != 0 )
668             break;
669     }
670     while( s != s_end );
671 
672     i3 = i0;
673 
674     /* check single pixel domain */
675     if( s != s_end )
676     {
677         /* follow border */
678         for( ;; )
679         {
680             s_end = s;
681 
682             for( ;; )
683             {
684                 i4 = i3 + deltas[++s];
685                 if( *i4 != 0 )
686                     break;
687             }
688 
689             if( i3 == stop_ptr || (i4 == i0 && i3 == i1) )
690                 break;
691 
692             i3 = i4;
693             s = (s + 4) & 7;
694         }                       /* end of border following loop */
695     }
696     return i3 == stop_ptr;
697 }
698 
699 
700 static CvStatus
icvFetchContourEx(schar * ptr,int step,CvPoint pt,CvSeq * contour,int _method,int nbd,CvRect * _rect)701 icvFetchContourEx( schar*               ptr,
702                    int                  step,
703                    CvPoint              pt,
704                    CvSeq*               contour,
705                    int  _method,
706                    int                  nbd,
707                    CvRect*              _rect )
708 {
709     int         deltas[16];
710     CvSeqWriter writer;
711     schar        *i0 = ptr, *i1, *i3, *i4;
712     CvRect      rect;
713     int         prev_s = -1, s, s_end;
714     int         method = _method - 1;
715 
716     assert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
717     assert( 1 < nbd && nbd < 128 );
718 
719     /* initialize local state */
720     CV_INIT_3X3_DELTAS( deltas, step, 1 );
721     memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
722 
723     /* initialize writer */
724     cvStartAppendToSeq( contour, &writer );
725 
726     if( method < 0 )
727         ((CvChain *)contour)->origin = pt;
728 
729     rect.x = rect.width = pt.x;
730     rect.y = rect.height = pt.y;
731 
732     s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
733 
734     do
735     {
736         s = (s - 1) & 7;
737         i1 = i0 + deltas[s];
738         if( *i1 != 0 )
739             break;
740     }
741     while( s != s_end );
742 
743     if( s == s_end )            /* single pixel domain */
744     {
745         *i0 = (schar) (nbd | 0x80);
746         if( method >= 0 )
747         {
748             CV_WRITE_SEQ_ELEM( pt, writer );
749         }
750     }
751     else
752     {
753         i3 = i0;
754 
755         prev_s = s ^ 4;
756 
757         /* follow border */
758         for( ;; )
759         {
760             s_end = s;
761 
762             for( ;; )
763             {
764                 i4 = i3 + deltas[++s];
765                 if( *i4 != 0 )
766                     break;
767             }
768             s &= 7;
769 
770             /* check "right" bound */
771             if( (unsigned) (s - 1) < (unsigned) s_end )
772             {
773                 *i3 = (schar) (nbd | 0x80);
774             }
775             else if( *i3 == 1 )
776             {
777                 *i3 = (schar) nbd;
778             }
779 
780             if( method < 0 )
781             {
782                 schar _s = (schar) s;
783                 CV_WRITE_SEQ_ELEM( _s, writer );
784             }
785             else if( s != prev_s || method == 0 )
786             {
787                 CV_WRITE_SEQ_ELEM( pt, writer );
788             }
789 
790             if( s != prev_s )
791             {
792                 /* update bounds */
793                 if( pt.x < rect.x )
794                     rect.x = pt.x;
795                 else if( pt.x > rect.width )
796                     rect.width = pt.x;
797 
798                 if( pt.y < rect.y )
799                     rect.y = pt.y;
800                 else if( pt.y > rect.height )
801                     rect.height = pt.y;
802             }
803 
804             prev_s = s;
805             pt.x += icvCodeDeltas[s].x;
806             pt.y += icvCodeDeltas[s].y;
807 
808             if( i4 == i0 && i3 == i1 )  break;
809 
810             i3 = i4;
811             s = (s + 4) & 7;
812         }                       /* end of border following loop */
813     }
814 
815     rect.width -= rect.x - 1;
816     rect.height -= rect.y - 1;
817 
818     cvEndWriteSeq( &writer );
819 
820     if( _method != CV_CHAIN_CODE )
821         ((CvContour*)contour)->rect = rect;
822 
823     assert( writer.seq->total == 0 && writer.seq->first == 0 ||
824             writer.seq->total > writer.seq->first->count ||
825             (writer.seq->first->prev == writer.seq->first &&
826              writer.seq->first->next == writer.seq->first) );
827 
828     if( _rect )  *_rect = rect;
829 
830     return CV_OK;
831 }
832 
833 
834 CvSeq *
cvFindNextContour(CvContourScanner scanner)835 cvFindNextContour( CvContourScanner scanner )
836 {
837     schar *img0;
838     schar *img;
839     int step;
840     int width, height;
841     int x, y;
842     int prev;
843     CvPoint lnbd;
844     CvSeq *contour = 0;
845     int nbd;
846     int mode;
847     CvStatus result = (CvStatus) 1;
848 
849     CV_FUNCNAME( "cvFindNextContour" );
850 
851     __BEGIN__;
852 
853     if( !scanner )
854         CV_ERROR( CV_StsNullPtr, "" );
855     icvEndProcessContour( scanner );
856 
857     /* initialize local state */
858     img0 = scanner->img0;
859     img = scanner->img;
860     step = scanner->img_step;
861     x = scanner->pt.x;
862     y = scanner->pt.y;
863     width = scanner->img_size.width;
864     height = scanner->img_size.height;
865     mode = scanner->mode;
866     lnbd = scanner->lnbd;
867     nbd = scanner->nbd;
868 
869     prev = img[x - 1];
870 
871     for( ; y < height; y++, img += step )
872     {
873         for( ; x < width; x++ )
874         {
875             int p = img[x];
876 
877             if( p != prev )
878             {
879                 _CvContourInfo *par_info = 0;
880                 _CvContourInfo *l_cinfo = 0;
881                 CvSeq *seq = 0;
882                 int is_hole = 0;
883                 CvPoint origin;
884 
885                 if( !(prev == 0 && p == 1) )    /* if not external contour */
886                 {
887                     /* check hole */
888                     if( p != 0 || prev < 1 )
889                         goto resume_scan;
890 
891                     if( prev & -2 )
892                     {
893                         lnbd.x = x - 1;
894                     }
895                     is_hole = 1;
896                 }
897 
898                 if( mode == 0 && (is_hole || img0[lnbd.y * step + lnbd.x] > 0) )
899                     goto resume_scan;
900 
901                 origin.y = y;
902                 origin.x = x - is_hole;
903 
904                 /* find contour parent */
905                 if( mode <= 1 || (!is_hole && mode == 2) || lnbd.x <= 0 )
906                 {
907                     par_info = &(scanner->frame_info);
908                 }
909                 else
910                 {
911                     int lval = img0[lnbd.y * step + lnbd.x] & 0x7f;
912                     _CvContourInfo *cur = scanner->cinfo_table[lval - 2];
913 
914                     assert( lval >= 2 );
915 
916                     /* find the first bounding contour */
917                     while( cur )
918                     {
919                         if( (unsigned) (lnbd.x - cur->rect.x) < (unsigned) cur->rect.width &&
920                             (unsigned) (lnbd.y - cur->rect.y) < (unsigned) cur->rect.height )
921                         {
922                             if( par_info )
923                             {
924                                 if( icvTraceContour( scanner->img0 +
925                                                      par_info->origin.y * step +
926                                                      par_info->origin.x, step, img + lnbd.x,
927                                                      par_info->is_hole ) > 0 )
928                                     break;
929                             }
930                             par_info = cur;
931                         }
932                         cur = cur->next;
933                     }
934 
935                     assert( par_info != 0 );
936 
937                     /* if current contour is a hole and previous contour is a hole or
938                        current contour is external and previous contour is external then
939                        the parent of the contour is the parent of the previous contour else
940                        the parent is the previous contour itself. */
941                     if( par_info->is_hole == is_hole )
942                     {
943                         par_info = par_info->parent;
944                         /* every contour must have a parent
945                            (at least, the frame of the image) */
946                         if( !par_info )
947                             par_info = &(scanner->frame_info);
948                     }
949 
950                     /* hole flag of the parent must differ from the flag of the contour */
951                     assert( par_info->is_hole != is_hole );
952                     if( par_info->contour == 0 )        /* removed contour */
953                         goto resume_scan;
954                 }
955 
956                 lnbd.x = x - is_hole;
957 
958                 cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos) );
959 
960                 seq = cvCreateSeq( scanner->seq_type1, scanner->header_size1,
961                                    scanner->elem_size1, scanner->storage1 );
962                 if( !seq )
963                 {
964                     result = CV_OUTOFMEM_ERR;
965                     goto exit_func;
966                 }
967                 seq->flags |= is_hole ? CV_SEQ_FLAG_HOLE : 0;
968 
969                 /* initialize header */
970                 if( mode <= 1 )
971                 {
972                     l_cinfo = &(scanner->cinfo_temp);
973                     result = icvFetchContour( img + x - is_hole, step,
974                                               cvPoint( origin.x + scanner->offset.x,
975                                                        origin.y + scanner->offset.y),
976                                               seq, scanner->approx_method1 );
977                     if( result < 0 )
978                         goto exit_func;
979                 }
980                 else
981                 {
982                     union { _CvContourInfo* ci; CvSetElem* se; } v;
983                     v.ci = l_cinfo;
984                     cvSetAdd( scanner->cinfo_set, 0, &v.se );
985                     l_cinfo = v.ci;
986 
987                     result = icvFetchContourEx( img + x - is_hole, step,
988                                                 cvPoint( origin.x + scanner->offset.x,
989                                                          origin.y + scanner->offset.y),
990                                                 seq, scanner->approx_method1,
991                                                 nbd, &(l_cinfo->rect) );
992                     if( result < 0 )
993                         goto exit_func;
994                     l_cinfo->rect.x -= scanner->offset.x;
995                     l_cinfo->rect.y -= scanner->offset.y;
996 
997                     l_cinfo->next = scanner->cinfo_table[nbd - 2];
998                     scanner->cinfo_table[nbd - 2] = l_cinfo;
999 
1000                     /* change nbd */
1001                     nbd = (nbd + 1) & 127;
1002                     nbd += nbd == 0 ? 3 : 0;
1003                 }
1004 
1005                 l_cinfo->is_hole = is_hole;
1006                 l_cinfo->contour = seq;
1007                 l_cinfo->origin = origin;
1008                 l_cinfo->parent = par_info;
1009 
1010                 if( scanner->approx_method1 != scanner->approx_method2 )
1011                 {
1012                     result = icvApproximateChainTC89( (CvChain *) seq,
1013                                                       scanner->header_size2,
1014                                                       scanner->storage2,
1015                                                       &(l_cinfo->contour),
1016                                                       scanner->approx_method2 );
1017                     if( result < 0 )
1018                         goto exit_func;
1019                     cvClearMemStorage( scanner->storage1 );
1020                 }
1021 
1022                 l_cinfo->contour->v_prev = l_cinfo->parent->contour;
1023 
1024                 if( par_info->contour == 0 )
1025                 {
1026                     l_cinfo->contour = 0;
1027                     if( scanner->storage1 == scanner->storage2 )
1028                     {
1029                         cvRestoreMemStoragePos( scanner->storage1, &(scanner->backup_pos) );
1030                     }
1031                     else
1032                     {
1033                         cvClearMemStorage( scanner->storage1 );
1034                     }
1035                     p = img[x];
1036                     goto resume_scan;
1037                 }
1038 
1039                 cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) );
1040                 scanner->l_cinfo = l_cinfo;
1041                 scanner->pt.x = x + 1;
1042                 scanner->pt.y = y;
1043                 scanner->lnbd = lnbd;
1044                 scanner->img = (schar *) img;
1045                 scanner->nbd = nbd;
1046                 contour = l_cinfo->contour;
1047 
1048                 result = CV_OK;
1049                 goto exit_func;
1050               resume_scan:
1051                 prev = p;
1052                 /* update lnbd */
1053                 if( prev & -2 )
1054                 {
1055                     lnbd.x = x;
1056                 }
1057             }                   /* end of prev != p */
1058         }                       /* end of loop on x */
1059 
1060         lnbd.x = 0;
1061         lnbd.y = y + 1;
1062         x = 1;
1063         prev = 0;
1064 
1065     }                           /* end of loop on y */
1066 
1067   exit_func:
1068 
1069     if( result != 0 )
1070         contour = 0;
1071     if( result < 0 )
1072         CV_ERROR_FROM_STATUS( result );
1073 
1074     __END__;
1075 
1076     return contour;
1077 }
1078 
1079 
1080 /*
1081    The function add to tree the last retrieved/substituted contour,
1082    releases temp_storage, restores state of dst_storage (if needed), and
1083    returns pointer to root of the contour tree */
1084 CV_IMPL CvSeq *
cvEndFindContours(CvContourScanner * _scanner)1085 cvEndFindContours( CvContourScanner * _scanner )
1086 {
1087     CvContourScanner scanner;
1088     CvSeq *first = 0;
1089 
1090     CV_FUNCNAME( "cvFindNextContour" );
1091 
1092     __BEGIN__;
1093 
1094     if( !_scanner )
1095         CV_ERROR( CV_StsNullPtr, "" );
1096     scanner = *_scanner;
1097 
1098     if( scanner )
1099     {
1100         icvEndProcessContour( scanner );
1101 
1102         if( scanner->storage1 != scanner->storage2 )
1103             cvReleaseMemStorage( &(scanner->storage1) );
1104 
1105         if( scanner->cinfo_storage )
1106             cvReleaseMemStorage( &(scanner->cinfo_storage) );
1107 
1108         first = scanner->frame.v_next;
1109         cvFree( _scanner );
1110     }
1111 
1112     __END__;
1113 
1114     return first;
1115 }
1116 
1117 
1118 #define ICV_SINGLE                  0
1119 #define ICV_CONNECTING_ABOVE        1
1120 #define ICV_CONNECTING_BELOW        -1
1121 #define ICV_IS_COMPONENT_POINT(val) ((val) != 0)
1122 
1123 #define CV_GET_WRITTEN_ELEM( writer ) ((writer).ptr - (writer).seq->elem_size)
1124 
1125 typedef  struct CvLinkedRunPoint
1126 {
1127     struct CvLinkedRunPoint* link;
1128     struct CvLinkedRunPoint* next;
1129     CvPoint pt;
1130 }
1131 CvLinkedRunPoint;
1132 
1133 
1134 static int
icvFindContoursInInterval(const CvArr * src,CvMemStorage * storage,CvSeq ** result,int contourHeaderSize)1135 icvFindContoursInInterval( const CvArr* src,
1136                            /*int minValue, int maxValue,*/
1137                            CvMemStorage* storage,
1138                            CvSeq** result,
1139                            int contourHeaderSize )
1140 {
1141     int count = 0;
1142     CvMemStorage* storage00 = 0;
1143     CvMemStorage* storage01 = 0;
1144     CvSeq* first = 0;
1145 
1146     CV_FUNCNAME( "icvFindContoursInInterval" );
1147 
1148     __BEGIN__;
1149 
1150     int i, j, k, n;
1151 
1152     uchar*  src_data = 0;
1153     int  img_step = 0;
1154     CvSize  img_size;
1155 
1156     int  connect_flag;
1157     int  lower_total;
1158     int  upper_total;
1159     int  all_total;
1160 
1161     CvSeq*  runs;
1162     CvLinkedRunPoint  tmp;
1163     CvLinkedRunPoint*  tmp_prev;
1164     CvLinkedRunPoint*  upper_line = 0;
1165     CvLinkedRunPoint*  lower_line = 0;
1166     CvLinkedRunPoint*  last_elem;
1167 
1168     CvLinkedRunPoint*  upper_run = 0;
1169     CvLinkedRunPoint*  lower_run = 0;
1170     CvLinkedRunPoint*  prev_point = 0;
1171 
1172     CvSeqWriter  writer_ext;
1173     CvSeqWriter  writer_int;
1174     CvSeqWriter  writer;
1175     CvSeqReader  reader;
1176 
1177     CvSeq* external_contours;
1178     CvSeq* internal_contours;
1179     CvSeq* prev = 0;
1180 
1181     if( !storage )
1182         CV_ERROR( CV_StsNullPtr, "NULL storage pointer" );
1183 
1184     if( !result )
1185         CV_ERROR( CV_StsNullPtr, "NULL double CvSeq pointer" );
1186 
1187     if( contourHeaderSize < (int)sizeof(CvContour))
1188         CV_ERROR( CV_StsBadSize, "Contour header size must be >= sizeof(CvContour)" );
1189 
1190     CV_CALL( storage00 = cvCreateChildMemStorage(storage));
1191     CV_CALL( storage01 = cvCreateChildMemStorage(storage));
1192 
1193     {
1194         CvMat stub, *mat;
1195 
1196         CV_CALL( mat = cvGetMat( src, &stub ));
1197         if( !CV_IS_MASK_ARR(mat))
1198             CV_ERROR( CV_StsBadArg, "Input array must be 8uC1 or 8sC1" );
1199         src_data = mat->data.ptr;
1200         img_step = mat->step;
1201         img_size = cvGetMatSize( mat );
1202     }
1203 
1204     // Create temporary sequences
1205     runs = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvLinkedRunPoint), storage00 );
1206     cvStartAppendToSeq( runs, &writer );
1207 
1208     cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_ext );
1209     cvStartWriteSeq( 0, sizeof(CvSeq), sizeof(CvLinkedRunPoint*), storage01, &writer_int );
1210 
1211     tmp_prev = &(tmp);
1212     tmp_prev->next = 0;
1213     tmp_prev->link = 0;
1214 
1215     // First line. None of runs is binded
1216     tmp.pt.y = 0;
1217     i = 0;
1218     CV_WRITE_SEQ_ELEM( tmp, writer );
1219     upper_line = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1220 
1221     tmp_prev = upper_line;
1222     for( j = 0; j < img_size.width; )
1223     {
1224         for( ; j < img_size.width && !ICV_IS_COMPONENT_POINT(src_data[j]); j++ )
1225             ;
1226         if( j == img_size.width )
1227             break;
1228 
1229         tmp.pt.x = j;
1230         CV_WRITE_SEQ_ELEM( tmp, writer );
1231         tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1232         tmp_prev = tmp_prev->next;
1233 
1234         for( ; j < img_size.width && ICV_IS_COMPONENT_POINT(src_data[j]); j++ )
1235             ;
1236 
1237         tmp.pt.x = j-1;
1238         CV_WRITE_SEQ_ELEM( tmp, writer );
1239         tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1240         tmp_prev->link = tmp_prev->next;
1241         // First point of contour
1242         CV_WRITE_SEQ_ELEM( tmp_prev, writer_ext );
1243         tmp_prev = tmp_prev->next;
1244     }
1245     cvFlushSeqWriter( &writer );
1246     upper_line = upper_line->next;
1247     upper_total = runs->total - 1;
1248     last_elem = tmp_prev;
1249     tmp_prev->next = 0;
1250 
1251     for( i = 1; i < img_size.height; i++ )
1252     {
1253 //------// Find runs in next line
1254         src_data += img_step;
1255         tmp.pt.y = i;
1256         all_total = runs->total;
1257         for( j = 0; j < img_size.width; )
1258         {
1259             for( ; j < img_size.width && !ICV_IS_COMPONENT_POINT(src_data[j]); j++ )
1260                 ;
1261             if( j == img_size.width ) break;
1262 
1263             tmp.pt.x = j;
1264             CV_WRITE_SEQ_ELEM( tmp, writer );
1265             tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1266             tmp_prev = tmp_prev->next;
1267 
1268             for( ; j < img_size.width && ICV_IS_COMPONENT_POINT(src_data[j]); j++ )
1269                 ;
1270 
1271             tmp.pt.x = j-1;
1272             CV_WRITE_SEQ_ELEM( tmp, writer );
1273             tmp_prev = tmp_prev->next = (CvLinkedRunPoint*)CV_GET_WRITTEN_ELEM( writer );
1274         }//j
1275         cvFlushSeqWriter( &writer );
1276         lower_line = last_elem->next;
1277         lower_total = runs->total - all_total;
1278         last_elem = tmp_prev;
1279         tmp_prev->next = 0;
1280 //------//
1281 //------// Find links between runs of lower_line and upper_line
1282         upper_run = upper_line;
1283         lower_run = lower_line;
1284         connect_flag = ICV_SINGLE;
1285 
1286         for( k = 0, n = 0; k < upper_total/2 && n < lower_total/2; )
1287         {
1288             switch( connect_flag )
1289             {
1290             case ICV_SINGLE:
1291                 if( upper_run->next->pt.x < lower_run->next->pt.x )
1292                 {
1293                     if( upper_run->next->pt.x >= lower_run->pt.x  -1 )
1294                     {
1295                         lower_run->link = upper_run;
1296                         connect_flag = ICV_CONNECTING_ABOVE;
1297                         prev_point = upper_run->next;
1298                     }
1299                     else
1300                         upper_run->next->link = upper_run;
1301                     k++;
1302                     upper_run = upper_run->next->next;
1303                 }
1304                 else
1305                 {
1306                     if( upper_run->pt.x <= lower_run->next->pt.x  +1 )
1307                     {
1308                         lower_run->link = upper_run;
1309                         connect_flag = ICV_CONNECTING_BELOW;
1310                         prev_point = lower_run->next;
1311                     }
1312                     else
1313                     {
1314                         lower_run->link = lower_run->next;
1315                         // First point of contour
1316                         CV_WRITE_SEQ_ELEM( lower_run, writer_ext );
1317                     }
1318                     n++;
1319                     lower_run = lower_run->next->next;
1320                 }
1321                 break;
1322             case ICV_CONNECTING_ABOVE:
1323                 if( upper_run->pt.x > lower_run->next->pt.x +1 )
1324                 {
1325                     prev_point->link = lower_run->next;
1326                     connect_flag = ICV_SINGLE;
1327                     n++;
1328                     lower_run = lower_run->next->next;
1329                 }
1330                 else
1331                 {
1332                     prev_point->link = upper_run;
1333                     if( upper_run->next->pt.x < lower_run->next->pt.x )
1334                     {
1335                         k++;
1336                         prev_point = upper_run->next;
1337                         upper_run = upper_run->next->next;
1338                     }
1339                     else
1340                     {
1341                         connect_flag = ICV_CONNECTING_BELOW;
1342                         prev_point = lower_run->next;
1343                         n++;
1344                         lower_run = lower_run->next->next;
1345                     }
1346                 }
1347                 break;
1348             case ICV_CONNECTING_BELOW:
1349                 if( lower_run->pt.x > upper_run->next->pt.x +1 )
1350                 {
1351                     upper_run->next->link = prev_point;
1352                     connect_flag = ICV_SINGLE;
1353                     k++;
1354                     upper_run = upper_run->next->next;
1355                 }
1356                 else
1357                 {
1358                     // First point of contour
1359                     CV_WRITE_SEQ_ELEM( lower_run, writer_int );
1360 
1361                     lower_run->link = prev_point;
1362                     if( lower_run->next->pt.x < upper_run->next->pt.x )
1363                     {
1364                         n++;
1365                         prev_point = lower_run->next;
1366                         lower_run = lower_run->next->next;
1367                     }
1368                     else
1369                     {
1370                         connect_flag = ICV_CONNECTING_ABOVE;
1371                         k++;
1372                         prev_point = upper_run->next;
1373                         upper_run = upper_run->next->next;
1374                     }
1375                 }
1376                 break;
1377             }
1378         }// k, n
1379 
1380         for( ; n < lower_total/2; n++ )
1381         {
1382             if( connect_flag != ICV_SINGLE )
1383             {
1384                 prev_point->link = lower_run->next;
1385                 connect_flag = ICV_SINGLE;
1386                 lower_run = lower_run->next->next;
1387                 continue;
1388             }
1389             lower_run->link = lower_run->next;
1390 
1391             //First point of contour
1392             CV_WRITE_SEQ_ELEM( lower_run, writer_ext );
1393 
1394             lower_run = lower_run->next->next;
1395         }
1396 
1397         for( ; k < upper_total/2; k++ )
1398         {
1399             if( connect_flag != ICV_SINGLE )
1400             {
1401                 upper_run->next->link = prev_point;
1402                 connect_flag = ICV_SINGLE;
1403                 upper_run = upper_run->next->next;
1404                 continue;
1405             }
1406             upper_run->next->link = upper_run;
1407             upper_run = upper_run->next->next;
1408         }
1409         upper_line = lower_line;
1410         upper_total = lower_total;
1411     }//i
1412 
1413     upper_run = upper_line;
1414 
1415     //the last line of image
1416     for( k = 0; k < upper_total/2; k++ )
1417     {
1418         upper_run->next->link = upper_run;
1419         upper_run = upper_run->next->next;
1420     }
1421 
1422 //------//
1423 //------//Find end read contours
1424     external_contours = cvEndWriteSeq( &writer_ext );
1425     internal_contours = cvEndWriteSeq( &writer_int );
1426 
1427     for( k = 0; k < 2; k++ )
1428     {
1429         CvSeq* contours = k == 0 ? external_contours : internal_contours;
1430 
1431         cvStartReadSeq( contours, &reader );
1432 
1433         for( j = 0; j < contours->total; j++, count++ )
1434         {
1435             CvLinkedRunPoint* p_temp;
1436             CvLinkedRunPoint* p00;
1437             CvLinkedRunPoint* p01;
1438             CvSeq* contour;
1439 
1440             CV_READ_SEQ_ELEM( p00, reader );
1441             p01 = p00;
1442 
1443             if( !p00->link )
1444                 continue;
1445 
1446             cvStartWriteSeq( CV_SEQ_ELTYPE_POINT | CV_SEQ_POLYLINE | CV_SEQ_FLAG_CLOSED,
1447                              contourHeaderSize, sizeof(CvPoint), storage, &writer );
1448             do
1449             {
1450                 CV_WRITE_SEQ_ELEM( p00->pt, writer );
1451                 p_temp = p00;
1452                 p00 = p00->link;
1453                 p_temp->link = 0;
1454             }
1455             while( p00 != p01 );
1456 
1457             contour = cvEndWriteSeq( &writer );
1458             cvBoundingRect( contour, 1 );
1459 
1460             if( k != 0 )
1461                 contour->flags |= CV_SEQ_FLAG_HOLE;
1462 
1463             if( !first )
1464                 prev = first = contour;
1465             else
1466             {
1467                 contour->h_prev = prev;
1468                 prev = prev->h_next = contour;
1469             }
1470         }
1471     }
1472 
1473     __END__;
1474 
1475     if( !first )
1476         count = -1;
1477 
1478     if( result )
1479         *result = first;
1480 
1481     cvReleaseMemStorage(&storage00);
1482     cvReleaseMemStorage(&storage01);
1483 
1484     return count;
1485 }
1486 
1487 
1488 
1489 /*F///////////////////////////////////////////////////////////////////////////////////////
1490 //    Name: cvFindContours
1491 //    Purpose:
1492 //      Finds all the contours on the bi-level image.
1493 //    Context:
1494 //    Parameters:
1495 //      img  - source image.
1496 //             Non-zero pixels are considered as 1-pixels
1497 //             and zero pixels as 0-pixels.
1498 //      step - full width of source image in bytes.
1499 //      size - width and height of the image in pixels
1500 //      storage - pointer to storage where will the output contours be placed.
1501 //      header_size - header size of resulting contours
1502 //      mode - mode of contour retrieval.
1503 //      method - method of approximation that is applied to contours
1504 //      first_contour - pointer to first contour pointer
1505 //    Returns:
1506 //      CV_OK or error code
1507 //    Notes:
1508 //F*/
1509 CV_IMPL int
cvFindContours(void * img,CvMemStorage * storage,CvSeq ** firstContour,int cntHeaderSize,int mode,int method,CvPoint offset)1510 cvFindContours( void*  img,  CvMemStorage*  storage,
1511                 CvSeq**  firstContour, int  cntHeaderSize,
1512                 int  mode,
1513                 int  method, CvPoint offset )
1514 {
1515     CvContourScanner scanner = 0;
1516     CvSeq *contour = 0;
1517     int count = -1;
1518 
1519     CV_FUNCNAME( "cvFindContours" );
1520 
1521     __BEGIN__;
1522 
1523     if( !firstContour )
1524         CV_ERROR( CV_StsNullPtr, "NULL double CvSeq pointer" );
1525 
1526     if( method == CV_LINK_RUNS )
1527     {
1528         if( offset.x != 0 || offset.y != 0 )
1529             CV_ERROR( CV_StsOutOfRange,
1530             "Nonzero offset is not supported in CV_LINK_RUNS yet" );
1531 
1532         CV_CALL( count = icvFindContoursInInterval( img, storage,
1533                                     firstContour, cntHeaderSize ));
1534     }
1535     else
1536     {
1537         CV_CALL( scanner = cvStartFindContours( img, storage,
1538                         cntHeaderSize, mode, method, offset ));
1539         assert( scanner );
1540 
1541         do
1542         {
1543             count++;
1544             contour = cvFindNextContour( scanner );
1545         }
1546         while( contour != 0 );
1547 
1548         *firstContour = cvEndFindContours( &scanner );
1549     }
1550 
1551     __END__;
1552 
1553     return count;
1554 }
1555 
1556 
1557 /* End of file. */
1558