Update the changelog
[opencv] / cv / src / cvapprox.cpp
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 /****************************************************************************************\
44 *                                  Chain Approximation                                   *
45 \****************************************************************************************/
46
47 typedef 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. */
58 CvStatus
59 icvApproximateChainTC89( CvChain*               chain,
60                          int                    header_size,
61                          CvMemStorage*          storage, 
62                          CvSeq**                contour, 
63                          int    method )
64 {
65     static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 };
66
67     char            local_buffer[1 << 16];
68     char*           buffer = local_buffer;
69     int             buffer_size;
70
71     _CvPtInfo       temp;
72     _CvPtInfo       *array, *first = 0, *current = 0, *prev_current = 0;
73     int             i, j, i1, i2, s, len;
74     int             count;
75
76     CvChainPtReader reader;
77     CvSeqWriter     writer;
78     CvPoint         pt = chain->origin;
79    
80     assert( chain && contour && buffer );
81
82     buffer_size = (chain->total + 8) * sizeof( _CvPtInfo );
83
84     *contour = 0;
85
86     if( !CV_IS_SEQ_CHAIN_CONTOUR( chain ))
87         return CV_BADFLAG_ERR;
88
89     if( header_size < (int)sizeof(CvContour) )
90         return CV_BADSIZE_ERR;
91     
92     cvStartWriteSeq( (chain->flags & ~CV_SEQ_ELTYPE_MASK) | CV_SEQ_ELTYPE_POINT,
93                      header_size, sizeof( CvPoint ), storage, &writer );
94     
95     if( chain->total == 0 )
96     {        
97         CV_WRITE_SEQ_ELEM( pt, writer );
98         goto exit_function;
99     }
100
101     cvStartReadChainPoints( chain, &reader );
102
103     if( method > CV_CHAIN_APPROX_SIMPLE && buffer_size > (int)sizeof(local_buffer))
104     {
105         buffer = (char *) cvAlloc( buffer_size );
106         if( !buffer )
107             return CV_OUTOFMEM_ERR;
108     }
109
110     array = (_CvPtInfo *) buffer;
111     count = chain->total;
112
113     temp.next = 0;
114     current = &temp;
115
116     /* Pass 0.
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++ )
121     {
122         int prev_code = *reader.prev_elem;
123
124         reader.prev_elem = reader.ptr;
125         CV_READ_CHAIN_POINT( pt, reader );
126
127         /* calc 1-curvature */
128         s = abs_diff[reader.code - prev_code + 7];
129
130         if( method <= CV_CHAIN_APPROX_SIMPLE )
131         {
132             if( method == CV_CHAIN_APPROX_NONE || s != 0 )
133             {
134                 CV_WRITE_SEQ_ELEM( pt, writer );
135             }
136         }
137         else
138         {
139             if( s != 0 )
140                 current = current->next = array + i;
141             array[i].s = s;
142             array[i].pt = pt;
143         }
144     }
145
146     //assert( pt.x == chain->origin.x && pt.y == chain->origin.y );
147
148     if( method <= CV_CHAIN_APPROX_SIMPLE )
149         goto exit_function;
150
151     current->next = 0;
152
153     len = i;
154     current = temp.next;
155
156     assert( current );
157
158     /* Pass 1.
159        Determines support region for all the remained points */
160     do
161     {
162         CvPoint pt0;
163         int k, l = 0, d_num = 0;
164
165         i = (int)(current - array);
166         pt0 = array[i].pt;
167
168         /* determine support region */
169         for( k = 1;; k++ )
170         {
171             int lk, dk_num;
172             int dx, dy;
173             Cv32suf d;
174
175             assert( k <= len );
176
177             /* calc indices */
178             i1 = i - k;
179             i1 += i1 < 0 ? len : 0;
180             i2 = i + k;
181             i2 -= i2 >= len ? len : 0;
182
183             dx = array[i2].pt.x - array[i1].pt.x;
184             dy = array[i2].pt.y - array[i1].pt.y;
185
186             /* distance between p_(i - k) and p_(i + k) */
187             lk = dx * dx + dy * dy;
188
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);
192
193             if( k > 1 && (l >= lk || (d_num > 0 && d.i <= 0 || d_num < 0 && d.i >= 0)))
194                 break;
195
196             d_num = dk_num;
197             l = lk;
198         }
199
200         current->k = --k;
201
202         /* determine cosine curvature if it should be used */
203         if( method == CV_CHAIN_APPROX_TC89_KCOS )
204         {
205             /* calc k-cosine curvature */
206             for( j = k, s = 0; j > 0; j-- )
207             {
208                 double temp_num;
209                 int dx1, dy1, dx2, dy2;
210                 Cv32suf sk;
211
212                 i1 = i - j;
213                 i1 += i1 < 0 ? len : 0;
214                 i2 = i + j;
215                 i2 -= i2 >= len ? len : 0;
216
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;
221
222                 if( (dx1 | dy1) == 0 || (dx2 | dy2) == 0 )
223                     break;
224
225                 temp_num = dx1 * dx2 + dy1 * dy2;
226                 temp_num =
227                     (float) (temp_num /
228                              sqrt( ((double)dx1 * dx1 + (double)dy1 * dy1) *
229                                    ((double)dx2 * dx2 + (double)dy2 * dy2) ));
230                 sk.f = (float) (temp_num + 1.1);
231
232                 assert( 0 <= sk.f && sk.f <= 2.2 );
233                 if( j < k && sk.i <= s )
234                     break;
235
236                 s = sk.i;
237             }
238             current->s = s;
239         }
240         current = current->next;
241     }
242     while( current != 0 );
243
244     prev_current = &temp;
245     current = temp.next;
246
247     /* Pass 2.
248        Performs non-maxima supression */
249     do
250     {
251         int k2 = current->k >> 1;
252
253         s = current->s;
254         i = (int)(current - array);
255
256         for( j = 1; j <= k2; j++ )
257         {
258             i2 = i - j;
259             i2 += i2 < 0 ? len : 0;
260
261             if( array[i2].s > s )
262                 break;
263
264             i2 = i + j;
265             i2 -= i2 >= len ? len : 0;
266
267             if( array[i2].s > s )
268                 break;
269         }
270
271         if( j <= k2 )           /* exclude point */
272         {
273             prev_current->next = current->next;
274             current->s = 0;     /* "clear" point */
275         }
276         else
277             prev_current = current;
278         current = current->next;
279     }
280     while( current != 0 );
281
282     /* Pass 3.
283        Removes non-dominant points with 1-length support region */
284     current = temp.next;
285     assert( current );
286     prev_current = &temp;
287
288     do
289     {
290         if( current->k == 1 )
291         {
292             s = current->s;
293             i = (int)(current - array);
294
295             i1 = i - 1;
296             i1 += i1 < 0 ? len : 0;
297
298             i2 = i + 1;
299             i2 -= i2 >= len ? len : 0;
300
301             if( s <= array[i1].s || s <= array[i2].s )
302             {
303                 prev_current->next = current->next;
304                 current->s = 0;
305             }
306             else
307                 prev_current = current;
308         }
309         else
310             prev_current = current;
311         current = current->next;
312     }
313     while( current != 0 );
314
315     if( method == CV_CHAIN_APPROX_TC89_KCOS )
316         goto copy_vect;
317
318     /* Pass 4.
319        Cleans remained couples of points */
320     assert( temp.next );
321
322     if( array[0].s != 0 && array[len - 1].s != 0 )      /* specific case */
323     {
324         for( i1 = 1; i1 < len && array[i1].s != 0; i1++ )
325         {
326             array[i1 - 1].s = 0;
327         }
328         if( i1 == len )
329             goto copy_vect;     /* all points survived */
330         i1--;
331
332         for( i2 = len - 2; i2 > 0 && array[i2].s != 0; i2-- )
333         {
334             array[i2].next = 0;
335             array[i2 + 1].s = 0;
336         }
337         i2++;
338
339         if( i1 == 0 && i2 == len - 1 )  /* only two points */
340         {
341             i1 = (int)(array[0].next - array);
342             array[len] = array[0];      /* move to the end */
343             array[len].next = 0;
344             array[len - 1].next = array + len;
345         }
346         temp.next = array + i1;
347     }
348
349     current = temp.next;
350     first = prev_current = &temp;
351     count = 1;
352
353     /* do last pass */
354     do
355     {
356         if( current->next == 0 || current->next - current != 1 )
357         {
358             if( count >= 2 )
359             {
360                 if( count == 2 )
361                 {
362                     int s1 = prev_current->s;
363                     int s2 = current->s;
364
365                     if( s1 > s2 || s1 == s2 && prev_current->k <= current->k )
366                         /* remove second */
367                         prev_current->next = current->next;
368                     else
369                         /* remove first */
370                         first->next = current;
371                 }
372                 else
373                     first->next->next = current;
374             }
375             first = current;
376             count = 1;
377         }
378         else
379             count++;
380         prev_current = current;
381         current = current->next;
382     }
383     while( current != 0 );
384
385   copy_vect:
386
387     /* gather points */
388     current = temp.next;
389     assert( current );
390
391     do
392     {
393         CV_WRITE_SEQ_ELEM( current->pt, writer );
394         current = current->next;
395     }
396     while( current != 0 );
397
398 exit_function:
399
400     *contour = cvEndWriteSeq( &writer );
401
402     assert( writer.seq->total > 0 );
403
404     if( buffer != local_buffer )
405         cvFree( &buffer );
406     return CV_OK;
407 }
408
409
410 /*Applies some approximation algorithm to chain-coded contour(s) and
411   converts it/them to polygonal representation */
412 CV_IMPL CvSeq*
413 cvApproxChains( CvSeq*              src_seq,
414                 CvMemStorage*       storage,
415                 int                 method,
416                 double              /*parameter*/, 
417                 int                 minimal_perimeter, 
418                 int                 recursive )
419 {
420     CvSeq *prev_contour = 0, *parent = 0;
421     CvSeq *dst_seq = 0;
422     
423     CV_FUNCNAME( "cvApproxChains" );
424
425     __BEGIN__;
426
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, "" );
431
432     while( src_seq != 0 )
433     {
434         int len = src_seq->total;
435
436         if( len >= minimal_perimeter )
437         {
438             CvSeq *contour;
439             
440             switch( method )
441             {
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 ));
449                 break;
450             default:
451                 assert(0);
452                 CV_ERROR( CV_StsOutOfRange, "" );
453             }
454
455             assert( contour );
456
457             if( contour->total > 0 )
458             {
459                 cvBoundingRect( contour, 1 );
460
461                 contour->v_prev = parent;
462                 contour->h_prev = prev_contour;
463
464                 if( prev_contour )
465                     prev_contour->h_next = contour;
466                 else if( parent )
467                     parent->v_next = contour;
468                 prev_contour = contour;
469                 if( !dst_seq )
470                     dst_seq = prev_contour;
471             }
472             else                /* if resultant contour has zero length, skip it */
473             {
474                 len = -1;
475             }
476         }
477
478         if( !recursive )
479             break;
480
481         if( src_seq->v_next && len >= minimal_perimeter )
482         {
483             assert( prev_contour != 0 );
484             parent = prev_contour;
485             prev_contour = 0;
486             src_seq = src_seq->v_next;
487         }
488         else
489         {
490             while( src_seq->h_next == 0 )
491             {
492                 src_seq = src_seq->v_prev;
493                 if( src_seq == 0 )
494                     break;
495                 prev_contour = parent;
496                 if( parent )
497                     parent = parent->v_prev;
498             }
499             if( src_seq )
500                 src_seq = src_seq->h_next;
501         }
502     }
503
504     __END__;
505
506     return dst_seq;
507 }
508
509
510 /****************************************************************************************\
511 *                               Polygonal Approximation                                  *
512 \****************************************************************************************/
513
514 /* Ramer-Douglas-Peucker algorithm for polygon simplification */
515
516 /* the version for integer point coordinates */
517 static CvStatus
518 icvApproxPolyDP_32s( CvSeq* src_contour, int header_size, 
519                      CvMemStorage* storage,
520                      CvSeq** dst_contour, float eps )
521 {
522     int             init_iters = 3;
523     CvSlice         slice = {0, 0}, right_slice = {0, 0};
524     CvSeqReader     reader, reader2;
525     CvSeqWriter     writer;
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 );
529     int             le_eps = 0;
530     CvMemStorage*   temp_storage = 0;
531     CvSeq*          stack = 0;
532     
533     assert( CV_SEQ_ELTYPE(src_contour) == CV_32SC2 );
534     cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer );
535
536     if( src_contour->total == 0  )
537     {
538         *dst_contour = cvEndWriteSeq( &writer );
539         return CV_OK;
540     }
541
542     temp_storage = cvCreateChildMemStorage( storage );
543
544     assert( src_contour->first != 0 );
545     stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage );
546     eps *= eps;
547     cvStartReadSeq( src_contour, &reader, 0 );
548
549     if( !is_closed )
550     {
551         right_slice.start_index = count;
552         end_pt = *(CvPoint*)(reader.ptr);
553         start_pt = *(CvPoint*)cvGetSeqElem( src_contour, -1 );
554
555         if( start_pt.x != end_pt.x || start_pt.y != end_pt.y )
556         {
557             slice.start_index = 0;
558             slice.end_index = count - 1;
559             cvSeqPush( stack, &slice );
560         }
561         else
562         {
563             is_closed = 1;
564             init_iters = 1;
565         }
566     }
567     
568     if( is_closed )
569     {
570         /* 1. Find approximately two farthest points of the contour */
571         right_slice.start_index = 0;
572
573         for( i = 0; i < init_iters; i++ )
574         {
575             int max_dist = 0;
576             cvSetSeqReaderPos( &reader, right_slice.start_index, 1 );
577             CV_READ_SEQ_ELEM( start_pt, reader );   /* read the first point */
578
579             for( j = 1; j < count; j++ )
580             {
581                 int dx, dy, dist;
582
583                 CV_READ_SEQ_ELEM( pt, reader );
584                 dx = pt.x - start_pt.x;
585                 dy = pt.y - start_pt.y;
586
587                 dist = dx * dx + dy * dy;
588
589                 if( dist > max_dist )
590                 {
591                     max_dist = dist;
592                     right_slice.start_index = j;
593                 }
594             }
595
596             le_eps = max_dist <= eps;
597         }
598
599         /* 2. initialize the stack */
600         if( !le_eps )
601         {
602             slice.start_index = cvGetSeqReaderPos( &reader );
603             slice.end_index = right_slice.start_index += slice.start_index;
604
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;
609
610             cvSeqPush( stack, &right_slice );
611             cvSeqPush( stack, &slice );
612         }
613         else
614             CV_WRITE_SEQ_ELEM( start_pt, writer );
615     }
616
617     /* 3. run recursive process */
618     while( stack->total != 0 )
619     {
620         cvSeqPop( stack, &slice );
621
622         cvSetSeqReaderPos( &reader, slice.end_index );
623         CV_READ_SEQ_ELEM( end_pt, reader );
624
625         cvSetSeqReaderPos( &reader, slice.start_index );
626         CV_READ_SEQ_ELEM( start_pt, reader );
627
628         if( slice.end_index > slice.start_index + 1 )
629         {
630             int dx, dy, dist, max_dist = 0;
631
632             dx = end_pt.x - start_pt.x;
633             dy = end_pt.y - start_pt.y;
634
635             assert( dx != 0 || dy != 0 );
636
637             for( i = slice.start_index + 1; i < slice.end_index; i++ )
638             {
639                 CV_READ_SEQ_ELEM( pt, reader );
640                 dist = abs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
641
642                 if( dist > max_dist )
643                 {
644                     max_dist = dist;
645                     right_slice.start_index = i;
646                 }
647             }
648
649             le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy);
650         }
651         else
652         {
653             assert( slice.end_index > slice.start_index );
654             le_eps = 1;
655             /* read starting point */
656             cvSetSeqReaderPos( &reader, slice.start_index );
657             CV_READ_SEQ_ELEM( start_pt, reader );
658         }
659
660         if( le_eps )
661         {
662             CV_WRITE_SEQ_ELEM( start_pt, writer );
663         }
664         else
665         {
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 );
670         }
671     }
672
673     is_closed = CV_IS_SEQ_CLOSED( src_contour );
674     if( !is_closed )
675         CV_WRITE_SEQ_ELEM( end_pt, writer );
676
677     *dst_contour = cvEndWriteSeq( &writer );
678     
679     cvStartReadSeq( *dst_contour, &reader, is_closed );
680     CV_READ_SEQ_ELEM( start_pt, reader );
681
682     reader2 = reader;
683     CV_READ_SEQ_ELEM( pt, reader );
684
685     new_count = count = (*dst_contour)->total;
686     for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
687     {
688         int dx, dy, dist;
689         CV_READ_SEQ_ELEM( end_pt, reader );
690
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 )
695         {
696             new_count--;
697             *((CvPoint*)reader2.ptr) = start_pt = end_pt;
698             CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
699             CV_READ_SEQ_ELEM( pt, reader );
700             i++;
701             continue;
702         }
703         *((CvPoint*)reader2.ptr) = start_pt = pt;
704         CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
705         pt = end_pt;
706     }
707
708     if( !is_closed )
709         *((CvPoint*)reader2.ptr) = pt;
710
711     if( new_count < count )
712         cvSeqPopMulti( *dst_contour, 0, count - new_count );
713
714     cvReleaseMemStorage( &temp_storage );
715
716     return CV_OK;
717 }
718
719
720 /* the version for integer point coordinates */
721 static CvStatus
722 icvApproxPolyDP_32f( CvSeq* src_contour, int header_size, 
723                      CvMemStorage* storage,
724                      CvSeq** dst_contour, float eps )
725 {
726     int             init_iters = 3;
727     CvSlice         slice = {0, 0}, right_slice = {0, 0};
728     CvSeqReader     reader, reader2;
729     CvSeqWriter     writer;
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 );
733     int             le_eps = 0;
734     CvMemStorage*   temp_storage = 0;
735     CvSeq*          stack = 0;
736     
737     assert( CV_SEQ_ELTYPE(src_contour) == CV_32FC2 );
738     cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer );
739
740     if( src_contour->total == 0  )
741     {
742         *dst_contour = cvEndWriteSeq( &writer );
743         return CV_OK;
744     }
745
746     temp_storage = cvCreateChildMemStorage( storage );
747
748     assert( src_contour->first != 0 );
749     stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage );
750     eps *= eps;
751     cvStartReadSeq( src_contour, &reader, 0 );
752
753     if( !is_closed )
754     {
755         right_slice.start_index = count;
756         end_pt = *(CvPoint2D32f*)(reader.ptr);
757         start_pt = *(CvPoint2D32f*)cvGetSeqElem( src_contour, -1 );
758
759         if( fabs(start_pt.x - end_pt.x) > FLT_EPSILON ||
760             fabs(start_pt.y - end_pt.y) > FLT_EPSILON )
761         {
762             slice.start_index = 0;
763             slice.end_index = count - 1;
764             cvSeqPush( stack, &slice );
765         }
766         else
767         {
768             is_closed = 1;
769             init_iters = 1;
770         }
771     }
772     
773     if( is_closed )
774     {
775         /* 1. Find approximately two farthest points of the contour */
776         right_slice.start_index = 0;
777
778         for( i = 0; i < init_iters; i++ )
779         {
780             double max_dist = 0;
781             cvSetSeqReaderPos( &reader, right_slice.start_index, 1 );
782             CV_READ_SEQ_ELEM( start_pt, reader );   /* read the first point */
783
784             for( j = 1; j < count; j++ )
785             {
786                 double dx, dy, dist;
787
788                 CV_READ_SEQ_ELEM( pt, reader );
789                 dx = pt.x - start_pt.x;
790                 dy = pt.y - start_pt.y;
791
792                 dist = dx * dx + dy * dy;
793
794                 if( dist > max_dist )
795                 {
796                     max_dist = dist;
797                     right_slice.start_index = j;
798                 }
799             }
800
801             le_eps = max_dist <= eps;
802         }
803
804         /* 2. initialize the stack */
805         if( !le_eps )
806         {
807             slice.start_index = cvGetSeqReaderPos( &reader );
808             slice.end_index = right_slice.start_index += slice.start_index;
809
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;
814
815             cvSeqPush( stack, &right_slice );
816             cvSeqPush( stack, &slice );
817         }
818         else
819             CV_WRITE_SEQ_ELEM( start_pt, writer );
820     }
821
822     /* 3. run recursive process */
823     while( stack->total != 0 )
824     {
825         cvSeqPop( stack, &slice );
826
827         cvSetSeqReaderPos( &reader, slice.end_index );
828         CV_READ_SEQ_ELEM( end_pt, reader );
829
830         cvSetSeqReaderPos( &reader, slice.start_index );
831         CV_READ_SEQ_ELEM( start_pt, reader );
832
833         if( slice.end_index > slice.start_index + 1 )
834         {
835             double dx, dy, dist, max_dist = 0;
836             
837             dx = end_pt.x - start_pt.x;
838             dy = end_pt.y - start_pt.y;
839
840             assert( dx != 0 || dy != 0 );
841
842             for( i = slice.start_index + 1; i < slice.end_index; i++ )
843             {
844                 CV_READ_SEQ_ELEM( pt, reader );
845                 dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
846
847                 if( dist > max_dist )
848                 {
849                     max_dist = dist;
850                     right_slice.start_index = i;
851                 }
852             }
853
854             le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy);
855         }
856         else
857         {
858             assert( slice.end_index > slice.start_index );
859             le_eps = 1;
860             /* read starting point */
861             cvSetSeqReaderPos( &reader, slice.start_index );
862             CV_READ_SEQ_ELEM( start_pt, reader );
863         }
864
865         if( le_eps )
866         {
867             CV_WRITE_SEQ_ELEM( start_pt, writer );
868         }
869         else
870         {
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 );
875         }
876     }
877
878     is_closed = CV_IS_SEQ_CLOSED( src_contour );
879     if( !is_closed )
880         CV_WRITE_SEQ_ELEM( end_pt, writer );
881
882     *dst_contour = cvEndWriteSeq( &writer );
883     
884     cvStartReadSeq( *dst_contour, &reader, is_closed );
885     CV_READ_SEQ_ELEM( start_pt, reader );
886
887     reader2 = reader;
888     CV_READ_SEQ_ELEM( pt, reader );
889
890     new_count = count = (*dst_contour)->total;
891     for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
892     {
893         double dx, dy, dist;
894         CV_READ_SEQ_ELEM( end_pt, reader );
895
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) )
900         {
901             new_count--;
902             *((CvPoint2D32f*)reader2.ptr) = start_pt = end_pt;
903             CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
904             CV_READ_SEQ_ELEM( pt, reader );
905             i++;
906             continue;
907         }
908         *((CvPoint2D32f*)reader2.ptr) = start_pt = pt;
909         CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
910         pt = end_pt;
911     }
912
913     if( !is_closed )
914         *((CvPoint2D32f*)reader2.ptr) = pt;
915
916     if( new_count < count )
917         cvSeqPopMulti( *dst_contour, 0, count - new_count );
918
919     cvReleaseMemStorage( &temp_storage );
920
921     return CV_OK;
922 }
923
924
925 CV_IMPL CvSeq*
926 cvApproxPoly( const void*  array, int  header_size,
927               CvMemStorage*  storage, int  method, 
928               double  parameter, int parameter2 )
929 {
930     CvSeq* dst_seq = 0;
931     CvSeq *prev_contour = 0, *parent = 0;
932     CvContour contour_header;
933     CvSeq* src_seq = 0;
934     CvSeqBlock block;
935     int recursive = 0;
936
937     CV_FUNCNAME( "cvApproxPoly" );
938
939     __BEGIN__;
940
941     if( CV_IS_SEQ( array ))
942     {
943         src_seq = (CvSeq*)array;
944         if( !CV_IS_SEQ_POLYLINE( src_seq ))
945             CV_ERROR( CV_StsBadArg, "Unsupported sequence type" );
946
947         recursive = parameter2;
948
949         if( !storage )
950             storage = src_seq->storage;
951     }
952     else
953     {
954         CV_CALL( src_seq = cvPointSeqFromMat(
955             CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0),
956             array, &contour_header, &block ));
957     }
958
959     if( !storage )
960         CV_ERROR( CV_StsNullPtr, "NULL storage pointer " );
961
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" );
965
966     if( header_size == 0 )
967         header_size = src_seq->header_size;
968
969     if( !CV_IS_SEQ_POLYLINE( src_seq ))
970     {
971         if( CV_IS_SEQ_CHAIN( src_seq ))
972         {
973             CV_ERROR( CV_StsBadArg, "Input curves are not polygonal. "
974                                     "Use cvApproxChains first" );
975         }
976         else
977         {
978             CV_ERROR( CV_StsBadArg, "Input curves have uknown type" );
979         }
980     }
981
982     if( header_size == 0 )
983         header_size = src_seq->header_size;
984
985     if( header_size < (int)sizeof(CvContour) )
986         CV_ERROR( CV_StsBadSize, "New header size must be non-less than sizeof(CvContour)" );
987
988     if( method != CV_POLY_APPROX_DP )
989         CV_ERROR( CV_StsOutOfRange, "Unknown approximation method" );
990
991     while( src_seq != 0 )
992     {
993         CvSeq *contour = 0;
994
995         switch (method)
996         {
997         case CV_POLY_APPROX_DP:
998             if( parameter < 0 )
999                 CV_ERROR( CV_StsOutOfRange, "Accuracy must be non-negative" );
1000
1001             if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 )
1002             {
1003                 IPPI_CALL( icvApproxPolyDP_32s( src_seq, header_size, storage,
1004                                                 &contour, (float)parameter ));
1005             }
1006             else
1007             {
1008                 IPPI_CALL( icvApproxPolyDP_32f( src_seq, header_size, storage,
1009                                                 &contour, (float)parameter ));
1010             }
1011             break;
1012         default:
1013             assert(0);
1014             CV_ERROR( CV_StsBadArg, "Invalid approximation method" );
1015         }
1016
1017         assert( contour );
1018
1019         if( header_size >= (int)sizeof(CvContour))
1020             cvBoundingRect( contour, 1 );
1021
1022         contour->v_prev = parent;
1023         contour->h_prev = prev_contour;
1024
1025         if( prev_contour )
1026             prev_contour->h_next = contour;
1027         else if( parent )
1028             parent->v_next = contour;
1029         prev_contour = contour;
1030         if( !dst_seq )
1031             dst_seq = prev_contour;
1032
1033         if( !recursive )
1034             break;
1035
1036         if( src_seq->v_next )
1037         {
1038             assert( prev_contour != 0 );
1039             parent = prev_contour;
1040             prev_contour = 0;
1041             src_seq = src_seq->v_next;
1042         }
1043         else
1044         {
1045             while( src_seq->h_next == 0 )
1046             {
1047                 src_seq = src_seq->v_prev;
1048                 if( src_seq == 0 )
1049                     break;
1050                 prev_contour = parent;
1051                 if( parent )
1052                     parent = parent->v_prev;
1053             }
1054             if( src_seq )
1055                 src_seq = src_seq->h_next;
1056         }
1057     }
1058
1059     __END__;
1060
1061     return dst_seq;
1062 }
1063
1064 /* End of file. */