preliminary version of a multi-level findcontours
authorVadim Pisarevsky <no@email>
Sat, 24 Mar 2012 20:03:07 +0000 (20:03 +0000)
committerVadim Pisarevsky <no@email>
Sat, 24 Mar 2012 20:03:07 +0000 (20:03 +0000)
modules/imgproc/include/opencv2/imgproc/imgproc.hpp
modules/imgproc/include/opencv2/imgproc/types_c.h
modules/imgproc/src/contours.cpp

index df6e527..58888c7 100644 (file)
@@ -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
index 0c2bd33..8a91777 100644 (file)
@@ -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 */
index d369f15..f8b08fa 100644 (file)
@@ -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;