diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index df6e527e6c..58888c7aae 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -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 diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index 0c2bd33eb4..8a91777b39 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -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 */ diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index d369f15192..f8b08fa527 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -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;