diff --git a/modules/imgproc/doc/miscellaneous_transformations.rst b/modules/imgproc/doc/miscellaneous_transformations.rst index 32690cf8f9..c8f7d15794 100644 --- a/modules/imgproc/doc/miscellaneous_transformations.rst +++ b/modules/imgproc/doc/miscellaneous_transformations.rst @@ -411,7 +411,7 @@ distanceTransform ----------------- Calculates the distance to the closest zero pixel for each pixel of the source image. -.. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, int distanceType, int maskSize ) +.. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, int distanceType, int maskSize, int dstType=CV_32F ) .. ocv:function:: void distanceTransform( InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize, int labelType=DIST_LABEL_CCOMP ) @@ -421,12 +421,14 @@ Calculates the distance to the closest zero pixel for each pixel of the source i :param src: 8-bit, single-channel (binary) source image. - :param dst: Output image with calculated distances. It is a 32-bit floating-point, single-channel image of the same size as ``src`` . + :param dst: Output image with calculated distances. It is a 8-bit or 32-bit floating-point, single-channel image of the same size as ``src`` . :param distanceType: Type of distance. It can be ``CV_DIST_L1, CV_DIST_L2`` , or ``CV_DIST_C`` . :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 dstType: Type of output image. It can be ``CV_8U`` or ``CV_32F``. Type ``CV_8U`` can be used only for the first variant of the function and ``distanceType == CV_DIST_L1``. + :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. diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index ffa41655b0..7928ae0fe8 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1389,7 +1389,7 @@ CV_EXPORTS_AS(distanceTransformWithLabels) void distanceTransform( InputArray sr //! computes the distance transform map CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst, - int distanceType, int maskSize ); + int distanceType, int maskSize, int dstType=CV_32F); //! fills the semi-uniform image region starting from the specified seed point diff --git a/modules/imgproc/perf/perf_distanceTransform.cpp b/modules/imgproc/perf/perf_distanceTransform.cpp index c1deb9305d..6136a65edf 100644 --- a/modules/imgproc/perf/perf_distanceTransform.cpp +++ b/modules/imgproc/perf/perf_distanceTransform.cpp @@ -21,3 +21,82 @@ PERF_TEST_P(Size_DistanceTransform, icvTrueDistTrans, testing::Values(TYPICAL_MA SANITY_CHECK(dst, 1); }*/ + +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace perf; +using std::tr1::make_tuple; +using std::tr1::get; + +CV_ENUM(DistanceType, DIST_L1, DIST_L2 , DIST_C) +CV_ENUM(MaskSize, DIST_MASK_3, DIST_MASK_5, DIST_MASK_PRECISE) +CV_ENUM(DstType, CV_8U, CV_32F) +CV_ENUM(LabelType, DIST_LABEL_CCOMP, DIST_LABEL_PIXEL) + +typedef std::tr1::tuple SrcSize_DistType_MaskSize_DstType; +typedef std::tr1::tuple SrcSize_DistType_MaskSize_LabelType; +typedef perf::TestBaseWithParam DistanceTransform_Test; +typedef perf::TestBaseWithParam DistanceTransform_NeedLabels_Test; + +PERF_TEST_P(DistanceTransform_Test, distanceTransform, + testing::Combine( + testing::Values(cv::Size(640, 480), cv::Size(800, 600), cv::Size(1024, 768), cv::Size(1280, 1024)), + DistanceType::all(), + MaskSize::all(), + DstType::all() + ) + ) +{ + Size srcSize = get<0>(GetParam()); + int distanceType = get<1>(GetParam()); + int maskSize = get<2>(GetParam()); + int dstType = get<3>(GetParam()); + + Mat src(srcSize, CV_8U); + Mat dst(srcSize, dstType); + + declare + .in(src, WARMUP_RNG) + .out(dst, WARMUP_RNG) + .time(30); + + TEST_CYCLE() distanceTransform( src, dst, distanceType, maskSize, dstType); + + double eps = 2e-4; + + SANITY_CHECK(dst, eps); +} + +PERF_TEST_P(DistanceTransform_NeedLabels_Test, distanceTransform_NeedLabels, + testing::Combine( + testing::Values(cv::Size(640, 480), cv::Size(800, 600), cv::Size(1024, 768), cv::Size(1280, 1024)), + DistanceType::all(), + MaskSize::all(), + LabelType::all() + ) + ) +{ + Size srcSize = get<0>(GetParam()); + int distanceType = get<1>(GetParam()); + int maskSize = get<2>(GetParam()); + int labelType = get<3>(GetParam()); + + Mat src(srcSize, CV_8U); + Mat label(srcSize, CV_32S); + Mat dst(srcSize, CV_32F); + + declare + .in(src, WARMUP_RNG) + .out(label, WARMUP_RNG) + .out(dst, WARMUP_RNG) + .time(30); + + TEST_CYCLE() distanceTransform( src, dst, label, distanceType, maskSize, labelType); + + double eps = 2e-4; + + SANITY_CHECK(label, eps); + SANITY_CHECK(dst, eps); +} diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index 7173fe335e..220696cfbf 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -488,7 +488,6 @@ struct DTColumnInvoker : ParallelLoopBody const float* sqr_tab; }; - struct DTRowInvoker : ParallelLoopBody { DTRowInvoker( Mat* _dst, const float* _sqr_tab, const float* _inv_tab ) @@ -578,7 +577,7 @@ trueDistTrans( const Mat& src, Mat& dst ) for( ; i <= m*3; i++ ) sat_tab[i] = i - shift; - cv::parallel_for_(cv::Range(0, n), cv::DTColumnInvoker(&src, &dst, sat_tab, sqr_tab)); + cv::parallel_for_(cv::Range(0, n), cv::DTColumnInvoker(&src, &dst, sat_tab, sqr_tab)); //dst.total()/(double)(1<<16) // stage 2: compute modified distance transform for each row float* inv_tab = sqr_tab + n; @@ -669,7 +668,8 @@ distanceATS_L1_8u( const Mat& src, Mat& dst ) { int b = dbase[x+dststep]; a = lut[MIN(a, b)]; - dbase[x] = (uchar)(MIN(a, dbase[x])); + a = MIN(a, dbase[x]); + dbase[x] = (uchar)(a); } } } @@ -677,23 +677,39 @@ distanceATS_L1_8u( const Mat& src, Mat& dst ) } +namespace cv +{ +static void distanceTransform_L1_8U(InputArray _src, OutputArray _dst) +{ + Mat src = _src.getMat(); + + CV_Assert( src.type() == CV_8UC1); + + _dst.create( src.size(), CV_8UC1); + Mat dst = _dst.getMat(); + + #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) && !defined HAVE_IPP_ICV_ONLY + IppiSize roi = { src.cols, src.rows }; + Ipp32s pMetrics[2] = { 1, 2 }; //L1, 3x3 mask + if (ippiDistanceTransform_3x3_8u_C1R(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, pMetrics)>=0) + return; + #endif + + distanceATS_L1_8u(src, dst); +} +} // Wrapper function for distance transform group void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labels, int distType, int maskSize, int labelType ) { - Mat src = _src.getMat(), dst = _dst.getMat(), labels; + Mat src = _src.getMat(), labels; bool need_labels = _labels.needed(); - CV_Assert( src.type() == CV_8U ); - if( dst.size == src.size && dst.type() == CV_8U && !need_labels && distType == CV_DIST_L1 ) - { - distanceATS_L1_8u(src, dst); - return; - } + CV_Assert( src.type() == CV_8UC1); - _dst.create( src.size(), CV_32F ); - dst = _dst.getMat(); + _dst.create( src.size(), CV_32F); + Mat dst = _dst.getMat(); if( need_labels ) { @@ -704,7 +720,6 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe maskSize = CV_DIST_MASK_5; } - CV_Assert( src.type() == CV_8UC1 ); float _mask[5] = {0}; if( maskSize != CV_DIST_MASK_3 && maskSize != CV_DIST_MASK_5 && maskSize != CV_DIST_MASK_PRECISE ) @@ -717,6 +732,22 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe if( maskSize == CV_DIST_MASK_PRECISE ) { + //ipp version is slower than the parallel in OpenCV + /*#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) + IppStatus status; + IppiSize roi = { src.cols, src.rows }; + Ipp8u *pBuffer; + int bufSize=0; + + status = ippiTrueDistanceTransformGetBufferSize_8u32f_C1R(roi, &bufSize); + + pBuffer = ippsMalloc_8u( bufSize ); + status = ippiTrueDistanceTransform_8u32f_C1R(src.ptr(),(int)src.step, dst.ptr(), (int)dst.step, roi, pBuffer); + ippsFree( pBuffer ); + return; + + #endif*/ + trueDistTrans( src, dst ); return; } @@ -734,9 +765,25 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe if( !need_labels ) { if( maskSize == CV_DIST_MASK_3 ) + { + #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) && !defined HAVE_IPP_ICV_ONLY + IppiSize roi = { src.cols, src.rows }; + if (ippiDistanceTransform_3x3_8u32f_C1R(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask)>=0) + return; + #endif + distanceTransform_3x3(src, temp, dst, _mask); + } else + { + #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) + IppiSize roi = { src.cols, src.rows }; + if (ippiDistanceTransform_5x5_8u32f_C1R(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roi, _mask)>=0) + return; + #endif + distanceTransform_5x5(src, temp, dst, _mask); + } } else { @@ -744,18 +791,7 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe if( labelType == CV_DIST_LABEL_CCOMP ) { - #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - if( maskSize == CV_DIST_MASK_5 ) - { - IppiSize roi = { src.cols, src.rows }; - if( ippiDistanceTransform_5x5_8u32f_C1R( - src.ptr(), (int)src.step, - dst.ptr(), (int)dst.step, roi, _mask) >= 0 ) - return; - setIppErrorStatus(); - } - #endif - Mat zpix = src == 0; + #endif Mat zpix = src == 0; connectedComponents(zpix, labels, 8, CV_32S); } else @@ -772,17 +808,19 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe } } - distanceTransformEx_5x5( src, temp, dst, labels, _mask ); + distanceTransformEx_5x5( src, temp, dst, labels, _mask ); } } - void cv::distanceTransform( InputArray _src, OutputArray _dst, - int distanceType, int maskSize ) + int distanceType, int maskSize, int dstType) { - distanceTransform(_src, _dst, noArray(), distanceType, maskSize, DIST_LABEL_PIXEL); -} + if (distanceType == CV_DIST_L1 && dstType==CV_8U) + distanceTransform_L1_8U(_src, _dst); + else + distanceTransform(_src, _dst, noArray(), distanceType, maskSize, DIST_LABEL_PIXEL); +} CV_IMPL void cvDistTransform( const void* srcarr, void* dstarr,