preliminary version of a multi-level findcontours

pull/13383/head
Vadim Pisarevsky 13 years ago
parent 9b2d7e19db
commit f196dd5ff7
  1. 3
      modules/imgproc/include/opencv2/imgproc/imgproc.hpp
  2. 3
      modules/imgproc/include/opencv2/imgproc/types_c.h
  3. 369
      modules/imgproc/src/contours.cpp

@ -971,7 +971,8 @@ enum
RETR_EXTERNAL=CV_RETR_EXTERNAL, //!< retrieve only the most external (top-level) contours 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_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_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 //! the contour approximation algorithm

@ -309,7 +309,8 @@ enum
CV_RETR_EXTERNAL=0, CV_RETR_EXTERNAL=0,
CV_RETR_LIST=1, CV_RETR_LIST=1,
CV_RETR_CCOMP=2, CV_RETR_CCOMP=2,
CV_RETR_TREE=3 CV_RETR_TREE=3,
CV_RETR_FLOODFILL=4
}; };
/* Contour approximation methods */ /* Contour approximation methods */

@ -156,7 +156,10 @@ typedef struct _CvContourScanner
0 - external only 0 - external only
1 - all the contours w/o any hierarchy 1 - all the contours w/o any hierarchy
2 - connected components (i.e. two-level structure - 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 subst_flag;
int seq_type1; /* type of fetched contours */ int seq_type1; /* type of fetched contours */
int header_size1; /* hdr size of fetched contours */ int header_size1; /* hdr size of fetched contours */
@ -164,7 +167,7 @@ typedef struct _CvContourScanner
int seq_type2; /* */ int seq_type2; /* */
int header_size2; /* the same for approx. contours */ int header_size2; /* the same for approx. contours */
int elem_size2; /* */ int elem_size2; /* */
_CvContourInfo *cinfo_table[126]; _CvContourInfo *cinfo_table[128];
} }
_CvContourScanner; _CvContourScanner;
@ -180,23 +183,21 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
int header_size, int mode, int header_size, int mode,
int method, CvPoint offset ) int method, CvPoint offset )
{ {
int y;
int step;
CvSize size;
uchar *img = 0;
CvContourScanner scanner = 0;
if( !storage ) if( !storage )
CV_Error( CV_StsNullPtr, "" ); CV_Error( CV_StsNullPtr, "" );
CvMat stub, *mat = cvGetMat( _img, &stub ); CvMat stub, *mat = cvGetMat( _img, &stub );
if( !CV_IS_MASK_ARR( mat )) if( CV_MAT_TYPE(mat->type) == CV_32SC1 && mode == CV_RETR_CCOMP )
CV_Error( CV_StsUnsupportedFormat, "[Start]FindContours support only 8uC1 images" ); 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 ); CvSize size = cvSize( mat->width, mat->height );
step = mat->step; int step = mat->step;
img = (uchar*)(mat->data.ptr); uchar* img = (uchar*)(mat->data.ptr);
if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS ) if( method < 0 || method > CV_CHAIN_APPROX_TC89_KCOS )
CV_Error( CV_StsOutOfRange, "" ); CV_Error( CV_StsOutOfRange, "" );
@ -204,8 +205,8 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour ))) if( header_size < (int) (method == CV_CHAIN_CODE ? sizeof( CvChain ) : sizeof( CvContour )))
CV_Error( CV_StsBadSize, "" ); CV_Error( CV_StsBadSize, "" );
scanner = (CvContourScanner)cvAlloc( sizeof( *scanner )); CvContourScanner scanner = (CvContourScanner)cvAlloc( sizeof( *scanner ));
memset( scanner, 0, sizeof( *scanner )); memset( scanner, 0, sizeof(*scanner) );
scanner->storage1 = scanner->storage2 = storage; scanner->storage1 = scanner->storage2 = storage;
scanner->img0 = (schar *) img; scanner->img0 = (schar *) img;
@ -284,16 +285,20 @@ cvStartFindContours( void* _img, CvMemStorage* storage,
} }
/* make zero borders */ /* make zero borders */
memset( img, 0, size.width ); int esz = CV_ELEM_SIZE(mat->type);
memset( img + step * (size.height - 1), 0, size.width ); 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 */ /* 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; 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 * CvSeq *
cvFindNextContour( CvContourScanner scanner ) 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 ) if( !scanner )
CV_Error( CV_StsNullPtr, "" ); CV_Error( CV_StsNullPtr, "" );
icvEndProcessContour( scanner ); icvEndProcessContour( scanner );
/* initialize local state */ /* initialize local state */
img0 = scanner->img0; schar* img0 = scanner->img0;
img = scanner->img; schar* img = scanner->img;
step = scanner->img_step; int step = scanner->img_step;
x = scanner->pt.x; int step_i = step / sizeof(int);
y = scanner->pt.y; int x = scanner->pt.x;
width = scanner->img_size.width; int y = scanner->pt.y;
height = scanner->img_size.height; int width = scanner->img_size.width;
mode = scanner->mode; int height = scanner->img_size.height;
lnbd = scanner->lnbd; int mode = scanner->mode;
nbd = scanner->nbd; CvPoint lnbd = scanner->lnbd;
int nbd = scanner->nbd;
prev = img[x - 1]; 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 ) 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++ ) for( ; x < width; x++ )
{ {
int p = img[x]; 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;
if( p != prev )
{ {
_CvContourInfo *par_info = 0; _CvContourInfo *par_info = 0;
_CvContourInfo *l_cinfo = 0; _CvContourInfo *l_cinfo = 0;
@ -835,13 +1047,16 @@ cvFindNextContour( CvContourScanner scanner )
int is_hole = 0; int is_hole = 0;
CvPoint origin; 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 */ /* 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; goto resume_scan;
if( prev & -2 ) if( prev & new_mask )
{ {
lnbd.x = x - 1; lnbd.x = x - 1;
} }
@ -855,16 +1070,16 @@ cvFindNextContour( CvContourScanner scanner )
origin.x = x - is_hole; origin.x = x - is_hole;
/* find contour parent */ /* 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); par_info = &(scanner->frame_info);
} }
else else
{ {
int lval = img0[lnbd.y * step + lnbd.x] & 0x7f; int lval = (img0_i ?
_CvContourInfo *cur = scanner->cinfo_table[lval - 2]; img0_i[lnbd.y * step_i + lnbd.x] :
(int)img0[lnbd.y * step + lnbd.x]) & 0x7f;
assert( lval >= 2 ); _CvContourInfo *cur = scanner->cinfo_table[lval];
/* find the first bounding contour */ /* find the first bounding contour */
while( cur ) while( cur )
@ -874,10 +1089,14 @@ cvFindNextContour( CvContourScanner scanner )
{ {
if( par_info ) if( par_info )
{ {
if( icvTraceContour( scanner->img0 + if( (img0_i &&
par_info->origin.y * step + icvTraceContour_32s( img0_i + par_info->origin.y * step_i +
par_info->origin.x, step, img + lnbd.x, par_info->origin.x, step_i, img_i + lnbd.x,
par_info->is_hole ) > 0 ) 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; break;
} }
par_info = cur; par_info = cur;
@ -929,21 +1148,34 @@ cvFindNextContour( CvContourScanner scanner )
v.ci = l_cinfo; v.ci = l_cinfo;
cvSetAdd( scanner->cinfo_set, 0, &v.se ); cvSetAdd( scanner->cinfo_set, 0, &v.se );
l_cinfo = v.ci; l_cinfo = v.ci;
int lval;
icvFetchContourEx( img + x - is_hole, step, if( img_i )
cvPoint( origin.x + scanner->offset.x, {
origin.y + scanner->offset.y), lval = img_i[x - is_hole] & 127;
seq, scanner->approx_method1, icvFetchContourEx_32s(img_i + x - is_hole, step_i,
nbd, &(l_cinfo->rect) ); 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.x -= scanner->offset.x;
l_cinfo->rect.y -= scanner->offset.y; l_cinfo->rect.y -= scanner->offset.y;
l_cinfo->next = scanner->cinfo_table[nbd - 2]; l_cinfo->next = scanner->cinfo_table[lval];
scanner->cinfo_table[nbd - 2] = l_cinfo; scanner->cinfo_table[lval] = l_cinfo;
/* change nbd */
nbd = (nbd + 1) & 127;
nbd += nbd == 0 ? 3 : 0;
} }
l_cinfo->is_hole = is_hole; l_cinfo->is_hole = is_hole;
@ -979,7 +1211,7 @@ cvFindNextContour( CvContourScanner scanner )
cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) ); cvSaveMemStoragePos( scanner->storage2, &(scanner->backup_pos2) );
scanner->l_cinfo = l_cinfo; 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->pt.y = y;
scanner->lnbd = lnbd; scanner->lnbd = lnbd;
scanner->img = (schar *) img; scanner->img = (schar *) img;
@ -1001,7 +1233,6 @@ cvFindNextContour( CvContourScanner scanner )
lnbd.y = y + 1; lnbd.y = y + 1;
x = 1; x = 1;
prev = 0; prev = 0;
} /* end of loop on y */ } /* end of loop on y */
return 0; return 0;

Loading…
Cancel
Save