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 */
int seq_type2; /* */
int header_size2; /* the same for approx. contours */
int elem_size2; /* */
- _CvContourInfo *cinfo_table[126];
+ _CvContourInfo *cinfo_table[128];
}
_CvContourScanner;
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, "" );
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);
}
/* 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;
}
}
+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;
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;
}
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 )
{
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;
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;
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;
lnbd.y = y + 1;
x = 1;
prev = 0;
-
} /* end of loop on y */
return 0;