1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
43 /****************************************************************************************\
44 * Chain Approximation *
45 \****************************************************************************************/
47 typedef struct _CvPtInfo
50 int k; /* support region */
51 int s; /* curvature value */
52 struct _CvPtInfo *next;
57 /* curvature: 0 - 1-curvature, 1 - k-cosine curvature. */
59 icvApproximateChainTC89( CvChain* chain,
61 CvMemStorage* storage,
65 static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 };
67 char local_buffer[1 << 16];
68 char* buffer = local_buffer;
72 _CvPtInfo *array, *first = 0, *current = 0, *prev_current = 0;
73 int i, j, i1, i2, s, len;
76 CvChainPtReader reader;
78 CvPoint pt = chain->origin;
80 assert( chain && contour && buffer );
82 buffer_size = (chain->total + 8) * sizeof( _CvPtInfo );
86 if( !CV_IS_SEQ_CHAIN_CONTOUR( chain ))
87 return CV_BADFLAG_ERR;
89 if( header_size < (int)sizeof(CvContour) )
90 return CV_BADSIZE_ERR;
92 cvStartWriteSeq( (chain->flags & ~CV_SEQ_ELTYPE_MASK) | CV_SEQ_ELTYPE_POINT,
93 header_size, sizeof( CvPoint ), storage, &writer );
95 if( chain->total == 0 )
97 CV_WRITE_SEQ_ELEM( pt, writer );
101 cvStartReadChainPoints( chain, &reader );
103 if( method > CV_CHAIN_APPROX_SIMPLE && buffer_size > (int)sizeof(local_buffer))
105 buffer = (char *) cvAlloc( buffer_size );
107 return CV_OUTOFMEM_ERR;
110 array = (_CvPtInfo *) buffer;
111 count = chain->total;
117 Restores all the digital curve points from the chain code.
118 Removes the points (from the resultant polygon)
119 that have zero 1-curvature */
120 for( i = 0; i < count; i++ )
122 int prev_code = *reader.prev_elem;
124 reader.prev_elem = reader.ptr;
125 CV_READ_CHAIN_POINT( pt, reader );
127 /* calc 1-curvature */
128 s = abs_diff[reader.code - prev_code + 7];
130 if( method <= CV_CHAIN_APPROX_SIMPLE )
132 if( method == CV_CHAIN_APPROX_NONE || s != 0 )
134 CV_WRITE_SEQ_ELEM( pt, writer );
140 current = current->next = array + i;
146 //assert( pt.x == chain->origin.x && pt.y == chain->origin.y );
148 if( method <= CV_CHAIN_APPROX_SIMPLE )
159 Determines support region for all the remained points */
163 int k, l = 0, d_num = 0;
165 i = (int)(current - array);
168 /* determine support region */
179 i1 += i1 < 0 ? len : 0;
181 i2 -= i2 >= len ? len : 0;
183 dx = array[i2].pt.x - array[i1].pt.x;
184 dy = array[i2].pt.y - array[i1].pt.y;
186 /* distance between p_(i - k) and p_(i + k) */
187 lk = dx * dx + dy * dy;
189 /* distance between p_i and the line (p_(i-k), p_(i+k)) */
190 dk_num = (pt0.x - array[i1].pt.x) * dy - (pt0.y - array[i1].pt.y) * dx;
191 d.f = (float) (((double) d_num) * lk - ((double) dk_num) * l);
193 if( k > 1 && (l >= lk || (d_num > 0 && d.i <= 0 || d_num < 0 && d.i >= 0)))
202 /* determine cosine curvature if it should be used */
203 if( method == CV_CHAIN_APPROX_TC89_KCOS )
205 /* calc k-cosine curvature */
206 for( j = k, s = 0; j > 0; j-- )
209 int dx1, dy1, dx2, dy2;
213 i1 += i1 < 0 ? len : 0;
215 i2 -= i2 >= len ? len : 0;
217 dx1 = array[i1].pt.x - pt0.x;
218 dy1 = array[i1].pt.y - pt0.y;
219 dx2 = array[i2].pt.x - pt0.x;
220 dy2 = array[i2].pt.y - pt0.y;
222 if( (dx1 | dy1) == 0 || (dx2 | dy2) == 0 )
225 temp_num = dx1 * dx2 + dy1 * dy2;
228 sqrt( ((double)dx1 * dx1 + (double)dy1 * dy1) *
229 ((double)dx2 * dx2 + (double)dy2 * dy2) ));
230 sk.f = (float) (temp_num + 1.1);
232 assert( 0 <= sk.f && sk.f <= 2.2 );
233 if( j < k && sk.i <= s )
240 current = current->next;
242 while( current != 0 );
244 prev_current = &temp;
248 Performs non-maxima supression */
251 int k2 = current->k >> 1;
254 i = (int)(current - array);
256 for( j = 1; j <= k2; j++ )
259 i2 += i2 < 0 ? len : 0;
261 if( array[i2].s > s )
265 i2 -= i2 >= len ? len : 0;
267 if( array[i2].s > s )
271 if( j <= k2 ) /* exclude point */
273 prev_current->next = current->next;
274 current->s = 0; /* "clear" point */
277 prev_current = current;
278 current = current->next;
280 while( current != 0 );
283 Removes non-dominant points with 1-length support region */
286 prev_current = &temp;
290 if( current->k == 1 )
293 i = (int)(current - array);
296 i1 += i1 < 0 ? len : 0;
299 i2 -= i2 >= len ? len : 0;
301 if( s <= array[i1].s || s <= array[i2].s )
303 prev_current->next = current->next;
307 prev_current = current;
310 prev_current = current;
311 current = current->next;
313 while( current != 0 );
315 if( method == CV_CHAIN_APPROX_TC89_KCOS )
319 Cleans remained couples of points */
322 if( array[0].s != 0 && array[len - 1].s != 0 ) /* specific case */
324 for( i1 = 1; i1 < len && array[i1].s != 0; i1++ )
329 goto copy_vect; /* all points survived */
332 for( i2 = len - 2; i2 > 0 && array[i2].s != 0; i2-- )
339 if( i1 == 0 && i2 == len - 1 ) /* only two points */
341 i1 = (int)(array[0].next - array);
342 array[len] = array[0]; /* move to the end */
344 array[len - 1].next = array + len;
346 temp.next = array + i1;
350 first = prev_current = &temp;
356 if( current->next == 0 || current->next - current != 1 )
362 int s1 = prev_current->s;
365 if( s1 > s2 || s1 == s2 && prev_current->k <= current->k )
367 prev_current->next = current->next;
370 first->next = current;
373 first->next->next = current;
380 prev_current = current;
381 current = current->next;
383 while( current != 0 );
393 CV_WRITE_SEQ_ELEM( current->pt, writer );
394 current = current->next;
396 while( current != 0 );
400 *contour = cvEndWriteSeq( &writer );
402 assert( writer.seq->total > 0 );
404 if( buffer != local_buffer )
410 /*Applies some approximation algorithm to chain-coded contour(s) and
411 converts it/them to polygonal representation */
413 cvApproxChains( CvSeq* src_seq,
414 CvMemStorage* storage,
416 double /*parameter*/,
417 int minimal_perimeter,
420 CvSeq *prev_contour = 0, *parent = 0;
423 CV_FUNCNAME( "cvApproxChains" );
427 if( !src_seq || !storage )
428 CV_ERROR( CV_StsNullPtr, "" );
429 if( method > CV_CHAIN_APPROX_TC89_KCOS || method <= 0 || minimal_perimeter < 0 )
430 CV_ERROR( CV_StsOutOfRange, "" );
432 while( src_seq != 0 )
434 int len = src_seq->total;
436 if( len >= minimal_perimeter )
442 case CV_CHAIN_APPROX_NONE:
443 case CV_CHAIN_APPROX_SIMPLE:
444 case CV_CHAIN_APPROX_TC89_L1:
445 case CV_CHAIN_APPROX_TC89_KCOS:
446 IPPI_CALL( icvApproximateChainTC89( (CvChain *) src_seq,
447 sizeof( CvContour ), storage,
448 (CvSeq**)&contour, method ));
452 CV_ERROR( CV_StsOutOfRange, "" );
457 if( contour->total > 0 )
459 cvBoundingRect( contour, 1 );
461 contour->v_prev = parent;
462 contour->h_prev = prev_contour;
465 prev_contour->h_next = contour;
467 parent->v_next = contour;
468 prev_contour = contour;
470 dst_seq = prev_contour;
472 else /* if resultant contour has zero length, skip it */
481 if( src_seq->v_next && len >= minimal_perimeter )
483 assert( prev_contour != 0 );
484 parent = prev_contour;
486 src_seq = src_seq->v_next;
490 while( src_seq->h_next == 0 )
492 src_seq = src_seq->v_prev;
495 prev_contour = parent;
497 parent = parent->v_prev;
500 src_seq = src_seq->h_next;
510 /****************************************************************************************\
511 * Polygonal Approximation *
512 \****************************************************************************************/
514 /* Ramer-Douglas-Peucker algorithm for polygon simplification */
516 /* the version for integer point coordinates */
518 icvApproxPolyDP_32s( CvSeq* src_contour, int header_size,
519 CvMemStorage* storage,
520 CvSeq** dst_contour, float eps )
523 CvSlice slice = {0, 0}, right_slice = {0, 0};
524 CvSeqReader reader, reader2;
526 CvPoint start_pt = {INT_MIN, INT_MIN}, end_pt = {0, 0}, pt = {0,0};
527 int i = 0, j, count = src_contour->total, new_count;
528 int is_closed = CV_IS_SEQ_CLOSED( src_contour );
530 CvMemStorage* temp_storage = 0;
533 assert( CV_SEQ_ELTYPE(src_contour) == CV_32SC2 );
534 cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer );
536 if( src_contour->total == 0 )
538 *dst_contour = cvEndWriteSeq( &writer );
542 temp_storage = cvCreateChildMemStorage( storage );
544 assert( src_contour->first != 0 );
545 stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage );
547 cvStartReadSeq( src_contour, &reader, 0 );
551 right_slice.start_index = count;
552 end_pt = *(CvPoint*)(reader.ptr);
553 start_pt = *(CvPoint*)cvGetSeqElem( src_contour, -1 );
555 if( start_pt.x != end_pt.x || start_pt.y != end_pt.y )
557 slice.start_index = 0;
558 slice.end_index = count - 1;
559 cvSeqPush( stack, &slice );
570 /* 1. Find approximately two farthest points of the contour */
571 right_slice.start_index = 0;
573 for( i = 0; i < init_iters; i++ )
576 cvSetSeqReaderPos( &reader, right_slice.start_index, 1 );
577 CV_READ_SEQ_ELEM( start_pt, reader ); /* read the first point */
579 for( j = 1; j < count; j++ )
583 CV_READ_SEQ_ELEM( pt, reader );
584 dx = pt.x - start_pt.x;
585 dy = pt.y - start_pt.y;
587 dist = dx * dx + dy * dy;
589 if( dist > max_dist )
592 right_slice.start_index = j;
596 le_eps = max_dist <= eps;
599 /* 2. initialize the stack */
602 slice.start_index = cvGetSeqReaderPos( &reader );
603 slice.end_index = right_slice.start_index += slice.start_index;
605 right_slice.start_index -= right_slice.start_index >= count ? count : 0;
606 right_slice.end_index = slice.start_index;
607 if( right_slice.end_index < right_slice.start_index )
608 right_slice.end_index += count;
610 cvSeqPush( stack, &right_slice );
611 cvSeqPush( stack, &slice );
614 CV_WRITE_SEQ_ELEM( start_pt, writer );
617 /* 3. run recursive process */
618 while( stack->total != 0 )
620 cvSeqPop( stack, &slice );
622 cvSetSeqReaderPos( &reader, slice.end_index );
623 CV_READ_SEQ_ELEM( end_pt, reader );
625 cvSetSeqReaderPos( &reader, slice.start_index );
626 CV_READ_SEQ_ELEM( start_pt, reader );
628 if( slice.end_index > slice.start_index + 1 )
630 int dx, dy, dist, max_dist = 0;
632 dx = end_pt.x - start_pt.x;
633 dy = end_pt.y - start_pt.y;
635 assert( dx != 0 || dy != 0 );
637 for( i = slice.start_index + 1; i < slice.end_index; i++ )
639 CV_READ_SEQ_ELEM( pt, reader );
640 dist = abs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
642 if( dist > max_dist )
645 right_slice.start_index = i;
649 le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy);
653 assert( slice.end_index > slice.start_index );
655 /* read starting point */
656 cvSetSeqReaderPos( &reader, slice.start_index );
657 CV_READ_SEQ_ELEM( start_pt, reader );
662 CV_WRITE_SEQ_ELEM( start_pt, writer );
666 right_slice.end_index = slice.end_index;
667 slice.end_index = right_slice.start_index;
668 cvSeqPush( stack, &right_slice );
669 cvSeqPush( stack, &slice );
673 is_closed = CV_IS_SEQ_CLOSED( src_contour );
675 CV_WRITE_SEQ_ELEM( end_pt, writer );
677 *dst_contour = cvEndWriteSeq( &writer );
679 cvStartReadSeq( *dst_contour, &reader, is_closed );
680 CV_READ_SEQ_ELEM( start_pt, reader );
683 CV_READ_SEQ_ELEM( pt, reader );
685 new_count = count = (*dst_contour)->total;
686 for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
689 CV_READ_SEQ_ELEM( end_pt, reader );
691 dx = end_pt.x - start_pt.x;
692 dy = end_pt.y - start_pt.y;
693 dist = abs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx);
694 if( (double)dist * dist <= 0.5*eps*((double)dx*dx + (double)dy*dy) && dx != 0 && dy != 0 )
697 *((CvPoint*)reader2.ptr) = start_pt = end_pt;
698 CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
699 CV_READ_SEQ_ELEM( pt, reader );
703 *((CvPoint*)reader2.ptr) = start_pt = pt;
704 CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
709 *((CvPoint*)reader2.ptr) = pt;
711 if( new_count < count )
712 cvSeqPopMulti( *dst_contour, 0, count - new_count );
714 cvReleaseMemStorage( &temp_storage );
720 /* the version for integer point coordinates */
722 icvApproxPolyDP_32f( CvSeq* src_contour, int header_size,
723 CvMemStorage* storage,
724 CvSeq** dst_contour, float eps )
727 CvSlice slice = {0, 0}, right_slice = {0, 0};
728 CvSeqReader reader, reader2;
730 CvPoint2D32f start_pt = {-1e6f, -1e6f}, end_pt = {0, 0}, pt = {0,0};
731 int i = 0, j, count = src_contour->total, new_count;
732 int is_closed = CV_IS_SEQ_CLOSED( src_contour );
734 CvMemStorage* temp_storage = 0;
737 assert( CV_SEQ_ELTYPE(src_contour) == CV_32FC2 );
738 cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer );
740 if( src_contour->total == 0 )
742 *dst_contour = cvEndWriteSeq( &writer );
746 temp_storage = cvCreateChildMemStorage( storage );
748 assert( src_contour->first != 0 );
749 stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage );
751 cvStartReadSeq( src_contour, &reader, 0 );
755 right_slice.start_index = count;
756 end_pt = *(CvPoint2D32f*)(reader.ptr);
757 start_pt = *(CvPoint2D32f*)cvGetSeqElem( src_contour, -1 );
759 if( fabs(start_pt.x - end_pt.x) > FLT_EPSILON ||
760 fabs(start_pt.y - end_pt.y) > FLT_EPSILON )
762 slice.start_index = 0;
763 slice.end_index = count - 1;
764 cvSeqPush( stack, &slice );
775 /* 1. Find approximately two farthest points of the contour */
776 right_slice.start_index = 0;
778 for( i = 0; i < init_iters; i++ )
781 cvSetSeqReaderPos( &reader, right_slice.start_index, 1 );
782 CV_READ_SEQ_ELEM( start_pt, reader ); /* read the first point */
784 for( j = 1; j < count; j++ )
788 CV_READ_SEQ_ELEM( pt, reader );
789 dx = pt.x - start_pt.x;
790 dy = pt.y - start_pt.y;
792 dist = dx * dx + dy * dy;
794 if( dist > max_dist )
797 right_slice.start_index = j;
801 le_eps = max_dist <= eps;
804 /* 2. initialize the stack */
807 slice.start_index = cvGetSeqReaderPos( &reader );
808 slice.end_index = right_slice.start_index += slice.start_index;
810 right_slice.start_index -= right_slice.start_index >= count ? count : 0;
811 right_slice.end_index = slice.start_index;
812 if( right_slice.end_index < right_slice.start_index )
813 right_slice.end_index += count;
815 cvSeqPush( stack, &right_slice );
816 cvSeqPush( stack, &slice );
819 CV_WRITE_SEQ_ELEM( start_pt, writer );
822 /* 3. run recursive process */
823 while( stack->total != 0 )
825 cvSeqPop( stack, &slice );
827 cvSetSeqReaderPos( &reader, slice.end_index );
828 CV_READ_SEQ_ELEM( end_pt, reader );
830 cvSetSeqReaderPos( &reader, slice.start_index );
831 CV_READ_SEQ_ELEM( start_pt, reader );
833 if( slice.end_index > slice.start_index + 1 )
835 double dx, dy, dist, max_dist = 0;
837 dx = end_pt.x - start_pt.x;
838 dy = end_pt.y - start_pt.y;
840 assert( dx != 0 || dy != 0 );
842 for( i = slice.start_index + 1; i < slice.end_index; i++ )
844 CV_READ_SEQ_ELEM( pt, reader );
845 dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
847 if( dist > max_dist )
850 right_slice.start_index = i;
854 le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy);
858 assert( slice.end_index > slice.start_index );
860 /* read starting point */
861 cvSetSeqReaderPos( &reader, slice.start_index );
862 CV_READ_SEQ_ELEM( start_pt, reader );
867 CV_WRITE_SEQ_ELEM( start_pt, writer );
871 right_slice.end_index = slice.end_index;
872 slice.end_index = right_slice.start_index;
873 cvSeqPush( stack, &right_slice );
874 cvSeqPush( stack, &slice );
878 is_closed = CV_IS_SEQ_CLOSED( src_contour );
880 CV_WRITE_SEQ_ELEM( end_pt, writer );
882 *dst_contour = cvEndWriteSeq( &writer );
884 cvStartReadSeq( *dst_contour, &reader, is_closed );
885 CV_READ_SEQ_ELEM( start_pt, reader );
888 CV_READ_SEQ_ELEM( pt, reader );
890 new_count = count = (*dst_contour)->total;
891 for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
894 CV_READ_SEQ_ELEM( end_pt, reader );
896 dx = end_pt.x - start_pt.x;
897 dy = end_pt.y - start_pt.y;
898 dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx);
899 if( (double)dist * dist <= 0.5*eps*((double)dx*dx + (double)dy*dy) )
902 *((CvPoint2D32f*)reader2.ptr) = start_pt = end_pt;
903 CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
904 CV_READ_SEQ_ELEM( pt, reader );
908 *((CvPoint2D32f*)reader2.ptr) = start_pt = pt;
909 CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
914 *((CvPoint2D32f*)reader2.ptr) = pt;
916 if( new_count < count )
917 cvSeqPopMulti( *dst_contour, 0, count - new_count );
919 cvReleaseMemStorage( &temp_storage );
926 cvApproxPoly( const void* array, int header_size,
927 CvMemStorage* storage, int method,
928 double parameter, int parameter2 )
931 CvSeq *prev_contour = 0, *parent = 0;
932 CvContour contour_header;
937 CV_FUNCNAME( "cvApproxPoly" );
941 if( CV_IS_SEQ( array ))
943 src_seq = (CvSeq*)array;
944 if( !CV_IS_SEQ_POLYLINE( src_seq ))
945 CV_ERROR( CV_StsBadArg, "Unsupported sequence type" );
947 recursive = parameter2;
950 storage = src_seq->storage;
954 CV_CALL( src_seq = cvPointSeqFromMat(
955 CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0),
956 array, &contour_header, &block ));
960 CV_ERROR( CV_StsNullPtr, "NULL storage pointer " );
962 if( header_size < 0 )
963 CV_ERROR( CV_StsOutOfRange, "header_size is negative. "
964 "Pass 0 to make the destination header_size == input header_size" );
966 if( header_size == 0 )
967 header_size = src_seq->header_size;
969 if( !CV_IS_SEQ_POLYLINE( src_seq ))
971 if( CV_IS_SEQ_CHAIN( src_seq ))
973 CV_ERROR( CV_StsBadArg, "Input curves are not polygonal. "
974 "Use cvApproxChains first" );
978 CV_ERROR( CV_StsBadArg, "Input curves have uknown type" );
982 if( header_size == 0 )
983 header_size = src_seq->header_size;
985 if( header_size < (int)sizeof(CvContour) )
986 CV_ERROR( CV_StsBadSize, "New header size must be non-less than sizeof(CvContour)" );
988 if( method != CV_POLY_APPROX_DP )
989 CV_ERROR( CV_StsOutOfRange, "Unknown approximation method" );
991 while( src_seq != 0 )
997 case CV_POLY_APPROX_DP:
999 CV_ERROR( CV_StsOutOfRange, "Accuracy must be non-negative" );
1001 if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 )
1003 IPPI_CALL( icvApproxPolyDP_32s( src_seq, header_size, storage,
1004 &contour, (float)parameter ));
1008 IPPI_CALL( icvApproxPolyDP_32f( src_seq, header_size, storage,
1009 &contour, (float)parameter ));
1014 CV_ERROR( CV_StsBadArg, "Invalid approximation method" );
1019 if( header_size >= (int)sizeof(CvContour))
1020 cvBoundingRect( contour, 1 );
1022 contour->v_prev = parent;
1023 contour->h_prev = prev_contour;
1026 prev_contour->h_next = contour;
1028 parent->v_next = contour;
1029 prev_contour = contour;
1031 dst_seq = prev_contour;
1036 if( src_seq->v_next )
1038 assert( prev_contour != 0 );
1039 parent = prev_contour;
1041 src_seq = src_seq->v_next;
1045 while( src_seq->h_next == 0 )
1047 src_seq = src_seq->v_prev;
1050 prev_contour = parent;
1052 parent = parent->v_prev;
1055 src_seq = src_seq->h_next;