From 1371c9693597d59af36b9cc417d60d408b4ba287 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 12 Apr 2012 12:34:55 +0000 Subject: [PATCH] extended morphological operations to handle arbitrary number of channels and CV_64F type; extended filter2D to handle CV_16S type. added test to check the supported formats; added description of supported formats in various filtering operations. --- modules/imgproc/doc/filtering.rst | 42 ++++++++------ modules/imgproc/src/filter.cpp | 3 +- modules/imgproc/src/morph.cpp | 68 ++++++++++++++++------- modules/imgproc/src/templmatch.cpp | 11 +--- modules/imgproc/test/test_filter.cpp | 104 +++++++++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 47 deletions(-) diff --git a/modules/imgproc/doc/filtering.rst b/modules/imgproc/doc/filtering.rst index a3fb73d..380e130 100644 --- a/modules/imgproc/doc/filtering.rst +++ b/modules/imgproc/doc/filtering.rst @@ -418,7 +418,7 @@ Smoothes an image using the normalized box filter. .. ocv:pyfunction:: cv2.blur(src, ksize[, dst[, anchor[, borderType]]]) -> dst - :param src: Source image. + :param src: Source image. The image can have any number of channels, which are processed independently. The depth should be ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F`` or ``CV_64F``. :param dst: Destination image of the same size and type as ``src`` . @@ -507,9 +507,7 @@ where \alpha = \fork{\frac{1}{\texttt{ksize.width*ksize.height}}}{when \texttt{normalize=true}}{1}{otherwise} -Unnormalized box filter is useful for computing various integral characteristics over each pixel neighborhood, such as covariance matrices of image derivatives (used in dense optical flow algorithms, -and so on). If you need to compute pixel sums over variable-size windows, use -:ocv:func:`integral` . +Unnormalized box filter is useful for computing various integral characteristics over each pixel neighborhood, such as covariance matrices of image derivatives (used in dense optical flow algorithms, and so on). If you need to compute pixel sums over variable-size windows, use :ocv:func:`integral` . .. seealso:: @@ -741,17 +739,17 @@ Creates an engine for non-separable morphological operations. .. ocv:function:: Ptr createMorphologyFilter(int op, int type, InputArray element, Point anchor=Point(-1,-1), int rowBorderType=BORDER_CONSTANT, int columnBorderType=-1, const Scalar& borderValue=morphologyDefaultBorderValue()) -.. ocv:function:: Ptr getMorphologyFilter(int op, int type, InputArray element, Point anchor=Point(-1,-1)) +.. ocv:function:: Ptr getMorphologyFilter(int op, int type, InputArray element, Point anchor=Point(-1,-1)) -.. ocv:function:: Ptr getMorphologyRowFilter(int op, int type, int esize, int anchor=-1) +.. ocv:function:: Ptr getMorphologyRowFilter(int op, int type, int esize, int anchor=-1) -.. ocv:function:: Ptr getMorphologyColumnFilter(int op, int type, int esize, int anchor=-1) +.. ocv:function:: Ptr getMorphologyColumnFilter(int op, int type, int esize, int anchor=-1) .. ocv:function:: Scalar morphologyDefaultBorderValue() :param op: Morphology operation ID, ``MORPH_ERODE`` or ``MORPH_DILATE`` . - - :param type: Input/output image type. The image must have 1-4 channels and valid image depths are ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F``. + + :param type: Input/output image type. The number of channels can be arbitrary. The depth should be one of ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F` or ``CV_64F``. :param element: 2D 8-bit structuring element for a morphological operation. Non-zero elements indicate the pixels that belong to the element. @@ -840,7 +838,7 @@ Dilates an image by using a specific structuring element. .. ocv:cfunction:: void cvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 ) .. ocv:pyoldfunction:: cv.Dilate(src, dst, element=None, iterations=1)-> None - :param src: Source image. The image must have 1-4 channels and valid image depths are ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F``. + :param src: Source image. The number of channels can be arbitrary. The depth should be one of ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F` or ``CV_64F``. :param dst: Destination image of the same size and type as ``src`` . @@ -880,9 +878,9 @@ Erodes an image by using a specific structuring element. .. ocv:cfunction:: void cvErode( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1) .. ocv:pyoldfunction:: cv.Erode(src, dst, element=None, iterations=1)-> None - :param src: Source image. The image must have 1-4 channels and valid image depths are ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F``. + :param src: Source image. The number of channels can be arbitrary. The depth should be one of ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F` or ``CV_64F``. - :param dst: Destination image of the same size and type as ``src`` . + :param dst: Destination image of the same size and type as ``src``. :param element: Structuring element used for erosion. If ``element=Mat()`` , a ``3 x 3`` rectangular structuring element is used. @@ -925,7 +923,13 @@ Convolves an image with the kernel. :param dst: Destination image of the same size and the same number of channels as ``src`` . - :param ddepth: Desired depth of the destination image. If it is negative, it will be the same as ``src.depth()`` . + :param ddepth: Desired depth of the destination image. If it is negative, it will be the same as ``src.depth()`` . The following combination of ``src.depth()`` and ``ddepth`` are supported: + * ``src.depth()`` = ``CV_8U``, ``ddepth`` = -1/``CV_16S``/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_16U``/``CV_16S``, ``ddepth`` = -1/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_32F``, ``ddepth`` = -1/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_64F``, ``ddepth`` = -1/``CV_64F`` + + when ``ddepth=-1``, the destination image will have the same depth as the source. :param kernel: Convolution kernel (or rather a correlation kernel), a single-channel floating point matrix. If you want to apply different kernels to different channels, split the image into separate color planes using :ocv:func:`split` and process them individually. @@ -965,7 +969,7 @@ Smoothes an image using a Gaussian filter. .. ocv:pyfunction:: cv2.GaussianBlur(src, ksize, sigma1[, dst[, sigma2[, borderType]]]) -> dst - :param src: Source image. + :param src: Source image. The image can have any number of channels, which are processed independently. The depth should be ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F`` or ``CV_64F``. :param dst: Destination image of the same size and type as ``src`` . @@ -1173,7 +1177,7 @@ Performs advanced morphological transformations. .. ocv:cfunction:: void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp, IplConvKernel* element, int operation, int iterations=1 ) .. ocv:pyoldfunction:: cv.MorphologyEx(src, dst, temp, element, operation, iterations=1)-> None - :param src: Source image. The image must have 1-4 channels and valid image depths are ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F``. + :param src: Source image. The number of channels can be arbitrary. The depth should be one of ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F` or ``CV_64F``. :param dst: Destination image of the same size and type as ``src`` . @@ -1406,7 +1410,13 @@ Applies a separable linear filter to an image. :param dst: Destination image of the same size and the same number of channels as ``src`` . - :param ddepth: Destination image depth. + :param ddepth: Destination image depth. The following combination of ``src.depth()`` and ``ddepth`` are supported: + * ``src.depth()`` = ``CV_8U``, ``ddepth`` = -1/``CV_16S``/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_16U``/``CV_16S``, ``ddepth`` = -1/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_32F``, ``ddepth`` = -1/``CV_32F``/``CV_64F`` + * ``src.depth()`` = ``CV_64F``, ``ddepth`` = -1/``CV_64F`` + + when ``ddepth=-1``, the destination image will have the same depth as the source. :param rowKernel: Coefficients for filtering each row. diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index a23e862..6a998e5 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -185,7 +185,8 @@ void FilterEngine::init( const Ptr& _filter2D, if( rowBorderType == BORDER_CONSTANT || columnBorderType == BORDER_CONSTANT ) { constBorderValue.resize(srcElemSize*borderLength); - scalarToRawData(_borderValue, &constBorderValue[0], srcType, + int srcType1 = CV_MAKETYPE(CV_MAT_DEPTH(srcType), MIN(CV_MAT_CN(srcType), 4)); + scalarToRawData(_borderValue, &constBorderValue[0], srcType1, borderLength*CV_MAT_CN(srcType)); } diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index 06fd5ba..801a2cd 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -75,6 +75,23 @@ template struct MaxOp template<> inline uchar MinOp::operator ()(uchar a, uchar b) const { return CV_MIN_8U(a, b); } template<> inline uchar MaxOp::operator ()(uchar a, uchar b) const { return CV_MAX_8U(a, b); } +struct MorphRowNoVec +{ + MorphRowNoVec(int, int) {} + int operator()(const uchar*, uchar*, int, int) const { return 0; } +}; + +struct MorphColumnNoVec +{ + MorphColumnNoVec(int, int) {} + int operator()(const uchar**, uchar*, int, int, int) const { return 0; } +}; + +struct MorphNoVec +{ + int operator()(uchar**, int, uchar*, int) const { return 0; } +}; + #if CV_SSE2 template struct MorphRowIVec @@ -567,23 +584,6 @@ typedef MorphFVec DilateVec32f; #else -struct MorphRowNoVec -{ - MorphRowNoVec(int, int) {} - int operator()(const uchar*, uchar*, int, int) const { return 0; } -}; - -struct MorphColumnNoVec -{ - MorphColumnNoVec(int, int) {} - int operator()(const uchar**, uchar*, int, int, int) const { return 0; } -}; - -struct MorphNoVec -{ - int operator()(uchar**, int, uchar*, int) const { return 0; } -}; - #ifdef HAVE_TEGRA_OPTIMIZATION using tegra::ErodeRowVec8u; using tegra::DilateRowVec8u; @@ -623,6 +623,13 @@ typedef MorphNoVec DilateVec32f; #endif +typedef MorphRowNoVec ErodeRowVec64f; +typedef MorphRowNoVec DilateRowVec64f; +typedef MorphColumnNoVec ErodeColumnVec64f; +typedef MorphColumnNoVec DilateColumnVec64f; +typedef MorphNoVec ErodeVec64f; +typedef MorphNoVec DilateVec64f; + template struct MorphRowFilter : public BaseRowFilter { @@ -861,6 +868,9 @@ cv::Ptr cv::getMorphologyRowFilter(int op, int type, int ksiz if( depth == CV_32F ) return Ptr(new MorphRowFilter, ErodeRowVec32f>(ksize, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphRowFilter, + ErodeRowVec64f>(ksize, anchor)); } else { @@ -876,6 +886,9 @@ cv::Ptr cv::getMorphologyRowFilter(int op, int type, int ksiz if( depth == CV_32F ) return Ptr(new MorphRowFilter, DilateRowVec32f>(ksize, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphRowFilter, + DilateRowVec64f>(ksize, anchor)); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); @@ -902,6 +915,9 @@ cv::Ptr cv::getMorphologyColumnFilter(int op, int type, in if( depth == CV_32F ) return Ptr(new MorphColumnFilter, ErodeColumnVec32f>(ksize, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphColumnFilter, + ErodeColumnVec64f>(ksize, anchor)); } else { @@ -917,6 +933,9 @@ cv::Ptr cv::getMorphologyColumnFilter(int op, int type, in if( depth == CV_32F ) return Ptr(new MorphColumnFilter, DilateColumnVec32f>(ksize, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphColumnFilter, + DilateColumnVec64f>(ksize, anchor)); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); @@ -940,6 +959,8 @@ cv::Ptr cv::getMorphologyFilter(int op, int type, InputArray _ke return Ptr(new MorphFilter, ErodeVec16s>(kernel, anchor)); if( depth == CV_32F ) return Ptr(new MorphFilter, ErodeVec32f>(kernel, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphFilter, ErodeVec64f>(kernel, anchor)); } else { @@ -951,6 +972,8 @@ cv::Ptr cv::getMorphologyFilter(int op, int type, InputArray _ke return Ptr(new MorphFilter, DilateVec16s>(kernel, anchor)); if( depth == CV_32F ) return Ptr(new MorphFilter, DilateVec32f>(kernel, anchor)); + if( depth == CV_64F ) + return Ptr(new MorphFilter, DilateVec64f>(kernel, anchor)); } CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type)); @@ -983,13 +1006,18 @@ cv::Ptr cv::createMorphologyFilter( int op, int type, InputArr borderValue == morphologyDefaultBorderValue() ) { int depth = CV_MAT_DEPTH(type); - CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_32F ); + CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_16S || + depth == CV_32F || depth == CV_64F ); if( op == MORPH_ERODE ) borderValue = Scalar::all( depth == CV_8U ? (double)UCHAR_MAX : - depth == CV_16U ? (double)USHRT_MAX : (double)FLT_MAX ); + depth == CV_16U ? (double)USHRT_MAX : + depth == CV_16S ? (double)SHRT_MAX : + depth == CV_32F ? (double)FLT_MAX : DBL_MAX); else borderValue = Scalar::all( depth == CV_8U || depth == CV_16U ? - 0. : (double)-FLT_MAX ); + 0. : + depth == CV_16S ? (double)SHRT_MIN : + depth == CV_32F ? (double)-FLT_MAX : -DBL_MAX); } return Ptr(new FilterEngine(filter2D, rowFilter, columnFilter, diff --git a/modules/imgproc/src/templmatch.cpp b/modules/imgproc/src/templmatch.cpp index 0f445ef..28f04a2 100644 --- a/modules/imgproc/src/templmatch.cpp +++ b/modules/imgproc/src/templmatch.cpp @@ -58,7 +58,6 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr, int cdepth = CV_MAT_DEPTH(ctype), ccn = CV_MAT_CN(ctype); CV_Assert( img.dims <= 2 && templ.dims <= 2 && corr.dims <= 2 ); - CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F ); if( depth != tdepth && tdepth != std::max(CV_32F, depth) ) { @@ -74,7 +73,7 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr, corr.create(corrsize, ctype); - int maxDepth = depth > CV_8U ? CV_64F : std::max(std::max(CV_32F, tdepth), cdepth); + int maxDepth = depth > CV_8S ? CV_64F : std::max(std::max(CV_32F, tdepth), cdepth); Size blocksize, dftsize; blocksize.width = cvRound(templ.cols*blockScale); @@ -228,14 +227,6 @@ void crossCorr( const Mat& img, const Mat& _templ, Mat& corr, } } -/*void -cv::crossCorr( const Mat& img, const Mat& templ, Mat& corr, - Point anchor, double delta, int borderType ) -{ - CvMat _img = img, _templ = templ, _corr = corr; - icvCrossCorr( &_img, &_templ, &_corr, anchor, delta, borderType ); -}*/ - } /*****************************************************************************************/ diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 61d1e36..49f0f6b 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -1775,3 +1775,107 @@ TEST(Imgproc_MinEigenVal, accuracy) { CV_MinEigenValTest test; test.safe_run(); TEST(Imgproc_EigenValsVecs, accuracy) { CV_EigenValVecTest test; test.safe_run(); } TEST(Imgproc_PreCornerDetect, accuracy) { CV_PreCornerDetectTest test; test.safe_run(); } TEST(Imgproc_Integral, accuracy) { CV_IntegralTest test; test.safe_run(); } + +////////////////////////////////////////////////////////////////////////////////// + +class CV_FilterSupportedFormatsTest : public cvtest::BaseTest +{ +public: + CV_FilterSupportedFormatsTest() {} + ~CV_FilterSupportedFormatsTest() {} +protected: + void run(int) + { + const int depths[][2] = + { + {CV_8U, CV_8U}, + {CV_8U, CV_16U}, + {CV_8U, CV_16S}, + {CV_8U, CV_32F}, + {CV_8U, CV_64F}, + {CV_16U, CV_16U}, + {CV_16U, CV_32F}, + {CV_16U, CV_64F}, + {CV_16S, CV_16S}, + {CV_16S, CV_32F}, + {CV_16S, CV_64F}, + {CV_32F, CV_32F}, + {CV_64F, CV_64F}, + {-1, -1} + }; + + int i = 0; + volatile int fidx = -1; + try + { + // use some "odd" size to do yet another smoke + // testing of the non-SIMD loop tails + Size sz(163, 117); + Mat small_kernel(5, 5, CV_32F), big_kernel(21, 21, CV_32F); + Mat kernelX(11, 1, CV_32F), kernelY(7, 1, CV_32F); + Mat symkernelX(11, 1, CV_32F), symkernelY(7, 1, CV_32F); + randu(small_kernel, -10, 10); + randu(big_kernel, -1, 1); + randu(kernelX, -1, 1); + randu(kernelY, -1, 1); + flip(kernelX, symkernelX, 0); + symkernelX += kernelX; + flip(kernelY, symkernelY, 0); + symkernelY += kernelY; + + Mat elem_ellipse = getStructuringElement(MORPH_ELLIPSE, Size(7, 7)); + Mat elem_rect = getStructuringElement(MORPH_RECT, Size(7, 7)); + + for( i = 0; depths[i][0] >= 0; i++ ) + { + int sdepth = depths[i][0]; + int ddepth = depths[i][1]; + Mat src(sz, CV_MAKETYPE(sdepth, 5)), dst; + randu(src, 0, 100); + // non-separable filtering with a small kernel + fidx = 0; + filter2D(src, dst, ddepth, small_kernel); + fidx++; + filter2D(src, dst, ddepth, big_kernel); + fidx++; + sepFilter2D(src, dst, ddepth, kernelX, kernelY); + fidx++; + sepFilter2D(src, dst, ddepth, symkernelX, symkernelY); + fidx++; + Sobel(src, dst, ddepth, 2, 0, 5); + fidx++; + Scharr(src, dst, ddepth, 0, 1); + if( sdepth != ddepth ) + continue; + fidx++; + GaussianBlur(src, dst, Size(5, 5), 1.2, 1.2); + fidx++; + blur(src, dst, Size(11, 11)); + fidx++; + morphologyEx(src, dst, MORPH_GRADIENT, elem_ellipse); + fidx++; + morphologyEx(src, dst, MORPH_GRADIENT, elem_rect); + } + } + catch(...) + { + ts->printf(cvtest::TS::LOG, "Combination of depths %d => %d in %s is not supported (yet it should be)", + depths[i][0], depths[i][1], + fidx == 0 ? "filter2D (small kernel)" : + fidx == 1 ? "filter2D (large kernel)" : + fidx == 2 ? "sepFilter2D" : + fidx == 3 ? "sepFilter2D (symmetrical/asymmetrical kernel)" : + fidx == 4 ? "Sobel" : + fidx == 5 ? "Scharr" : + fidx == 6 ? "GaussianBlur" : + fidx == 7 ? "blur" : + fidx == 8 || fidx == 9 ? "morphologyEx" : + "unknown???"); + + ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); + } + } +}; + +TEST(Imgproc_Filtering, supportedFormats) { CV_FilterSupportedFormatsTest test; test.safe_run(); } + -- 2.7.4