|
|
|
@ -40,372 +40,6 @@ |
|
|
|
|
//M*/
|
|
|
|
|
#include "precomp.hpp" |
|
|
|
|
|
|
|
|
|
/****************************************************************************************\
|
|
|
|
|
* Chain Approximation * |
|
|
|
|
\****************************************************************************************/ |
|
|
|
|
|
|
|
|
|
typedef struct _CvPtInfo |
|
|
|
|
{ |
|
|
|
|
CvPoint pt; |
|
|
|
|
int k; /* support region */ |
|
|
|
|
int s; /* curvature value */ |
|
|
|
|
struct _CvPtInfo *next; |
|
|
|
|
} |
|
|
|
|
_CvPtInfo; |
|
|
|
|
|
|
|
|
|
static const CvPoint icvCodeDeltas[8] = |
|
|
|
|
{ {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} }; |
|
|
|
|
|
|
|
|
|
/** Freeman chain reader state */ |
|
|
|
|
typedef struct CvChainPtReader |
|
|
|
|
{ |
|
|
|
|
CV_SEQ_READER_FIELDS() |
|
|
|
|
char code; |
|
|
|
|
CvPoint pt; |
|
|
|
|
schar deltas[8][2]; |
|
|
|
|
} |
|
|
|
|
CvChainPtReader; |
|
|
|
|
|
|
|
|
|
static void cvStartReadChainPoints( CvChain * chain, CvChainPtReader * reader ) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
if( !chain || !reader ) |
|
|
|
|
CV_Error( cv::Error::StsNullPtr, "" ); |
|
|
|
|
|
|
|
|
|
if( chain->elem_size != 1 || chain->header_size < (int)sizeof(CvChain)) |
|
|
|
|
CV_Error( cv::Error::StsBadSize, "" ); |
|
|
|
|
|
|
|
|
|
cvStartReadSeq( (CvSeq *) chain, (CvSeqReader *) reader, 0 ); |
|
|
|
|
|
|
|
|
|
reader->pt = chain->origin; |
|
|
|
|
for( i = 0; i < 8; i++ ) |
|
|
|
|
{ |
|
|
|
|
reader->deltas[i][0] = (schar) icvCodeDeltas[i].x; |
|
|
|
|
reader->deltas[i][1] = (schar) icvCodeDeltas[i].y; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* curvature: 0 - 1-curvature, 1 - k-cosine curvature. */ |
|
|
|
|
CvSeq* icvApproximateChainTC89( CvChain* chain, int header_size, |
|
|
|
|
CvMemStorage* storage, int method ) |
|
|
|
|
{ |
|
|
|
|
static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 }; |
|
|
|
|
|
|
|
|
|
cv::AutoBuffer<_CvPtInfo> buf(chain->total + 8); |
|
|
|
|
|
|
|
|
|
_CvPtInfo temp; |
|
|
|
|
_CvPtInfo *array = buf.data(), *first = 0, *current = 0, *prev_current = 0; |
|
|
|
|
int i, j, i1, i2, s, len; |
|
|
|
|
int count = chain->total; |
|
|
|
|
|
|
|
|
|
CvChainPtReader reader; |
|
|
|
|
CvSeqWriter writer; |
|
|
|
|
CvPoint pt = chain->origin; |
|
|
|
|
|
|
|
|
|
CV_Assert( CV_IS_SEQ_CHAIN_CONTOUR( chain )); |
|
|
|
|
CV_Assert( header_size >= (int)sizeof(CvContour) ); |
|
|
|
|
|
|
|
|
|
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 ); |
|
|
|
|
return cvEndWriteSeq( &writer ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
reader.code = 0; |
|
|
|
|
cvStartReadChainPoints( chain, &reader ); |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//CV_Assert( pt.x == chain->origin.x && pt.y == chain->origin.y );
|
|
|
|
|
|
|
|
|
|
if( method <= cv::CHAIN_APPROX_SIMPLE ) |
|
|
|
|
return cvEndWriteSeq( &writer ); |
|
|
|
|
|
|
|
|
|
current->next = 0; |
|
|
|
|
|
|
|
|
|
len = i; |
|
|
|
|
current = temp.next; |
|
|
|
|
|
|
|
|
|
CV_Assert( current ); |
|
|
|
|
|
|
|
|
|
/* Pass 1.
|
|
|
|
|
Determines support region for all the remained points */ |
|
|
|
|
do |
|
|
|
|
{ |
|
|
|
|
cv::Point2i 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; |
|
|
|
|
|
|
|
|
|
CV_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); |
|
|
|
|
|
|
|
|
|
CV_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 suppression */ |
|
|
|
|
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; |
|
|
|
|
CV_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 */ |
|
|
|
|
CV_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; |
|
|
|
|
CV_Assert( current ); |
|
|
|
|
|
|
|
|
|
do |
|
|
|
|
{ |
|
|
|
|
CV_WRITE_SEQ_ELEM( current->pt, writer ); |
|
|
|
|
current = current->next; |
|
|
|
|
} |
|
|
|
|
while( current != 0 ); |
|
|
|
|
|
|
|
|
|
return cvEndWriteSeq( &writer ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/****************************************************************************************\
|
|
|
|
|
* Polygonal Approximation * |
|
|
|
|
\****************************************************************************************/ |
|
|
|
|