preliminary version of a multi-level findcontours
This commit is contained in:
parent
9b2d7e19db
commit
f196dd5ff7
@ -971,7 +971,8 @@ enum
|
||||
RETR_EXTERNAL=CV_RETR_EXTERNAL, //!< retrieve only the most external (top-level) contours
|
||||
RETR_LIST=CV_RETR_LIST, //!< retrieve all the contours without any hierarchical information
|
||||
RETR_CCOMP=CV_RETR_CCOMP, //!< retrieve the connected components (that can possibly be nested)
|
||||
RETR_TREE=CV_RETR_TREE //!< retrieve all the contours and the whole hierarchy
|
||||
RETR_TREE=CV_RETR_TREE, //!< retrieve all the contours and the whole hierarchy
|
||||
RETR_FLOODFILL=CV_RETR_FLOODFILL
|
||||
};
|
||||
|
||||
//! the contour approximation algorithm
|
||||
|
@ -309,7 +309,8 @@ enum
|
||||
CV_RETR_EXTERNAL=0,
|
||||
CV_RETR_LIST=1,
|
||||
CV_RETR_CCOMP=2,
|
||||
CV_RETR_TREE=3
|
||||
CV_RETR_TREE=3,
|
||||
CV_RETR_FLOODFILL=4
|
||||
};
|
||||
|
||||
/* Contour approximation methods */
|
||||
|
@ -156,7 +156,10 @@ typedef struct _CvContourScanner
|
||||
0 - external only
|
||||
1 - all the contours w/o any hierarchy
|
||||
2 - connected components (i.e. two-level structure -
|
||||
external contours and holes) */
|
||||
external contours and holes),
|
||||
3 - full hierarchy;
|
||||
4 - connected components of a multi-level image
|
||||
*/
|
||||
int subst_flag;
|
||||
int seq_type1; /* type of fetched contours */
|
||||
int header_size1; /* hdr size of fetched contours */
|
||||
@ -164,7 +167,7 @@ typedef struct _CvContourScanner
|
||||
int seq_type2; /* */
|
||||
int header_size2; /* the same for approx. contours */
|
||||
int elem_size2; /* */
|
||||
_CvContourInfo *cinfo_table[126];
|
||||
_CvContourInfo *cinfo_table[128];
|
||||
}
|
||||
_CvContourScanner;
|
||||
|
||||
@ -180,23 +183,21 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
|
||||
int header_size, int mode,
|
||||
int method, CvPoint offset )
|
||||
{
|
||||
int y;
|
||||
int step;
|
||||
CvSize size;
|
||||
uchar *img = 0;
|
||||
CvContourScanner scanner = 0;
|
||||
|
||||
if( !storage )
|
||||
CV_Error( CV_StsNullPtr, "" );
|
||||
|
||||
CvMat stub, *mat = cvGetMat( _img, &stub );
|
||||
|
||||
if( !CV_IS_MASK_ARR( mat ))
|
||||
CV_Error( CV_StsUnsupportedFormat, "[Start]FindContours support only 8uC1 images" );
|
||||
if( CV_MAT_TYPE(mat->type) == CV_32SC1 && mode == CV_RETR_CCOMP )
|
||||
mode = CV_RETR_FLOODFILL;
|
||||
|
||||
if( !((CV_IS_MASK_ARR( mat ) && mode < CV_RETR_FLOODFILL) ||
|
||||
(CV_MAT_TYPE(mat->type) == CV_32SC1 && mode == CV_RETR_FLOODFILL)) )
|
||||
CV_Error( CV_StsUnsupportedFormat, "[Start]FindContours support only 8uC1 and 32sC1 images" );
|
||||
|
||||
size = cvSize( mat->width, mat->height );
|
||||
step = mat->step;
|
||||
img = (uchar*)(mat->data.ptr);
|
||||
CvSize size = cvSize( mat->width, mat->height );
|
||||
int step = mat->step;
|
||||
uchar* img = (uchar*)(mat->data.ptr);
|
||||
|
||||
if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS )
|
||||
CV_Error( CV_StsOutOfRange, "" );
|
||||
@ -204,9 +205,9 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
|
||||
if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour )))
|
||||
CV_Error( CV_StsBadSize, "" );
|
||||
|
||||
scanner = (CvContourScanner)cvAlloc( sizeof( *scanner ));
|
||||
memset( scanner, 0, sizeof( *scanner ));
|
||||
|
||||
CvContourScanner scanner = (CvContourScanner)cvAlloc( sizeof( *scanner ));
|
||||
memset( scanner, 0, sizeof(*scanner) );
|
||||
|
||||
scanner->storage1 = scanner->storage2 = storage;
|
||||
scanner->img0 = (schar *) img;
|
||||
scanner->img = (schar *) (img + step);
|
||||
@ -284,16 +285,20 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
|
||||
}
|
||||
|
||||
/* make zero borders */
|
||||
memset( img, 0, size.width );
|
||||
memset( img + step * (size.height - 1), 0, size.width );
|
||||
int esz = CV_ELEM_SIZE(mat->type);
|
||||
memset( img, 0, size.width*esz );
|
||||
memset( img + step * (size.height - 1), 0, size.width*esz );
|
||||
|
||||
for( y = 1, img += step; y < size.height - 1; y++, img += step )
|
||||
img += step;
|
||||
for( int y = 1; y < size.height - 1; y++, img += step )
|
||||
{
|
||||
img[0] = img[size.width - 1] = 0;
|
||||
for( int k = 0; k < esz; k++ )
|
||||
img[k] = img[(size.width - 1)*esz + k] = (schar)0;
|
||||
}
|
||||
|
||||
/* converts all pixels to 0 or 1 */
|
||||
cvThreshold( mat, mat, 0, 1, CV_THRESH_BINARY );
|
||||
if( CV_MAT_TYPE(mat->type) != CV_32S )
|
||||
cvThreshold( mat, mat, 0, 1, CV_THRESH_BINARY );
|
||||
|
||||
return scanner;
|
||||
}
|
||||
@ -790,44 +795,251 @@ icvFetchContourEx( schar* ptr,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
icvTraceContour_32s( int *ptr, int step, int *stop_ptr, int is_hole )
|
||||
{
|
||||
int deltas[16];
|
||||
int *i0 = ptr, *i1, *i3, *i4;
|
||||
int s, s_end;
|
||||
const int right_flag = INT_MIN;
|
||||
const int new_flag = (int)((unsigned)INT_MIN >> 1);
|
||||
const int value_mask = ~(right_flag | new_flag);
|
||||
const int ccomp_val = *i0 & value_mask;
|
||||
|
||||
/* initialize local state */
|
||||
CV_INIT_3X3_DELTAS( deltas, step, 1 );
|
||||
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
|
||||
|
||||
s_end = s = is_hole ? 0 : 4;
|
||||
|
||||
do
|
||||
{
|
||||
s = (s - 1) & 7;
|
||||
i1 = i0 + deltas[s];
|
||||
if( (*i1 & value_mask) == ccomp_val )
|
||||
break;
|
||||
}
|
||||
while( s != s_end );
|
||||
|
||||
i3 = i0;
|
||||
|
||||
/* check single pixel domain */
|
||||
if( s != s_end )
|
||||
{
|
||||
/* follow border */
|
||||
for( ;; )
|
||||
{
|
||||
s_end = s;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
i4 = i3 + deltas[++s];
|
||||
if( (*i4 & value_mask) == ccomp_val )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i3 == stop_ptr || (i4 == i0 && i3 == i1) )
|
||||
break;
|
||||
|
||||
i3 = i4;
|
||||
s = (s + 4) & 7;
|
||||
} /* end of border following loop */
|
||||
}
|
||||
return i3 == stop_ptr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
icvFetchContourEx_32s( int* ptr,
|
||||
int step,
|
||||
CvPoint pt,
|
||||
CvSeq* contour,
|
||||
int _method,
|
||||
CvRect* _rect )
|
||||
{
|
||||
int deltas[16];
|
||||
CvSeqWriter writer;
|
||||
int *i0 = ptr, *i1, *i3, *i4;
|
||||
CvRect rect;
|
||||
int prev_s = -1, s, s_end;
|
||||
int method = _method - 1;
|
||||
const int right_flag = INT_MIN;
|
||||
const int new_flag = (int)((unsigned)INT_MIN >> 1);
|
||||
const int value_mask = ~(right_flag | new_flag);
|
||||
const int ccomp_val = *i0 & value_mask;
|
||||
const int nbd0 = ccomp_val | new_flag;
|
||||
const int nbd1 = nbd0 | right_flag;
|
||||
|
||||
assert( (unsigned) _method <= CV_CHAIN_APPROX_SIMPLE );
|
||||
|
||||
/* initialize local state */
|
||||
CV_INIT_3X3_DELTAS( deltas, step, 1 );
|
||||
memcpy( deltas + 8, deltas, 8 * sizeof( deltas[0] ));
|
||||
|
||||
/* initialize writer */
|
||||
cvStartAppendToSeq( contour, &writer );
|
||||
|
||||
if( method < 0 )
|
||||
((CvChain *)contour)->origin = pt;
|
||||
|
||||
rect.x = rect.width = pt.x;
|
||||
rect.y = rect.height = pt.y;
|
||||
|
||||
s_end = s = CV_IS_SEQ_HOLE( contour ) ? 0 : 4;
|
||||
|
||||
do
|
||||
{
|
||||
s = (s - 1) & 7;
|
||||
i1 = i0 + deltas[s];
|
||||
if( (*i1 & value_mask) == ccomp_val )
|
||||
break;
|
||||
}
|
||||
while( s != s_end );
|
||||
|
||||
if( s == s_end ) /* single pixel domain */
|
||||
{
|
||||
*i0 = nbd1;
|
||||
if( method >= 0 )
|
||||
{
|
||||
CV_WRITE_SEQ_ELEM( pt, writer );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i3 = i0;
|
||||
prev_s = s ^ 4;
|
||||
|
||||
/* follow border */
|
||||
for( ;; )
|
||||
{
|
||||
s_end = s;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
i4 = i3 + deltas[++s];
|
||||
if( (*i4 & value_mask) == ccomp_val )
|
||||
break;
|
||||
}
|
||||
s &= 7;
|
||||
|
||||
/* check "right" bound */
|
||||
if( (unsigned) (s - 1) < (unsigned) s_end )
|
||||
{
|
||||
*i3 = nbd1;
|
||||
}
|
||||
else if( *i3 == ccomp_val )
|
||||
{
|
||||
*i3 = nbd0;
|
||||
}
|
||||
|
||||
if( method < 0 )
|
||||
{
|
||||
schar _s = (schar) s;
|
||||
CV_WRITE_SEQ_ELEM( _s, writer );
|
||||
}
|
||||
else if( s != prev_s || method == 0 )
|
||||
{
|
||||
CV_WRITE_SEQ_ELEM( pt, writer );
|
||||
}
|
||||
|
||||
if( s != prev_s )
|
||||
{
|
||||
/* update bounds */
|
||||
if( pt.x < rect.x )
|
||||
rect.x = pt.x;
|
||||
else if( pt.x > rect.width )
|
||||
rect.width = pt.x;
|
||||
|
||||
if( pt.y < rect.y )
|
||||
rect.y = pt.y;
|
||||
else if( pt.y > rect.height )
|
||||
rect.height = pt.y;
|
||||
}
|
||||
|
||||
prev_s = s;
|
||||
pt.x += icvCodeDeltas[s].x;
|
||||
pt.y += icvCodeDeltas[s].y;
|
||||
|
||||
if( i4 == i0 && i3 == i1 ) break;
|
||||
|
||||
i3 = i4;
|
||||
s = (s + 4) & 7;
|
||||
} /* end of border following loop */
|
||||
}
|
||||
|
||||
rect.width -= rect.x - 1;
|
||||
rect.height -= rect.y - 1;
|
||||
|
||||
cvEndWriteSeq( &writer );
|
||||
|
||||
if( _method != CV_CHAIN_CODE )
|
||||
((CvContour*)contour)->rect = rect;
|
||||
|
||||
assert( (writer.seq->total == 0 && writer.seq->first == 0) ||
|
||||
writer.seq->total > writer.seq->first->count ||
|
||||
(writer.seq->first->prev == writer.seq->first &&
|
||||
writer.seq->first->next == writer.seq->first) );
|
||||
|
||||
if( _rect ) *_rect = rect;
|
||||
}
|
||||
|
||||
|
||||
CvSeq *
|
||||
cvFindNextContour( CvContourScanner scanner )
|
||||
{
|
||||
schar *img0;
|
||||
schar *img;
|
||||
int step;
|
||||
int width, height;
|
||||
int x, y;
|
||||
int prev;
|
||||
CvPoint lnbd;
|
||||
int nbd;
|
||||
int mode;
|
||||
|
||||
if( !scanner )
|
||||
CV_Error( CV_StsNullPtr, "" );
|
||||
icvEndProcessContour( scanner );
|
||||
|
||||
/* initialize local state */
|
||||
img0 = scanner->img0;
|
||||
img = scanner->img;
|
||||
step = scanner->img_step;
|
||||
x = scanner->pt.x;
|
||||
y = scanner->pt.y;
|
||||
width = scanner->img_size.width;
|
||||
height = scanner->img_size.height;
|
||||
mode = scanner->mode;
|
||||
lnbd = scanner->lnbd;
|
||||
nbd = scanner->nbd;
|
||||
|
||||
prev = img[x - 1];
|
||||
schar* img0 = scanner->img0;
|
||||
schar* img = scanner->img;
|
||||
int step = scanner->img_step;
|
||||
int step_i = step / sizeof(int);
|
||||
int x = scanner->pt.x;
|
||||
int y = scanner->pt.y;
|
||||
int width = scanner->img_size.width;
|
||||
int height = scanner->img_size.height;
|
||||
int mode = scanner->mode;
|
||||
CvPoint lnbd = scanner->lnbd;
|
||||
int nbd = scanner->nbd;
|
||||
int prev = img[x - 1];
|
||||
int new_mask = -2;
|
||||
|
||||
if( mode == CV_RETR_FLOODFILL )
|
||||
{
|
||||
prev = ((int*)img)[x - 1];
|
||||
new_mask = INT_MIN >> 1;
|
||||
}
|
||||
|
||||
for( ; y < height; y++, img += step )
|
||||
{
|
||||
int* img0_i = 0;
|
||||
int* img_i = 0;
|
||||
int p = 0;
|
||||
|
||||
if( mode == CV_RETR_FLOODFILL )
|
||||
{
|
||||
img0_i = (int*)img0;
|
||||
img_i = (int*)img;
|
||||
}
|
||||
|
||||
for( ; x < width; x++ )
|
||||
{
|
||||
int p = img[x];
|
||||
|
||||
if( p != prev )
|
||||
if( img_i )
|
||||
{
|
||||
for( ; x < width && ((p = img_i[x]) == prev || (p & ~new_mask) == (prev & ~new_mask)); x++ )
|
||||
prev = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( ; x < width && (p = img[x]) == prev; x++ )
|
||||
;
|
||||
}
|
||||
|
||||
if( x >= width )
|
||||
break;
|
||||
|
||||
{
|
||||
_CvContourInfo *par_info = 0;
|
||||
_CvContourInfo *l_cinfo = 0;
|
||||
@ -835,13 +1047,16 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
int is_hole = 0;
|
||||
CvPoint origin;
|
||||
|
||||
if( !(prev == 0 && p == 1) ) /* if not external contour */
|
||||
/* if not external contour */
|
||||
if( (!img_i && !(prev == 0 && p == 1)) ||
|
||||
(img_i && !(((prev & new_mask) != 0 || prev == 0) && (p & new_mask) == 0)) )
|
||||
{
|
||||
/* check hole */
|
||||
if( p != 0 || prev < 1 )
|
||||
if( (!img_i && (p != 0 || prev < 1)) ||
|
||||
(img_i && ((prev & new_mask) != 0 || (p & new_mask) != 0)))
|
||||
goto resume_scan;
|
||||
|
||||
if( prev & -2 )
|
||||
if( prev & new_mask )
|
||||
{
|
||||
lnbd.x = x - 1;
|
||||
}
|
||||
@ -855,16 +1070,16 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
origin.x = x - is_hole;
|
||||
|
||||
/* find contour parent */
|
||||
if( mode <= 1 || (!is_hole && mode == 2) || lnbd.x <= 0 )
|
||||
if( mode <= 1 || (!is_hole && (mode == CV_RETR_CCOMP || mode == CV_RETR_FLOODFILL)) || lnbd.x <= 0 )
|
||||
{
|
||||
par_info = &(scanner->frame_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
int lval = img0[lnbd.y * step + lnbd.x] & 0x7f;
|
||||
_CvContourInfo *cur = scanner->cinfo_table[lval - 2];
|
||||
|
||||
assert( lval >= 2 );
|
||||
int lval = (img0_i ?
|
||||
img0_i[lnbd.y * step_i + lnbd.x] :
|
||||
(int)img0[lnbd.y * step + lnbd.x]) & 0x7f;
|
||||
_CvContourInfo *cur = scanner->cinfo_table[lval];
|
||||
|
||||
/* find the first bounding contour */
|
||||
while( cur )
|
||||
@ -874,10 +1089,14 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
{
|
||||
if( par_info )
|
||||
{
|
||||
if( icvTraceContour( scanner->img0 +
|
||||
par_info->origin.y * step +
|
||||
par_info->origin.x, step, img + lnbd.x,
|
||||
par_info->is_hole ) > 0 )
|
||||
if( (img0_i &&
|
||||
icvTraceContour_32s( img0_i + par_info->origin.y * step_i +
|
||||
par_info->origin.x, step_i, img_i + lnbd.x,
|
||||
par_info->is_hole ) > 0) ||
|
||||
(!img0_i &&
|
||||
icvTraceContour( img0 + par_info->origin.y * step +
|
||||
par_info->origin.x, step, img + lnbd.x,
|
||||
par_info->is_hole ) > 0) )
|
||||
break;
|
||||
}
|
||||
par_info = cur;
|
||||
@ -929,21 +1148,34 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
v.ci = l_cinfo;
|
||||
cvSetAdd( scanner->cinfo_set, 0, &v.se );
|
||||
l_cinfo = v.ci;
|
||||
int lval;
|
||||
|
||||
icvFetchContourEx( img + x - is_hole, step,
|
||||
cvPoint( origin.x + scanner->offset.x,
|
||||
origin.y + scanner->offset.y),
|
||||
seq, scanner->approx_method1,
|
||||
nbd, &(l_cinfo->rect) );
|
||||
if( img_i )
|
||||
{
|
||||
lval = img_i[x - is_hole] & 127;
|
||||
icvFetchContourEx_32s(img_i + x - is_hole, step_i,
|
||||
cvPoint( origin.x + scanner->offset.x,
|
||||
origin.y + scanner->offset.y),
|
||||
seq, scanner->approx_method1,
|
||||
&(l_cinfo->rect) );
|
||||
}
|
||||
else
|
||||
{
|
||||
lval = nbd;
|
||||
// change nbd
|
||||
nbd = (nbd + 1) & 127;
|
||||
nbd += nbd == 0 ? 3 : 0;
|
||||
icvFetchContourEx( img + x - is_hole, step,
|
||||
cvPoint( origin.x + scanner->offset.x,
|
||||
origin.y + scanner->offset.y),
|
||||
seq, scanner->approx_method1,
|
||||
lval, &(l_cinfo->rect) );
|
||||
}
|
||||
l_cinfo->rect.x -= scanner->offset.x;
|
||||
l_cinfo->rect.y -= scanner->offset.y;
|
||||
|
||||
l_cinfo->next = scanner->cinfo_table[nbd - 2];
|
||||
scanner->cinfo_table[nbd - 2] = l_cinfo;
|
||||
|
||||
/* change nbd */
|
||||
nbd = (nbd + 1) & 127;
|
||||
nbd += nbd == 0 ? 3 : 0;
|
||||
l_cinfo->next = scanner->cinfo_table[lval];
|
||||
scanner->cinfo_table[lval] = l_cinfo;
|
||||
}
|
||||
|
||||
l_cinfo->is_hole = is_hole;
|
||||
@ -979,7 +1211,7 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
|
||||
cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) );
|
||||
scanner->l_cinfo = l_cinfo;
|
||||
scanner->pt.x = x + 1;
|
||||
scanner->pt.x = !img_i ? x + 1 : x + 1 - is_hole;
|
||||
scanner->pt.y = y;
|
||||
scanner->lnbd = lnbd;
|
||||
scanner->img = (schar *) img;
|
||||
@ -1001,7 +1233,6 @@ cvFindNextContour( CvContourScanner scanner )
|
||||
lnbd.y = y + 1;
|
||||
x = 1;
|
||||
prev = 0;
|
||||
|
||||
} /* end of loop on y */
|
||||
|
||||
return 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user