| /*M/////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
| // |
| // By downloading, copying, installing or using the software you agree to this license. |
| // If you do not agree to this license, do not download, install, |
| // copy or use the software. |
| // |
| // |
| // Intel License Agreement |
| // For Open Source Computer Vision Library |
| // |
| // Copyright (C) 2000, Intel Corporation, all rights reserved. |
| // Third party copyrights are property of their respective owners. |
| // |
| // Redistribution and use in source and binary forms, with or without modification, |
| // are permitted provided that the following conditions are met: |
| // |
| // * Redistribution's of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistribution's in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // * The name of Intel Corporation may not be used to endorse or promote products |
| // derived from this software without specific prior written permission. |
| // |
| // This software is provided by the copyright holders and contributors "as is" and |
| // any express or implied warranties, including, but not limited to, the implied |
| // warranties of merchantability and fitness for a particular purpose are disclaimed. |
| // In no event shall the Intel Corporation or contributors be liable for any direct, |
| // indirect, incidental, special, exemplary, or consequential damages |
| // (including, but not limited to, procurement of substitute goods or services; |
| // loss of use, data, or profits; or business interruption) however caused |
| // and on any theory of liability, whether in contract, strict liability, |
| // or tort (including negligence or otherwise) arising in any way out of |
| // the use of this software, even if advised of the possibility of such damage. |
| // |
| //M*/ |
| #include "_cv.h" |
| |
| /****************************************************************************************\ |
| * Chain Approximation * |
| \****************************************************************************************/ |
| |
| typedef struct _CvPtInfo |
| { |
| CvPoint pt; |
| int k; /* support region */ |
| int s; /* curvature value */ |
| struct _CvPtInfo *next; |
| } |
| _CvPtInfo; |
| |
| |
| /* curvature: 0 - 1-curvature, 1 - k-cosine curvature. */ |
| CvStatus |
| icvApproximateChainTC89( CvChain* chain, |
| int header_size, |
| CvMemStorage* storage, |
| CvSeq** contour, |
| int method ) |
| { |
| static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 }; |
| |
| char local_buffer[1 << 16]; |
| char* buffer = local_buffer; |
| int buffer_size; |
| |
| _CvPtInfo temp; |
| _CvPtInfo *array, *first = 0, *current = 0, *prev_current = 0; |
| int i, j, i1, i2, s, len; |
| int count; |
| |
| CvChainPtReader reader; |
| CvSeqWriter writer; |
| CvPoint pt = chain->origin; |
| |
| assert( chain && contour && buffer ); |
| |
| buffer_size = (chain->total + 8) * sizeof( _CvPtInfo ); |
| |
| *contour = 0; |
| |
| if( !CV_IS_SEQ_CHAIN_CONTOUR( chain )) |
| return CV_BADFLAG_ERR; |
| |
| if( header_size < (int)sizeof(CvContour) ) |
| return CV_BADSIZE_ERR; |
| |
| cvStartWriteSeq( (chain->flags & ~CV_SEQ_ELTYPE_MASK) | CV_SEQ_ELTYPE_POINT, |
| header_size, sizeof( CvPoint ), storage, &writer ); |
| |
| if( chain->total == 0 ) |
| { |
| CV_WRITE_SEQ_ELEM( pt, writer ); |
| goto exit_function; |
| } |
| |
| cvStartReadChainPoints( chain, &reader ); |
| |
| if( method > CV_CHAIN_APPROX_SIMPLE && buffer_size > (int)sizeof(local_buffer)) |
| { |
| buffer = (char *) cvAlloc( buffer_size ); |
| if( !buffer ) |
| return CV_OUTOFMEM_ERR; |
| } |
| |
| array = (_CvPtInfo *) buffer; |
| count = chain->total; |
| |
| temp.next = 0; |
| current = &temp; |
| |
| /* Pass 0. |
| Restores all the digital curve points from the chain code. |
| Removes the points (from the resultant polygon) |
| that have zero 1-curvature */ |
| for( i = 0; i < count; i++ ) |
| { |
| int prev_code = *reader.prev_elem; |
| |
| reader.prev_elem = reader.ptr; |
| CV_READ_CHAIN_POINT( pt, reader ); |
| |
| /* calc 1-curvature */ |
| s = abs_diff[reader.code - prev_code + 7]; |
| |
| if( method <= CV_CHAIN_APPROX_SIMPLE ) |
| { |
| if( method == CV_CHAIN_APPROX_NONE || s != 0 ) |
| { |
| CV_WRITE_SEQ_ELEM( pt, writer ); |
| } |
| } |
| else |
| { |
| if( s != 0 ) |
| current = current->next = array + i; |
| array[i].s = s; |
| array[i].pt = pt; |
| } |
| } |
| |
| //assert( pt.x == chain->origin.x && pt.y == chain->origin.y ); |
| |
| if( method <= CV_CHAIN_APPROX_SIMPLE ) |
| goto exit_function; |
| |
| current->next = 0; |
| |
| len = i; |
| current = temp.next; |
| |
| assert( current ); |
| |
| /* Pass 1. |
| Determines support region for all the remained points */ |
| do |
| { |
| CvPoint pt0; |
| int k, l = 0, d_num = 0; |
| |
| i = (int)(current - array); |
| pt0 = array[i].pt; |
| |
| /* determine support region */ |
| for( k = 1;; k++ ) |
| { |
| int lk, dk_num; |
| int dx, dy; |
| Cv32suf d; |
| |
| assert( k <= len ); |
| |
| /* calc indices */ |
| i1 = i - k; |
| i1 += i1 < 0 ? len : 0; |
| i2 = i + k; |
| i2 -= i2 >= len ? len : 0; |
| |
| dx = array[i2].pt.x - array[i1].pt.x; |
| dy = array[i2].pt.y - array[i1].pt.y; |
| |
| /* distance between p_(i - k) and p_(i + k) */ |
| lk = dx * dx + dy * dy; |
| |
| /* distance between p_i and the line (p_(i-k), p_(i+k)) */ |
| dk_num = (pt0.x - array[i1].pt.x) * dy - (pt0.y - array[i1].pt.y) * dx; |
| d.f = (float) (((double) d_num) * lk - ((double) dk_num) * l); |
| |
| if( k > 1 && (l >= lk || ((d_num > 0 && d.i <= 0) || (d_num < 0 && d.i >= 0)))) |
| break; |
| |
| d_num = dk_num; |
| l = lk; |
| } |
| |
| current->k = --k; |
| |
| /* determine cosine curvature if it should be used */ |
| if( method == CV_CHAIN_APPROX_TC89_KCOS ) |
| { |
| /* calc k-cosine curvature */ |
| for( j = k, s = 0; j > 0; j-- ) |
| { |
| double temp_num; |
| int dx1, dy1, dx2, dy2; |
| Cv32suf sk; |
| |
| i1 = i - j; |
| i1 += i1 < 0 ? len : 0; |
| i2 = i + j; |
| i2 -= i2 >= len ? len : 0; |
| |
| dx1 = array[i1].pt.x - pt0.x; |
| dy1 = array[i1].pt.y - pt0.y; |
| dx2 = array[i2].pt.x - pt0.x; |
| dy2 = array[i2].pt.y - pt0.y; |
| |
| if( (dx1 | dy1) == 0 || (dx2 | dy2) == 0 ) |
| break; |
| |
| temp_num = dx1 * dx2 + dy1 * dy2; |
| temp_num = |
| (float) (temp_num / |
| sqrt( ((double)dx1 * dx1 + (double)dy1 * dy1) * |
| ((double)dx2 * dx2 + (double)dy2 * dy2) )); |
| sk.f = (float) (temp_num + 1.1); |
| |
| assert( 0 <= sk.f && sk.f <= 2.2 ); |
| if( j < k && sk.i <= s ) |
| break; |
| |
| s = sk.i; |
| } |
| current->s = s; |
| } |
| current = current->next; |
| } |
| while( current != 0 ); |
| |
| prev_current = &temp; |
| current = temp.next; |
| |
| /* Pass 2. |
| Performs non-maxima supression */ |
| do |
| { |
| int k2 = current->k >> 1; |
| |
| s = current->s; |
| i = (int)(current - array); |
| |
| for( j = 1; j <= k2; j++ ) |
| { |
| i2 = i - j; |
| i2 += i2 < 0 ? len : 0; |
| |
| if( array[i2].s > s ) |
| break; |
| |
| i2 = i + j; |
| i2 -= i2 >= len ? len : 0; |
| |
| if( array[i2].s > s ) |
| break; |
| } |
| |
| if( j <= k2 ) /* exclude point */ |
| { |
| prev_current->next = current->next; |
| current->s = 0; /* "clear" point */ |
| } |
| else |
| prev_current = current; |
| current = current->next; |
| } |
| while( current != 0 ); |
| |
| /* Pass 3. |
| Removes non-dominant points with 1-length support region */ |
| current = temp.next; |
| assert( current ); |
| prev_current = &temp; |
| |
| do |
| { |
| if( current->k == 1 ) |
| { |
| s = current->s; |
| i = (int)(current - array); |
| |
| i1 = i - 1; |
| i1 += i1 < 0 ? len : 0; |
| |
| i2 = i + 1; |
| i2 -= i2 >= len ? len : 0; |
| |
| if( s <= array[i1].s || s <= array[i2].s ) |
| { |
| prev_current->next = current->next; |
| current->s = 0; |
| } |
| else |
| prev_current = current; |
| } |
| else |
| prev_current = current; |
| current = current->next; |
| } |
| while( current != 0 ); |
| |
| if( method == CV_CHAIN_APPROX_TC89_KCOS ) |
| goto copy_vect; |
| |
| /* Pass 4. |
| Cleans remained couples of points */ |
| assert( temp.next ); |
| |
| if( array[0].s != 0 && array[len - 1].s != 0 ) /* specific case */ |
| { |
| for( i1 = 1; i1 < len && array[i1].s != 0; i1++ ) |
| { |
| array[i1 - 1].s = 0; |
| } |
| if( i1 == len ) |
| goto copy_vect; /* all points survived */ |
| i1--; |
| |
| for( i2 = len - 2; i2 > 0 && array[i2].s != 0; i2-- ) |
| { |
| array[i2].next = 0; |
| array[i2 + 1].s = 0; |
| } |
| i2++; |
| |
| if( i1 == 0 && i2 == len - 1 ) /* only two points */ |
| { |
| i1 = (int)(array[0].next - array); |
| array[len] = array[0]; /* move to the end */ |
| array[len].next = 0; |
| array[len - 1].next = array + len; |
| } |
| temp.next = array + i1; |
| } |
| |
| current = temp.next; |
| first = prev_current = &temp; |
| count = 1; |
| |
| /* do last pass */ |
| do |
| { |
| if( current->next == 0 || current->next - current != 1 ) |
| { |
| if( count >= 2 ) |
| { |
| if( count == 2 ) |
| { |
| int s1 = prev_current->s; |
| int s2 = current->s; |
| |
| if( s1 > s2 || (s1 == s2 && prev_current->k <= current->k) ) |
| /* remove second */ |
| prev_current->next = current->next; |
| else |
| /* remove first */ |
| first->next = current; |
| } |
| else |
| first->next->next = current; |
| } |
| first = current; |
| count = 1; |
| } |
| else |
| count++; |
| prev_current = current; |
| current = current->next; |
| } |
| while( current != 0 ); |
| |
| copy_vect: |
| |
| /* gather points */ |
| current = temp.next; |
| assert( current ); |
| |
| do |
| { |
| CV_WRITE_SEQ_ELEM( current->pt, writer ); |
| current = current->next; |
| } |
| while( current != 0 ); |
| |
| exit_function: |
| |
| *contour = cvEndWriteSeq( &writer ); |
| |
| assert( writer.seq->total > 0 ); |
| |
| if( buffer != local_buffer ) |
| cvFree( &buffer ); |
| return CV_OK; |
| } |
| |
| |
| /*Applies some approximation algorithm to chain-coded contour(s) and |
| converts it/them to polygonal representation */ |
| CV_IMPL CvSeq* |
| cvApproxChains( CvSeq* src_seq, |
| CvMemStorage* storage, |
| int method, |
| double /*parameter*/, |
| int minimal_perimeter, |
| int recursive ) |
| { |
| CvSeq *prev_contour = 0, *parent = 0; |
| CvSeq *dst_seq = 0; |
| |
| CV_FUNCNAME( "cvApproxChains" ); |
| |
| __BEGIN__; |
| |
| if( !src_seq || !storage ) |
| CV_ERROR( CV_StsNullPtr, "" ); |
| if( method > CV_CHAIN_APPROX_TC89_KCOS || method <= 0 || minimal_perimeter < 0 ) |
| CV_ERROR( CV_StsOutOfRange, "" ); |
| |
| while( src_seq != 0 ) |
| { |
| int len = src_seq->total; |
| |
| if( len >= minimal_perimeter ) |
| { |
| CvSeq *contour; |
| |
| switch( method ) |
| { |
| case CV_CHAIN_APPROX_NONE: |
| case CV_CHAIN_APPROX_SIMPLE: |
| case CV_CHAIN_APPROX_TC89_L1: |
| case CV_CHAIN_APPROX_TC89_KCOS: |
| IPPI_CALL( icvApproximateChainTC89( (CvChain *) src_seq, |
| sizeof( CvContour ), storage, |
| (CvSeq**)&contour, method )); |
| break; |
| default: |
| assert(0); |
| CV_ERROR( CV_StsOutOfRange, "" ); |
| } |
| |
| assert( contour ); |
| |
| if( contour->total > 0 ) |
| { |
| cvBoundingRect( contour, 1 ); |
| |
| contour->v_prev = parent; |
| contour->h_prev = prev_contour; |
| |
| if( prev_contour ) |
| prev_contour->h_next = contour; |
| else if( parent ) |
| parent->v_next = contour; |
| prev_contour = contour; |
| if( !dst_seq ) |
| dst_seq = prev_contour; |
| } |
| else /* if resultant contour has zero length, skip it */ |
| { |
| len = -1; |
| } |
| } |
| |
| if( !recursive ) |
| break; |
| |
| if( src_seq->v_next && len >= minimal_perimeter ) |
| { |
| assert( prev_contour != 0 ); |
| parent = prev_contour; |
| prev_contour = 0; |
| src_seq = src_seq->v_next; |
| } |
| else |
| { |
| while( src_seq->h_next == 0 ) |
| { |
| src_seq = src_seq->v_prev; |
| if( src_seq == 0 ) |
| break; |
| prev_contour = parent; |
| if( parent ) |
| parent = parent->v_prev; |
| } |
| if( src_seq ) |
| src_seq = src_seq->h_next; |
| } |
| } |
| |
| __END__; |
| |
| return dst_seq; |
| } |
| |
| |
| /****************************************************************************************\ |
| * Polygonal Approximation * |
| \****************************************************************************************/ |
| |
| /* Ramer-Douglas-Peucker algorithm for polygon simplification */ |
| |
| /* the version for integer point coordinates */ |
| static CvStatus |
| icvApproxPolyDP_32s( CvSeq* src_contour, int header_size, |
| CvMemStorage* storage, |
| CvSeq** dst_contour, float eps ) |
| { |
| int init_iters = 3; |
| CvSlice slice = {0, 0}, right_slice = {0, 0}; |
| CvSeqReader reader, reader2; |
| CvSeqWriter writer; |
| CvPoint start_pt = {INT_MIN, INT_MIN}, end_pt = {0, 0}, pt = {0,0}; |
| int i = 0, j, count = src_contour->total, new_count; |
| int is_closed = CV_IS_SEQ_CLOSED( src_contour ); |
| int le_eps = 0; |
| CvMemStorage* temp_storage = 0; |
| CvSeq* stack = 0; |
| |
| assert( CV_SEQ_ELTYPE(src_contour) == CV_32SC2 ); |
| cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer ); |
| |
| if( src_contour->total == 0 ) |
| { |
| *dst_contour = cvEndWriteSeq( &writer ); |
| return CV_OK; |
| } |
| |
| temp_storage = cvCreateChildMemStorage( storage ); |
| |
| assert( src_contour->first != 0 ); |
| stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage ); |
| eps *= eps; |
| cvStartReadSeq( src_contour, &reader, 0 ); |
| |
| if( !is_closed ) |
| { |
| right_slice.start_index = count; |
| end_pt = *(CvPoint*)(reader.ptr); |
| start_pt = *(CvPoint*)cvGetSeqElem( src_contour, -1 ); |
| |
| if( start_pt.x != end_pt.x || start_pt.y != end_pt.y ) |
| { |
| slice.start_index = 0; |
| slice.end_index = count - 1; |
| cvSeqPush( stack, &slice ); |
| } |
| else |
| { |
| is_closed = 1; |
| init_iters = 1; |
| } |
| } |
| |
| if( is_closed ) |
| { |
| /* 1. Find approximately two farthest points of the contour */ |
| right_slice.start_index = 0; |
| |
| for( i = 0; i < init_iters; i++ ) |
| { |
| int max_dist = 0; |
| cvSetSeqReaderPos( &reader, right_slice.start_index, 1 ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); /* read the first point */ |
| |
| for( j = 1; j < count; j++ ) |
| { |
| int dx, dy, dist; |
| |
| CV_READ_SEQ_ELEM( pt, reader ); |
| dx = pt.x - start_pt.x; |
| dy = pt.y - start_pt.y; |
| |
| dist = dx * dx + dy * dy; |
| |
| if( dist > max_dist ) |
| { |
| max_dist = dist; |
| right_slice.start_index = j; |
| } |
| } |
| |
| le_eps = max_dist <= eps; |
| } |
| |
| /* 2. initialize the stack */ |
| if( !le_eps ) |
| { |
| slice.start_index = cvGetSeqReaderPos( &reader ); |
| slice.end_index = right_slice.start_index += slice.start_index; |
| |
| right_slice.start_index -= right_slice.start_index >= count ? count : 0; |
| right_slice.end_index = slice.start_index; |
| if( right_slice.end_index < right_slice.start_index ) |
| right_slice.end_index += count; |
| |
| cvSeqPush( stack, &right_slice ); |
| cvSeqPush( stack, &slice ); |
| } |
| else |
| CV_WRITE_SEQ_ELEM( start_pt, writer ); |
| } |
| |
| /* 3. run recursive process */ |
| while( stack->total != 0 ) |
| { |
| cvSeqPop( stack, &slice ); |
| |
| cvSetSeqReaderPos( &reader, slice.end_index ); |
| CV_READ_SEQ_ELEM( end_pt, reader ); |
| |
| cvSetSeqReaderPos( &reader, slice.start_index ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| |
| if( slice.end_index > slice.start_index + 1 ) |
| { |
| int dx, dy, dist, max_dist = 0; |
| |
| dx = end_pt.x - start_pt.x; |
| dy = end_pt.y - start_pt.y; |
| |
| assert( dx != 0 || dy != 0 ); |
| |
| for( i = slice.start_index + 1; i < slice.end_index; i++ ) |
| { |
| CV_READ_SEQ_ELEM( pt, reader ); |
| dist = abs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy); |
| |
| if( dist > max_dist ) |
| { |
| max_dist = dist; |
| right_slice.start_index = i; |
| } |
| } |
| |
| le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy); |
| } |
| else |
| { |
| assert( slice.end_index > slice.start_index ); |
| le_eps = 1; |
| /* read starting point */ |
| cvSetSeqReaderPos( &reader, slice.start_index ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| } |
| |
| if( le_eps ) |
| { |
| CV_WRITE_SEQ_ELEM( start_pt, writer ); |
| } |
| else |
| { |
| right_slice.end_index = slice.end_index; |
| slice.end_index = right_slice.start_index; |
| cvSeqPush( stack, &right_slice ); |
| cvSeqPush( stack, &slice ); |
| } |
| } |
| |
| is_closed = CV_IS_SEQ_CLOSED( src_contour ); |
| if( !is_closed ) |
| CV_WRITE_SEQ_ELEM( end_pt, writer ); |
| |
| *dst_contour = cvEndWriteSeq( &writer ); |
| |
| cvStartReadSeq( *dst_contour, &reader, is_closed ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| |
| reader2 = reader; |
| CV_READ_SEQ_ELEM( pt, reader ); |
| |
| new_count = count = (*dst_contour)->total; |
| for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ ) |
| { |
| int dx, dy, dist; |
| CV_READ_SEQ_ELEM( end_pt, reader ); |
| |
| dx = end_pt.x - start_pt.x; |
| dy = end_pt.y - start_pt.y; |
| dist = abs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx); |
| if( (double)dist * dist <= 0.5*eps*((double)dx*dx + (double)dy*dy) && dx != 0 && dy != 0 ) |
| { |
| new_count--; |
| *((CvPoint*)reader2.ptr) = start_pt = end_pt; |
| CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 ); |
| CV_READ_SEQ_ELEM( pt, reader ); |
| i++; |
| continue; |
| } |
| *((CvPoint*)reader2.ptr) = start_pt = pt; |
| CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 ); |
| pt = end_pt; |
| } |
| |
| if( !is_closed ) |
| *((CvPoint*)reader2.ptr) = pt; |
| |
| if( new_count < count ) |
| cvSeqPopMulti( *dst_contour, 0, count - new_count ); |
| |
| cvReleaseMemStorage( &temp_storage ); |
| |
| return CV_OK; |
| } |
| |
| |
| /* the version for integer point coordinates */ |
| static CvStatus |
| icvApproxPolyDP_32f( CvSeq* src_contour, int header_size, |
| CvMemStorage* storage, |
| CvSeq** dst_contour, float eps ) |
| { |
| int init_iters = 3; |
| CvSlice slice = {0, 0}, right_slice = {0, 0}; |
| CvSeqReader reader, reader2; |
| CvSeqWriter writer; |
| CvPoint2D32f start_pt = {-1e6f, -1e6f}, end_pt = {0, 0}, pt = {0,0}; |
| int i = 0, j, count = src_contour->total, new_count; |
| int is_closed = CV_IS_SEQ_CLOSED( src_contour ); |
| int le_eps = 0; |
| CvMemStorage* temp_storage = 0; |
| CvSeq* stack = 0; |
| |
| assert( CV_SEQ_ELTYPE(src_contour) == CV_32FC2 ); |
| cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer ); |
| |
| if( src_contour->total == 0 ) |
| { |
| *dst_contour = cvEndWriteSeq( &writer ); |
| return CV_OK; |
| } |
| |
| temp_storage = cvCreateChildMemStorage( storage ); |
| |
| assert( src_contour->first != 0 ); |
| stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage ); |
| eps *= eps; |
| cvStartReadSeq( src_contour, &reader, 0 ); |
| |
| if( !is_closed ) |
| { |
| right_slice.start_index = count; |
| end_pt = *(CvPoint2D32f*)(reader.ptr); |
| start_pt = *(CvPoint2D32f*)cvGetSeqElem( src_contour, -1 ); |
| |
| if( fabs(start_pt.x - end_pt.x) > FLT_EPSILON || |
| fabs(start_pt.y - end_pt.y) > FLT_EPSILON ) |
| { |
| slice.start_index = 0; |
| slice.end_index = count - 1; |
| cvSeqPush( stack, &slice ); |
| } |
| else |
| { |
| is_closed = 1; |
| init_iters = 1; |
| } |
| } |
| |
| if( is_closed ) |
| { |
| /* 1. Find approximately two farthest points of the contour */ |
| right_slice.start_index = 0; |
| |
| for( i = 0; i < init_iters; i++ ) |
| { |
| double max_dist = 0; |
| cvSetSeqReaderPos( &reader, right_slice.start_index, 1 ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); /* read the first point */ |
| |
| for( j = 1; j < count; j++ ) |
| { |
| double dx, dy, dist; |
| |
| CV_READ_SEQ_ELEM( pt, reader ); |
| dx = pt.x - start_pt.x; |
| dy = pt.y - start_pt.y; |
| |
| dist = dx * dx + dy * dy; |
| |
| if( dist > max_dist ) |
| { |
| max_dist = dist; |
| right_slice.start_index = j; |
| } |
| } |
| |
| le_eps = max_dist <= eps; |
| } |
| |
| /* 2. initialize the stack */ |
| if( !le_eps ) |
| { |
| slice.start_index = cvGetSeqReaderPos( &reader ); |
| slice.end_index = right_slice.start_index += slice.start_index; |
| |
| right_slice.start_index -= right_slice.start_index >= count ? count : 0; |
| right_slice.end_index = slice.start_index; |
| if( right_slice.end_index < right_slice.start_index ) |
| right_slice.end_index += count; |
| |
| cvSeqPush( stack, &right_slice ); |
| cvSeqPush( stack, &slice ); |
| } |
| else |
| CV_WRITE_SEQ_ELEM( start_pt, writer ); |
| } |
| |
| /* 3. run recursive process */ |
| while( stack->total != 0 ) |
| { |
| cvSeqPop( stack, &slice ); |
| |
| cvSetSeqReaderPos( &reader, slice.end_index ); |
| CV_READ_SEQ_ELEM( end_pt, reader ); |
| |
| cvSetSeqReaderPos( &reader, slice.start_index ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| |
| if( slice.end_index > slice.start_index + 1 ) |
| { |
| double dx, dy, dist, max_dist = 0; |
| |
| dx = end_pt.x - start_pt.x; |
| dy = end_pt.y - start_pt.y; |
| |
| assert( dx != 0 || dy != 0 ); |
| |
| for( i = slice.start_index + 1; i < slice.end_index; i++ ) |
| { |
| CV_READ_SEQ_ELEM( pt, reader ); |
| dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy); |
| |
| if( dist > max_dist ) |
| { |
| max_dist = dist; |
| right_slice.start_index = i; |
| } |
| } |
| |
| le_eps = (double)max_dist * max_dist <= eps * ((double)dx * dx + (double)dy * dy); |
| } |
| else |
| { |
| assert( slice.end_index > slice.start_index ); |
| le_eps = 1; |
| /* read starting point */ |
| cvSetSeqReaderPos( &reader, slice.start_index ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| } |
| |
| if( le_eps ) |
| { |
| CV_WRITE_SEQ_ELEM( start_pt, writer ); |
| } |
| else |
| { |
| right_slice.end_index = slice.end_index; |
| slice.end_index = right_slice.start_index; |
| cvSeqPush( stack, &right_slice ); |
| cvSeqPush( stack, &slice ); |
| } |
| } |
| |
| is_closed = CV_IS_SEQ_CLOSED( src_contour ); |
| if( !is_closed ) |
| CV_WRITE_SEQ_ELEM( end_pt, writer ); |
| |
| *dst_contour = cvEndWriteSeq( &writer ); |
| |
| cvStartReadSeq( *dst_contour, &reader, is_closed ); |
| CV_READ_SEQ_ELEM( start_pt, reader ); |
| |
| reader2 = reader; |
| CV_READ_SEQ_ELEM( pt, reader ); |
| |
| new_count = count = (*dst_contour)->total; |
| for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ ) |
| { |
| double dx, dy, dist; |
| CV_READ_SEQ_ELEM( end_pt, reader ); |
| |
| dx = end_pt.x - start_pt.x; |
| dy = end_pt.y - start_pt.y; |
| dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx); |
| if( (double)dist * dist <= 0.5*eps*((double)dx*dx + (double)dy*dy) ) |
| { |
| new_count--; |
| *((CvPoint2D32f*)reader2.ptr) = start_pt = end_pt; |
| CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 ); |
| CV_READ_SEQ_ELEM( pt, reader ); |
| i++; |
| continue; |
| } |
| *((CvPoint2D32f*)reader2.ptr) = start_pt = pt; |
| CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 ); |
| pt = end_pt; |
| } |
| |
| if( !is_closed ) |
| *((CvPoint2D32f*)reader2.ptr) = pt; |
| |
| if( new_count < count ) |
| cvSeqPopMulti( *dst_contour, 0, count - new_count ); |
| |
| cvReleaseMemStorage( &temp_storage ); |
| |
| return CV_OK; |
| } |
| |
| |
| CV_IMPL CvSeq* |
| cvApproxPoly( const void* array, int header_size, |
| CvMemStorage* storage, int method, |
| double parameter, int parameter2 ) |
| { |
| CvSeq* dst_seq = 0; |
| CvSeq *prev_contour = 0, *parent = 0; |
| CvContour contour_header; |
| CvSeq* src_seq = 0; |
| CvSeqBlock block; |
| int recursive = 0; |
| |
| CV_FUNCNAME( "cvApproxPoly" ); |
| |
| __BEGIN__; |
| |
| if( CV_IS_SEQ( array )) |
| { |
| src_seq = (CvSeq*)array; |
| if( !CV_IS_SEQ_POLYLINE( src_seq )) |
| CV_ERROR( CV_StsBadArg, "Unsupported sequence type" ); |
| |
| recursive = parameter2; |
| |
| if( !storage ) |
| storage = src_seq->storage; |
| } |
| else |
| { |
| CV_CALL( src_seq = cvPointSeqFromMat( |
| CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0), |
| array, &contour_header, &block )); |
| } |
| |
| if( !storage ) |
| CV_ERROR( CV_StsNullPtr, "NULL storage pointer " ); |
| |
| if( header_size < 0 ) |
| CV_ERROR( CV_StsOutOfRange, "header_size is negative. " |
| "Pass 0 to make the destination header_size == input header_size" ); |
| |
| if( header_size == 0 ) |
| header_size = src_seq->header_size; |
| |
| if( !CV_IS_SEQ_POLYLINE( src_seq )) |
| { |
| if( CV_IS_SEQ_CHAIN( src_seq )) |
| { |
| CV_ERROR( CV_StsBadArg, "Input curves are not polygonal. " |
| "Use cvApproxChains first" ); |
| } |
| else |
| { |
| CV_ERROR( CV_StsBadArg, "Input curves have uknown type" ); |
| } |
| } |
| |
| if( header_size == 0 ) |
| header_size = src_seq->header_size; |
| |
| if( header_size < (int)sizeof(CvContour) ) |
| CV_ERROR( CV_StsBadSize, "New header size must be non-less than sizeof(CvContour)" ); |
| |
| if( method != CV_POLY_APPROX_DP ) |
| CV_ERROR( CV_StsOutOfRange, "Unknown approximation method" ); |
| |
| while( src_seq != 0 ) |
| { |
| CvSeq *contour = 0; |
| |
| switch (method) |
| { |
| case CV_POLY_APPROX_DP: |
| if( parameter < 0 ) |
| CV_ERROR( CV_StsOutOfRange, "Accuracy must be non-negative" ); |
| |
| if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 ) |
| { |
| IPPI_CALL( icvApproxPolyDP_32s( src_seq, header_size, storage, |
| &contour, (float)parameter )); |
| } |
| else |
| { |
| IPPI_CALL( icvApproxPolyDP_32f( src_seq, header_size, storage, |
| &contour, (float)parameter )); |
| } |
| break; |
| default: |
| assert(0); |
| CV_ERROR( CV_StsBadArg, "Invalid approximation method" ); |
| } |
| |
| assert( contour ); |
| |
| if( header_size >= (int)sizeof(CvContour)) |
| cvBoundingRect( contour, 1 ); |
| |
| contour->v_prev = parent; |
| contour->h_prev = prev_contour; |
| |
| if( prev_contour ) |
| prev_contour->h_next = contour; |
| else if( parent ) |
| parent->v_next = contour; |
| prev_contour = contour; |
| if( !dst_seq ) |
| dst_seq = prev_contour; |
| |
| if( !recursive ) |
| break; |
| |
| if( src_seq->v_next ) |
| { |
| assert( prev_contour != 0 ); |
| parent = prev_contour; |
| prev_contour = 0; |
| src_seq = src_seq->v_next; |
| } |
| else |
| { |
| while( src_seq->h_next == 0 ) |
| { |
| src_seq = src_seq->v_prev; |
| if( src_seq == 0 ) |
| break; |
| prev_contour = parent; |
| if( parent ) |
| parent = parent->v_prev; |
| } |
| if( src_seq ) |
| src_seq = src_seq->h_next; |
| } |
| } |
| |
| __END__; |
| |
| return dst_seq; |
| } |
| |
| /* End of file. */ |