diff --git a/modules/imgproc/doc/miscellaneous_transformations.rst b/modules/imgproc/doc/miscellaneous_transformations.rst index 2d689fd532..85288b050d 100644 --- a/modules/imgproc/doc/miscellaneous_transformations.rst +++ b/modules/imgproc/doc/miscellaneous_transformations.rst @@ -412,11 +412,11 @@ Calculates the distance to the closest zero pixel for each pixel of the source i .. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, int distanceType, int maskSize ) -.. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize ) +.. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize, int labelType=DIST_LABEL_CCOMP ) -.. ocv:pyfunction:: cv2.distanceTransform(src, distanceType, maskSize[, dst[, labels]]) -> dst, labels +.. ocv:pyfunction:: cv2.distanceTransform(src, distanceType, maskSize[, dst[, labels[, labelType=cv2.DIST_LABEL_CCOMP]]]) -> dst, labels -.. ocv:cfunction:: void cvDistTransform( const CvArr* src, CvArr* dst, int distanceType=CV_DIST_L2, int maskSize=3, const float* mask=NULL, CvArr* labels=NULL ) +.. ocv:cfunction:: void cvDistTransform( const CvArr* src, CvArr* dst, int distanceType=CV_DIST_L2, int maskSize=3, const float* mask=NULL, CvArr* labels=NULL, int labelType=CV_DIST_LABEL_CCOMP ) .. ocv:pyoldfunction:: cv.DistTransform(src, dst, distanceType=CV_DIST_L2, maskSize=3, mask=None, labels=None)-> None @@ -429,6 +429,8 @@ Calculates the distance to the closest zero pixel for each pixel of the source i :param maskSize: Size of the distance transform mask. It can be 3, 5, or ``CV_DIST_MASK_PRECISE`` (the latter option is only supported by the first function). In case of the ``CV_DIST_L1`` or ``CV_DIST_C`` distance type, the parameter is forced to 3 because a :math:`3\times 3` mask gives the same result as :math:`5\times 5` or any larger aperture. :param labels: Optional output 2D array of labels (the discrete Voronoi diagram). It has the type ``CV_32SC1`` and the same size as ``src`` . See the details below. + + :param labelType: Type of the label array to build. If ``labelType==DIST_LABEL_CCOMP`` then each connected component of zeros in ``src`` (as well as all the non-zero pixels closest to the connected component) will be assigned the same label. If ``labelType==DIST_LABEL_PIXEL`` then each zero pixel (and all the non-zero pixels closest to it) gets its own label. The functions ``distanceTransform`` calculate the approximate or precise distance from every binary image pixel to the nearest zero pixel. @@ -469,17 +471,13 @@ Note that both the precise and the approximate algorithms are linear on the numb The second variant of the function does not only compute the minimum distance for each pixel :math:`(x, y)` but also identifies the nearest connected -component consisting of zero pixels. Index of the component is stored in +component consisting of zero pixels (``labelType==DIST_LABEL_CCOMP``) or the nearest zero pixel (``labelType==DIST_LABEL_PIXEL``). Index of the component/pixel is stored in :math:`\texttt{labels}(x, y)` . -The connected components of zero pixels are also found and marked by the function. +When ``labelType==DIST_LABEL_CCOMP``, the function automatically finds connected components of zero pixels in the input image and marks them with distinct labels. When ``labelType==DIST_LABEL_CCOMP``, the function scans through the input image and marks all the zero pixels with distinct labels. In this mode, the complexity is still linear. That is, the function provides a very fast way to compute the Voronoi diagram for a binary image. -Currently, the second variant can use only the approximate distance transform algorithm. - - - - +Currently, the second variant can use only the approximate distance transform algorithm, i.e. ``maskSize=CV_DIST_MASK_PRECISE`` is not supported yet. floodFill ------------- diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 58888c7aae..8cbce706d7 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -751,9 +751,16 @@ CV_EXPORTS_W void grabCut( InputArray img, InputOutputArray mask, Rect rect, InputOutputArray bgdModel, InputOutputArray fgdModel, int iterCount, int mode = GC_EVAL ); +enum +{ + DIST_LABEL_CCOMP = 0, + DIST_LABEL_PIXEL = 1 +}; + //! builds the discrete Voronoi diagram CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray src, OutputArray dst, - OutputArray labels, int distanceType, int maskSize ); + OutputArray labels, int distanceType, int maskSize, + int labelType=DIST_LABEL_CCOMP ); //! computes the distance transform map CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst, diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h index 7d79a2bca0..1bc8f2c0c5 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc_c.h +++ b/modules/imgproc/include/opencv2/imgproc/imgproc_c.h @@ -620,7 +620,8 @@ CVAPI(void) cvDistTransform( const CvArr* src, CvArr* dst, int distance_type CV_DEFAULT(CV_DIST_L2), int mask_size CV_DEFAULT(3), const float* mask CV_DEFAULT(NULL), - CvArr* labels CV_DEFAULT(NULL)); + CvArr* labels CV_DEFAULT(NULL), + int labelType CV_DEFAULT(CV_DIST_LABEL_CCOMP)); /* Applies fixed-level threshold to grayscale image. diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index 8a91777b39..2b232f093b 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -470,6 +470,13 @@ enum CV_DIST_MASK_PRECISE =0 }; +/* Content of output label array: connected components or pixels */ +enum +{ + CV_DIST_LABEL_CCOMP = 0, + CV_DIST_LABEL_PIXEL = 1 +}; + /* Distance types for Distance Transform and M-estimators */ enum { diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index 8ae5c7ab5f..80ff77b63c 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -706,19 +706,12 @@ CV_IMPL void cvDistTransform( const void* srcarr, void* dstarr, int distType, int maskSize, const float *mask, - void* labelsarr ) + void* labelsarr, int labelType ) { - cv::Ptr temp; - cv::Ptr src_copy; - cv::Ptr st; - float _mask[5] = {0}; CvMat srcstub, *src = (CvMat*)srcarr; CvMat dststub, *dst = (CvMat*)dstarr; CvMat lstub, *labels = (CvMat*)labelsarr; - CvSize size; - //CvIPPDistTransFunc ipp_func = 0; - //CvIPPDistTransFunc2 ipp_inp_func = 0; src = cvGetMat( src, &srcstub ); dst = cvGetMat( dst, &dststub ); @@ -773,46 +766,16 @@ cvDistTransform( const void* srcarr, void* dstarr, memcpy( _mask, mask, (maskSize/2 + 1)*sizeof(float)); } - /*if( !labels ) - { - if( CV_MAT_TYPE(dst->type) == CV_32FC1 ) - ipp_func = (CvIPPDistTransFunc)(maskSize == CV_DIST_MASK_3 ? - icvDistanceTransform_3x3_8u32f_C1R_p : icvDistanceTransform_5x5_8u32f_C1R_p); - else if( src->data.ptr != dst->data.ptr ) - ipp_func = (CvIPPDistTransFunc)icvDistanceTransform_3x3_8u_C1R_p; - else - ipp_inp_func = icvDistanceTransform_3x3_8u_C1IR_p; - }*/ - - size = cvGetMatSize(src); - - /*if( (ipp_func || ipp_inp_func) && src->cols >= 4 && src->rows >= 2 ) - { - int _imask[3]; - _imask[0] = cvRound(_mask[0]); - _imask[1] = cvRound(_mask[1]); - _imask[2] = cvRound(_mask[2]); + CvSize size = cvGetMatSize(src); - if( ipp_func ) - { - IPPI_CALL( ipp_func( src->data.ptr, src->step, - dst->data.fl, dst->step, size, - CV_MAT_TYPE(dst->type) == CV_8UC1 ? - (void*)_imask : (void*)_mask )); - } - else - { - IPPI_CALL( ipp_inp_func( src->data.ptr, src->step, size, _imask )); - } - } - else*/ if( CV_MAT_TYPE(dst->type) == CV_8UC1 ) + if( CV_MAT_TYPE(dst->type) == CV_8UC1 ) { icvDistanceATS_L1_8u( src, dst ); } else { int border = maskSize == CV_DIST_MASK_3 ? 1 : 2; - temp = cvCreateMat( size.height + border*2, size.width + border*2, CV_32SC1 ); + cv::Ptr temp = cvCreateMat( size.height + border*2, size.width + border*2, CV_32SC1 ); if( !labels ) { @@ -825,25 +788,37 @@ cvDistTransform( const void* srcarr, void* dstarr, } else { - CvSeq *contours = 0; - int label; - - st = cvCreateMemStorage(); - src_copy = cvCreateMat( size.height+border*2, size.width+border*2, src->type ); - cvCopyMakeBorder(src, src_copy, cvPoint(border, border), IPL_BORDER_CONSTANT, cvScalarAll(255)); - cvCmpS( src_copy, 0, src_copy, CV_CMP_EQ ); - cvFindContours( src_copy, st, &contours, sizeof(CvContour), - CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(-border, -border)); cvZero( labels ); - for( label = 1; contours != 0; contours = contours->h_next, label++ ) + + if( labelType == CV_DIST_LABEL_CCOMP ) { - CvScalar area_color = cvScalarAll(label); - cvDrawContours( labels, contours, area_color, area_color, -255, -1, 8 ); + CvSeq *contours = 0; + cv::Ptr st = cvCreateMemStorage(); + cv::Ptr src_copy = cvCreateMat( size.height+border*2, size.width+border*2, src->type ); + cvCopyMakeBorder(src, src_copy, cvPoint(border, border), IPL_BORDER_CONSTANT, cvScalarAll(255)); + cvCmpS( src_copy, 0, src_copy, CV_CMP_EQ ); + cvFindContours( src_copy, st, &contours, sizeof(CvContour), + CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(-border, -border)); + + for( int label = 1; contours != 0; contours = contours->h_next, label++ ) + { + CvScalar area_color = cvScalarAll(label); + cvDrawContours( labels, contours, area_color, area_color, -255, -1, 8 ); + } + } + else + { + int k = 1; + for( int i = 0; i < src->rows; i++ ) + { + const uchar* srcptr = src->data.ptr + src->step*i; + int* labelptr = (int*)(labels->data.ptr + labels->step*i); + + for( int j = 0; j < src->cols; j++ ) + if( srcptr[j] == 0 ) + labelptr[j] = k++; + } } - - //cvCopy( src, src_copy ); - //CvPoint top_left = {0,0}, bottom_right = {size.width-1,size.height-1}; - //cvRectangle( src_copy, top_left, bottom_right, cvScalarAll(255), 1, 8 ); icvDistanceTransformEx_5x5_C1R( src->data.ptr, src->step, temp->data.i, temp->step, dst->data.fl, dst->step, labels->data.i, labels->step, size, _mask ); @@ -852,13 +827,13 @@ cvDistTransform( const void* srcarr, void* dstarr, } void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labels, - int distanceType, int maskSize ) + int distanceType, int maskSize, int labelType ) { Mat src = _src.getMat(); _dst.create(src.size(), CV_32F); _labels.create(src.size(), CV_32S); CvMat c_src = src, c_dst = _dst.getMat(), c_labels = _labels.getMat(); - cvDistTransform(&c_src, &c_dst, distanceType, maskSize, 0, &c_labels); + cvDistTransform(&c_src, &c_dst, distanceType, maskSize, 0, &c_labels, labelType); } void cv::distanceTransform( InputArray _src, OutputArray _dst, @@ -868,7 +843,7 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, _dst.create(src.size(), CV_32F); Mat dst = _dst.getMat(); CvMat c_src = src, c_dst = _dst.getMat(); - cvDistTransform(&c_src, &c_dst, distanceType, maskSize, 0, 0); + cvDistTransform(&c_src, &c_dst, distanceType, maskSize, 0, 0, -1); } /* End of file. */ diff --git a/samples/cpp/distrans.cpp b/samples/cpp/distrans.cpp index 2cae7e7544..b9cd67d739 100644 --- a/samples/cpp/distrans.cpp +++ b/samples/cpp/distrans.cpp @@ -6,7 +6,7 @@ using namespace cv; int maskSize0 = CV_DIST_MASK_5; -bool buildVoronoi = false; +int voronoiType = -1; int edgeThresh = 100; int distType0 = CV_DIST_L1; @@ -29,17 +29,17 @@ void onTrackbar( int, void* ) Scalar(255,0,255) }; - int maskSize = buildVoronoi ? CV_DIST_MASK_5 : maskSize0; - int distType = buildVoronoi ? CV_DIST_L2 : distType0; + int maskSize = voronoiType >= 0 ? CV_DIST_MASK_5 : maskSize0; + int distType = voronoiType >= 0 ? CV_DIST_L2 : distType0; Mat edge = gray >= edgeThresh, dist, labels, dist8u; - if( !buildVoronoi ) + if( voronoiType < 0 ) distanceTransform( edge, dist, distType, maskSize ); else - distanceTransform( edge, dist, labels, distType, maskSize ); + distanceTransform( edge, dist, labels, distType, maskSize, voronoiType ); - if( !buildVoronoi ) + if( voronoiType < 0 ) { // begin "painting" the distance transform result dist *= 5000; @@ -70,9 +70,10 @@ void onTrackbar( int, void* ) for( int j = 0; j < labels.cols; j++ ) { int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1; - int b = cvRound(colors[idx][0]); - int g = cvRound(colors[idx][1]); - int r = cvRound(colors[idx][2]); + float scale = 1.f/(1 + dd[j]*dd[j]*0.0004f); + int b = cvRound(colors[idx][0]*scale); + int g = cvRound(colors[idx][1]*scale); + int r = cvRound(colors[idx][2]*scale); d[j*3] = (uchar)b; d[j*3+1] = (uchar)g; d[j*3+2] = (uchar)r; @@ -96,7 +97,8 @@ void help() "\t3 - use 3x3 mask\n" "\t5 - use 5x5 mask\n" "\t0 - use precise distance transform\n" - "\tv - switch Voronoi diagram mode on/off\n" + "\tv - switch to Voronoi diagram mode\n" + "\tp - switch to pixel-based Voronoi diagram mode\n" "\tSPACE - loop through all the modes\n\n"); } @@ -126,30 +128,38 @@ int main( int argc, const char** argv ) // Call to update the view onTrackbar(0, 0); - int c = cvWaitKey(0); + int c = cvWaitKey(0) & 255; - if( (char)c == 27 ) + if( c == 27 ) break; - if( (char)c == 'c' || (char)c == 'C' ) + if( c == 'c' || c == 'C' || c == '1' || c == '2' || + c == '3' || c == '5' || c == '0' ) + voronoiType = -1; + + if( c == 'c' || c == 'C' ) distType0 = CV_DIST_C; - else if( (char)c == '1' ) + else if( c == '1' ) distType0 = CV_DIST_L1; - else if( (char)c == '2' ) + else if( c == '2' ) distType0 = CV_DIST_L2; - else if( (char)c == '3' ) + else if( c == '3' ) maskSize0 = CV_DIST_MASK_3; - else if( (char)c == '5' ) + else if( c == '5' ) maskSize0 = CV_DIST_MASK_5; - else if( (char)c == '0' ) + else if( c == '0' ) maskSize0 = CV_DIST_MASK_PRECISE; - else if( (char)c == 'v' ) - buildVoronoi = !buildVoronoi; - else if( (char)c == ' ' ) + else if( c == 'v' ) + voronoiType = 0; + else if( c == 'p' ) + voronoiType = 1; + else if( c == ' ' ) { - if( buildVoronoi ) + if( voronoiType == 0 ) + voronoiType = 1; + else if( voronoiType == 1 ) { - buildVoronoi = false; + voronoiType = -1; maskSize0 = CV_DIST_MASK_3; distType0 = CV_DIST_C; } @@ -162,7 +172,7 @@ int main( int argc, const char** argv ) else if( maskSize0 == CV_DIST_MASK_5 ) maskSize0 = CV_DIST_MASK_PRECISE; else if( maskSize0 == CV_DIST_MASK_PRECISE ) - buildVoronoi = true; + voronoiType = 0; } }