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. 373
      modules/imgproc/src/contours.cpp

@ -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…
Cancel
Save