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
43/****************************************************************************************\
44*                                  Chain Approximation                                   *
45\****************************************************************************************/
46
47typedef struct _CvPtInfo
48{
49    CvPoint pt;
50    int k;                      /* support region */
51    int s;                      /* curvature value */
52    struct _CvPtInfo *next;
53}
54_CvPtInfo;
55
56
57/* curvature: 0 - 1-curvature, 1 - k-cosine curvature. */
58CvSeq* icvApproximateChainTC89( CvChain* chain, int header_size,
59                                CvMemStorage* storage, int method )
60{
61    static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 };
62
63    cv::AutoBuffer<_CvPtInfo> buf(chain->total + 8);
64
65    _CvPtInfo       temp;
66    _CvPtInfo       *array = buf, *first = 0, *current = 0, *prev_current = 0;
67    int             i, j, i1, i2, s, len;
68    int             count = chain->total;
69
70    CvChainPtReader reader;
71    CvSeqWriter     writer;
72    CvPoint         pt = chain->origin;
73
74    CV_Assert( CV_IS_SEQ_CHAIN_CONTOUR( chain ));
75    CV_Assert( header_size >= (int)sizeof(CvContour) );
76
77    cvStartWriteSeq( (chain->flags & ~CV_SEQ_ELTYPE_MASK) | CV_SEQ_ELTYPE_POINT,
78                     header_size, sizeof( CvPoint ), storage, &writer );
79
80    if( chain->total == 0 )
81    {
82        CV_WRITE_SEQ_ELEM( pt, writer );
83        return cvEndWriteSeq( &writer );
84    }
85
86    cvStartReadChainPoints( chain, &reader );
87
88    temp.next = 0;
89    current = &temp;
90
91    /* Pass 0.
92       Restores all the digital curve points from the chain code.
93       Removes the points (from the resultant polygon)
94       that have zero 1-curvature */
95    for( i = 0; i < count; i++ )
96    {
97        int prev_code = *reader.prev_elem;
98
99        reader.prev_elem = reader.ptr;
100        CV_READ_CHAIN_POINT( pt, reader );
101
102        /* calc 1-curvature */
103        s = abs_diff[reader.code - prev_code + 7];
104
105        if( method <= CV_CHAIN_APPROX_SIMPLE )
106        {
107            if( method == CV_CHAIN_APPROX_NONE || s != 0 )
108            {
109                CV_WRITE_SEQ_ELEM( pt, writer );
110            }
111        }
112        else
113        {
114            if( s != 0 )
115                current = current->next = array + i;
116            array[i].s = s;
117            array[i].pt = pt;
118        }
119    }
120
121    //assert( pt.x == chain->origin.x && pt.y == chain->origin.y );
122
123    if( method <= CV_CHAIN_APPROX_SIMPLE )
124        return cvEndWriteSeq( &writer );
125
126    current->next = 0;
127
128    len = i;
129    current = temp.next;
130
131    assert( current );
132
133    /* Pass 1.
134       Determines support region for all the remained points */
135    do
136    {
137        CvPoint pt0;
138        int k, l = 0, d_num = 0;
139
140        i = (int)(current - array);
141        pt0 = array[i].pt;
142
143        /* determine support region */
144        for( k = 1;; k++ )
145        {
146            int lk, dk_num;
147            int dx, dy;
148            Cv32suf d;
149
150            assert( k <= len );
151
152            /* calc indices */
153            i1 = i - k;
154            i1 += i1 < 0 ? len : 0;
155            i2 = i + k;
156            i2 -= i2 >= len ? len : 0;
157
158            dx = array[i2].pt.x - array[i1].pt.x;
159            dy = array[i2].pt.y - array[i1].pt.y;
160
161            /* distance between p_(i - k) and p_(i + k) */
162            lk = dx * dx + dy * dy;
163
164            /* distance between p_i and the line (p_(i-k), p_(i+k)) */
165            dk_num = (pt0.x - array[i1].pt.x) * dy - (pt0.y - array[i1].pt.y) * dx;
166            d.f = (float) (((double) d_num) * lk - ((double) dk_num) * l);
167
168            if( k > 1 && (l >= lk || ((d_num > 0 && d.i <= 0) || (d_num < 0 && d.i >= 0))))
169                break;
170
171            d_num = dk_num;
172            l = lk;
173        }
174
175        current->k = --k;
176
177        /* determine cosine curvature if it should be used */
178        if( method == CV_CHAIN_APPROX_TC89_KCOS )
179        {
180            /* calc k-cosine curvature */
181            for( j = k, s = 0; j > 0; j-- )
182            {
183                double temp_num;
184                int dx1, dy1, dx2, dy2;
185                Cv32suf sk;
186
187                i1 = i - j;
188                i1 += i1 < 0 ? len : 0;
189                i2 = i + j;
190                i2 -= i2 >= len ? len : 0;
191
192                dx1 = array[i1].pt.x - pt0.x;
193                dy1 = array[i1].pt.y - pt0.y;
194                dx2 = array[i2].pt.x - pt0.x;
195                dy2 = array[i2].pt.y - pt0.y;
196
197                if( (dx1 | dy1) == 0 || (dx2 | dy2) == 0 )
198                    break;
199
200                temp_num = dx1 * dx2 + dy1 * dy2;
201                temp_num =
202                    (float) (temp_num /
203                             sqrt( ((double)dx1 * dx1 + (double)dy1 * dy1) *
204                                   ((double)dx2 * dx2 + (double)dy2 * dy2) ));
205                sk.f = (float) (temp_num + 1.1);
206
207                assert( 0 <= sk.f && sk.f <= 2.2 );
208                if( j < k && sk.i <= s )
209                    break;
210
211                s = sk.i;
212            }
213            current->s = s;
214        }
215        current = current->next;
216    }
217    while( current != 0 );
218
219    prev_current = &temp;
220    current = temp.next;
221
222    /* Pass 2.
223       Performs non-maxima suppression */
224    do
225    {
226        int k2 = current->k >> 1;
227
228        s = current->s;
229        i = (int)(current - array);
230
231        for( j = 1; j <= k2; j++ )
232        {
233            i2 = i - j;
234            i2 += i2 < 0 ? len : 0;
235
236            if( array[i2].s > s )
237                break;
238
239            i2 = i + j;
240            i2 -= i2 >= len ? len : 0;
241
242            if( array[i2].s > s )
243                break;
244        }
245
246        if( j <= k2 )           /* exclude point */
247        {
248            prev_current->next = current->next;
249            current->s = 0;     /* "clear" point */
250        }
251        else
252            prev_current = current;
253        current = current->next;
254    }
255    while( current != 0 );
256
257    /* Pass 3.
258       Removes non-dominant points with 1-length support region */
259    current = temp.next;
260    assert( current );
261    prev_current = &temp;
262
263    do
264    {
265        if( current->k == 1 )
266        {
267            s = current->s;
268            i = (int)(current - array);
269
270            i1 = i - 1;
271            i1 += i1 < 0 ? len : 0;
272
273            i2 = i + 1;
274            i2 -= i2 >= len ? len : 0;
275
276            if( s <= array[i1].s || s <= array[i2].s )
277            {
278                prev_current->next = current->next;
279                current->s = 0;
280            }
281            else
282                prev_current = current;
283        }
284        else
285            prev_current = current;
286        current = current->next;
287    }
288    while( current != 0 );
289
290    if( method == CV_CHAIN_APPROX_TC89_KCOS )
291        goto copy_vect;
292
293    /* Pass 4.
294       Cleans remained couples of points */
295    assert( temp.next );
296
297    if( array[0].s != 0 && array[len - 1].s != 0 )      /* specific case */
298    {
299        for( i1 = 1; i1 < len && array[i1].s != 0; i1++ )
300        {
301            array[i1 - 1].s = 0;
302        }
303        if( i1 == len )
304            goto copy_vect;     /* all points survived */
305        i1--;
306
307        for( i2 = len - 2; i2 > 0 && array[i2].s != 0; i2-- )
308        {
309            array[i2].next = 0;
310            array[i2 + 1].s = 0;
311        }
312        i2++;
313
314        if( i1 == 0 && i2 == len - 1 )  /* only two points */
315        {
316            i1 = (int)(array[0].next - array);
317            array[len] = array[0];      /* move to the end */
318            array[len].next = 0;
319            array[len - 1].next = array + len;
320        }
321        temp.next = array + i1;
322    }
323
324    current = temp.next;
325    first = prev_current = &temp;
326    count = 1;
327
328    /* do last pass */
329    do
330    {
331        if( current->next == 0 || current->next - current != 1 )
332        {
333            if( count >= 2 )
334            {
335                if( count == 2 )
336                {
337                    int s1 = prev_current->s;
338                    int s2 = current->s;
339
340                    if( s1 > s2 || (s1 == s2 && prev_current->k <= current->k) )
341                        /* remove second */
342                        prev_current->next = current->next;
343                    else
344                        /* remove first */
345                        first->next = current;
346                }
347                else
348                    first->next->next = current;
349            }
350            first = current;
351            count = 1;
352        }
353        else
354            count++;
355        prev_current = current;
356        current = current->next;
357    }
358    while( current != 0 );
359
360copy_vect:
361
362    // gather points
363    current = temp.next;
364    assert( current );
365
366    do
367    {
368        CV_WRITE_SEQ_ELEM( current->pt, writer );
369        current = current->next;
370    }
371    while( current != 0 );
372
373    return cvEndWriteSeq( &writer );
374}
375
376
377/*Applies some approximation algorithm to chain-coded contour(s) and
378  converts it/them to polygonal representation */
379CV_IMPL CvSeq*
380cvApproxChains( CvSeq*              src_seq,
381                CvMemStorage*       storage,
382                int                 method,
383                double              /*parameter*/,
384                int                 minimal_perimeter,
385                int                 recursive )
386{
387    CvSeq *prev_contour = 0, *parent = 0;
388    CvSeq *dst_seq = 0;
389
390    if( !src_seq || !storage )
391        CV_Error( CV_StsNullPtr, "" );
392    if( method > CV_CHAIN_APPROX_TC89_KCOS || method <= 0 || minimal_perimeter < 0 )
393        CV_Error( CV_StsOutOfRange, "" );
394
395    while( src_seq != 0 )
396    {
397        int len = src_seq->total;
398
399        if( len >= minimal_perimeter )
400        {
401            CvSeq *contour = 0;
402
403            switch( method )
404            {
405            case CV_CHAIN_APPROX_NONE:
406            case CV_CHAIN_APPROX_SIMPLE:
407            case CV_CHAIN_APPROX_TC89_L1:
408            case CV_CHAIN_APPROX_TC89_KCOS:
409                contour = icvApproximateChainTC89( (CvChain *) src_seq, sizeof( CvContour ), storage, method );
410                break;
411            default:
412                CV_Error( CV_StsOutOfRange, "" );
413            }
414
415            if( contour->total > 0 )
416            {
417                cvBoundingRect( contour, 1 );
418
419                contour->v_prev = parent;
420                contour->h_prev = prev_contour;
421
422                if( prev_contour )
423                    prev_contour->h_next = contour;
424                else if( parent )
425                    parent->v_next = contour;
426                prev_contour = contour;
427                if( !dst_seq )
428                    dst_seq = prev_contour;
429            }
430            else                /* if resultant contour has zero length, skip it */
431            {
432                len = -1;
433            }
434        }
435
436        if( !recursive )
437            break;
438
439        if( src_seq->v_next && len >= minimal_perimeter )
440        {
441            assert( prev_contour != 0 );
442            parent = prev_contour;
443            prev_contour = 0;
444            src_seq = src_seq->v_next;
445        }
446        else
447        {
448            while( src_seq->h_next == 0 )
449            {
450                src_seq = src_seq->v_prev;
451                if( src_seq == 0 )
452                    break;
453                prev_contour = parent;
454                if( parent )
455                    parent = parent->v_prev;
456            }
457            if( src_seq )
458                src_seq = src_seq->h_next;
459        }
460    }
461
462    return dst_seq;
463}
464
465
466/****************************************************************************************\
467*                               Polygonal Approximation                                  *
468\****************************************************************************************/
469
470/* Ramer-Douglas-Peucker algorithm for polygon simplification */
471
472namespace cv
473{
474
475template<typename T> static int
476approxPolyDP_( const Point_<T>* src_contour, int count0, Point_<T>* dst_contour,
477              bool is_closed0, double eps, AutoBuffer<Range>* _stack )
478{
479    #define PUSH_SLICE(slice) \
480        if( top >= stacksz ) \
481        { \
482            _stack->resize(stacksz*3/2); \
483            stack = *_stack; \
484            stacksz = _stack->size(); \
485        } \
486        stack[top++] = slice
487
488    #define READ_PT(pt, pos) \
489        pt = src_contour[pos]; \
490        if( ++pos >= count ) pos = 0
491
492    #define READ_DST_PT(pt, pos) \
493        pt = dst_contour[pos]; \
494        if( ++pos >= count ) pos = 0
495
496    #define WRITE_PT(pt) \
497        dst_contour[new_count++] = pt
498
499    typedef cv::Point_<T> PT;
500    int             init_iters = 3;
501    Range           slice(0, 0), right_slice(0, 0);
502    PT              start_pt((T)-1000000, (T)-1000000), end_pt(0, 0), pt(0,0);
503    int             i = 0, j, pos = 0, wpos, count = count0, new_count=0;
504    int             is_closed = is_closed0;
505    bool            le_eps = false;
506    size_t top = 0, stacksz = _stack->size();
507    Range*          stack = *_stack;
508
509    if( count == 0  )
510        return 0;
511
512    eps *= eps;
513
514    if( !is_closed )
515    {
516        right_slice.start = count;
517        end_pt = src_contour[0];
518        start_pt = src_contour[count-1];
519
520        if( start_pt.x != end_pt.x || start_pt.y != end_pt.y )
521        {
522            slice.start = 0;
523            slice.end = count - 1;
524            PUSH_SLICE(slice);
525        }
526        else
527        {
528            is_closed = 1;
529            init_iters = 1;
530        }
531    }
532
533    if( is_closed )
534    {
535        // 1. Find approximately two farthest points of the contour
536        right_slice.start = 0;
537
538        for( i = 0; i < init_iters; i++ )
539        {
540            double dist, max_dist = 0;
541            pos = (pos + right_slice.start) % count;
542            READ_PT(start_pt, pos);
543
544            for( j = 1; j < count; j++ )
545            {
546                double dx, dy;
547
548                READ_PT(pt, pos);
549                dx = pt.x - start_pt.x;
550                dy = pt.y - start_pt.y;
551
552                dist = dx * dx + dy * dy;
553
554                if( dist > max_dist )
555                {
556                    max_dist = dist;
557                    right_slice.start = j;
558                }
559            }
560
561            le_eps = max_dist <= eps;
562        }
563
564        // 2. initialize the stack
565        if( !le_eps )
566        {
567            right_slice.end = slice.start = pos % count;
568            slice.end = right_slice.start = (right_slice.start + slice.start) % count;
569
570            PUSH_SLICE(right_slice);
571            PUSH_SLICE(slice);
572        }
573        else
574            WRITE_PT(start_pt);
575    }
576
577    // 3. run recursive process
578    while( top > 0 )
579    {
580        slice = stack[--top];
581        end_pt = src_contour[slice.end];
582        pos = slice.start;
583        READ_PT(start_pt, pos);
584
585        if( pos != slice.end )
586        {
587            double dx, dy, dist, max_dist = 0;
588
589            dx = end_pt.x - start_pt.x;
590            dy = end_pt.y - start_pt.y;
591
592            assert( dx != 0 || dy != 0 );
593
594            while( pos != slice.end )
595            {
596                READ_PT(pt, pos);
597                dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
598
599                if( dist > max_dist )
600                {
601                    max_dist = dist;
602                    right_slice.start = (pos+count-1)%count;
603                }
604            }
605
606            le_eps = max_dist * max_dist <= eps * (dx * dx + dy * dy);
607        }
608        else
609        {
610            le_eps = true;
611            // read starting point
612            start_pt = src_contour[slice.start];
613        }
614
615        if( le_eps )
616        {
617            WRITE_PT(start_pt);
618        }
619        else
620        {
621            right_slice.end = slice.end;
622            slice.end = right_slice.start;
623            PUSH_SLICE(right_slice);
624            PUSH_SLICE(slice);
625        }
626    }
627
628    if( !is_closed )
629        WRITE_PT( src_contour[count-1] );
630
631    // last stage: do final clean-up of the approximated contour -
632    // remove extra points on the [almost] stright lines.
633    is_closed = is_closed0;
634    count = new_count;
635    pos = is_closed ? count - 1 : 0;
636    READ_DST_PT(start_pt, pos);
637    wpos = pos;
638    READ_DST_PT(pt, pos);
639
640    for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
641    {
642        double dx, dy, dist, successive_inner_product;
643        READ_DST_PT( end_pt, pos );
644
645        dx = end_pt.x - start_pt.x;
646        dy = end_pt.y - start_pt.y;
647        dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx);
648        successive_inner_product = (pt.x - start_pt.x) * (end_pt.x - pt.x) +
649        (pt.y - start_pt.y) * (end_pt.y - pt.y);
650
651        if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 &&
652           successive_inner_product >= 0 )
653        {
654            new_count--;
655            dst_contour[wpos] = start_pt = end_pt;
656            if(++wpos >= count) wpos = 0;
657            READ_DST_PT(pt, pos);
658            i++;
659            continue;
660        }
661        dst_contour[wpos] = start_pt = pt;
662        if(++wpos >= count) wpos = 0;
663        pt = end_pt;
664    }
665
666    if( !is_closed )
667        dst_contour[wpos] = pt;
668
669    return new_count;
670}
671
672}
673
674void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
675                      double epsilon, bool closed )
676{
677    Mat curve = _curve.getMat();
678    int npoints = curve.checkVector(2), depth = curve.depth();
679    CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F));
680
681    if( npoints == 0 )
682    {
683        _approxCurve.release();
684        return;
685    }
686
687    AutoBuffer<Point> _buf(npoints);
688    AutoBuffer<Range> _stack(npoints);
689    Point* buf = _buf;
690    int nout = 0;
691
692    if( depth == CV_32S )
693        nout = approxPolyDP_(curve.ptr<Point>(), npoints, buf, closed, epsilon, &_stack);
694    else if( depth == CV_32F )
695        nout = approxPolyDP_(curve.ptr<Point2f>(), npoints, (Point2f*)buf, closed, epsilon, &_stack);
696    else
697        CV_Error( CV_StsUnsupportedFormat, "" );
698
699    Mat(nout, 1, CV_MAKETYPE(depth, 2), buf).copyTo(_approxCurve);
700}
701
702
703CV_IMPL CvSeq*
704cvApproxPoly( const void* array, int header_size,
705             CvMemStorage* storage, int method,
706             double parameter, int parameter2 )
707{
708    cv::AutoBuffer<cv::Point> _buf;
709    cv::AutoBuffer<cv::Range> stack(100);
710    CvSeq* dst_seq = 0;
711    CvSeq *prev_contour = 0, *parent = 0;
712    CvContour contour_header;
713    CvSeq* src_seq = 0;
714    CvSeqBlock block;
715    int recursive = 0;
716
717    if( CV_IS_SEQ( array ))
718    {
719        src_seq = (CvSeq*)array;
720        if( !CV_IS_SEQ_POLYLINE( src_seq ))
721            CV_Error( CV_StsBadArg, "Unsupported sequence type" );
722
723        recursive = parameter2;
724
725        if( !storage )
726            storage = src_seq->storage;
727    }
728    else
729    {
730        src_seq = cvPointSeqFromMat(
731                                    CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0),
732                                    array, &contour_header, &block );
733    }
734
735    if( !storage )
736        CV_Error( CV_StsNullPtr, "NULL storage pointer " );
737
738    if( header_size < 0 )
739        CV_Error( CV_StsOutOfRange, "header_size is negative. "
740                 "Pass 0 to make the destination header_size == input header_size" );
741
742    if( header_size == 0 )
743        header_size = src_seq->header_size;
744
745    if( !CV_IS_SEQ_POLYLINE( src_seq ))
746    {
747        if( CV_IS_SEQ_CHAIN( src_seq ))
748        {
749            CV_Error( CV_StsBadArg, "Input curves are not polygonal. "
750                     "Use cvApproxChains first" );
751        }
752        else
753        {
754            CV_Error( CV_StsBadArg, "Input curves have uknown type" );
755        }
756    }
757
758    if( header_size == 0 )
759        header_size = src_seq->header_size;
760
761    if( header_size < (int)sizeof(CvContour) )
762        CV_Error( CV_StsBadSize, "New header size must be non-less than sizeof(CvContour)" );
763
764    if( method != CV_POLY_APPROX_DP )
765        CV_Error( CV_StsOutOfRange, "Unknown approximation method" );
766
767    while( src_seq != 0 )
768    {
769        CvSeq *contour = 0;
770
771        switch (method)
772        {
773        case CV_POLY_APPROX_DP:
774            if( parameter < 0 )
775                CV_Error( CV_StsOutOfRange, "Accuracy must be non-negative" );
776
777            CV_Assert( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 ||
778                      CV_SEQ_ELTYPE(src_seq) == CV_32FC2 );
779
780            {
781            int npoints = src_seq->total, nout = 0;
782            _buf.allocate(npoints*2);
783            cv::Point *src = _buf, *dst = src + npoints;
784            bool closed = CV_IS_SEQ_CLOSED(src_seq);
785
786            if( src_seq->first->next == src_seq->first )
787                src = (cv::Point*)src_seq->first->data;
788            else
789                cvCvtSeqToArray(src_seq, src);
790
791            if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 )
792                nout = cv::approxPolyDP_(src, npoints, dst, closed, parameter, &stack);
793            else if( CV_SEQ_ELTYPE(src_seq) == CV_32FC2 )
794                nout = cv::approxPolyDP_((cv::Point2f*)src, npoints,
795                                         (cv::Point2f*)dst, closed, parameter, &stack);
796            else
797                CV_Error( CV_StsUnsupportedFormat, "" );
798
799            contour = cvCreateSeq( src_seq->flags, header_size,
800                                  src_seq->elem_size, storage );
801            cvSeqPushMulti(contour, dst, nout);
802            }
803            break;
804        default:
805            CV_Error( CV_StsBadArg, "Invalid approximation method" );
806        }
807
808        assert( contour );
809
810        if( header_size >= (int)sizeof(CvContour))
811            cvBoundingRect( contour, 1 );
812
813        contour->v_prev = parent;
814        contour->h_prev = prev_contour;
815
816        if( prev_contour )
817            prev_contour->h_next = contour;
818        else if( parent )
819            parent->v_next = contour;
820        prev_contour = contour;
821        if( !dst_seq )
822            dst_seq = prev_contour;
823
824        if( !recursive )
825            break;
826
827        if( src_seq->v_next )
828        {
829            assert( prev_contour != 0 );
830            parent = prev_contour;
831            prev_contour = 0;
832            src_seq = src_seq->v_next;
833        }
834        else
835        {
836            while( src_seq->h_next == 0 )
837            {
838                src_seq = src_seq->v_prev;
839                if( src_seq == 0 )
840                    break;
841                prev_contour = parent;
842                if( parent )
843                    parent = parent->v_prev;
844            }
845            if( src_seq )
846                src_seq = src_seq->h_next;
847        }
848    }
849
850    return dst_seq;
851}
852
853/* End of file. */
854