extended morphological operations to handle arbitrary number of channels and CV_64F...
authorVadim Pisarevsky <no@email>
Thu, 12 Apr 2012 12:34:55 +0000 (12:34 +0000)
committerVadim Pisarevsky <no@email>
Thu, 12 Apr 2012 12:34:55 +0000 (12:34 +0000)
modules/imgproc/doc/filtering.rst
modules/imgproc/src/filter.cpp
modules/imgproc/src/morph.cpp
modules/imgproc/src/templmatch.cpp
modules/imgproc/test/test_filter.cpp

index a3fb73d..380e130 100644 (file)
@@ -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<FilterEngine> 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<BaseFilter> getMorphologyFilter(int op, int type, InputArray element,                                    Point anchor=Point(-1,-1))
+.. ocv:function:: Ptr<BaseFilter> getMorphologyFilter(int op, int type, InputArray element, Point anchor=Point(-1,-1))
 
-.. ocv:function:: Ptr<BaseRowFilter> getMorphologyRowFilter(int op, int type,                                          int esize, int anchor=-1)
+.. ocv:function:: Ptr<BaseRowFilter> getMorphologyRowFilter(int op, int type, int esize, int anchor=-1)
 
-.. ocv:function:: Ptr<BaseColumnFilter> getMorphologyColumnFilter(int op, int type,                                                int esize, int anchor=-1)
+.. ocv:function:: Ptr<BaseColumnFilter> 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.
 
index a23e862..6a998e5 100644 (file)
@@ -185,7 +185,8 @@ void FilterEngine::init( const Ptr<BaseFilter>& _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));
     }
 
index 06fd5ba..801a2cd 100644 (file)
@@ -75,6 +75,23 @@ template<typename T> struct MaxOp
 template<> inline uchar MinOp<uchar>::operator ()(uchar a, uchar b) const { return CV_MIN_8U(a, b); }
 template<> inline uchar MaxOp<uchar>::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<class VecUpdate> struct MorphRowIVec
@@ -567,23 +584,6 @@ typedef MorphFVec<VMax32f> 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<class Op, class VecOp> struct MorphRowFilter : public BaseRowFilter
 {
@@ -861,6 +868,9 @@ cv::Ptr<cv::BaseRowFilter> cv::getMorphologyRowFilter(int op, int type, int ksiz
         if( depth == CV_32F )
             return Ptr<BaseRowFilter>(new MorphRowFilter<MinOp<float>,
                                       ErodeRowVec32f>(ksize, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseRowFilter>(new MorphRowFilter<MinOp<double>,
+                                      ErodeRowVec64f>(ksize, anchor));
     }
     else
     {
@@ -876,6 +886,9 @@ cv::Ptr<cv::BaseRowFilter> cv::getMorphologyRowFilter(int op, int type, int ksiz
         if( depth == CV_32F )
             return Ptr<BaseRowFilter>(new MorphRowFilter<MaxOp<float>,
                                       DilateRowVec32f>(ksize, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseRowFilter>(new MorphRowFilter<MaxOp<double>,
+                                      DilateRowVec64f>(ksize, anchor));
     } 
 
     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
@@ -902,6 +915,9 @@ cv::Ptr<cv::BaseColumnFilter> cv::getMorphologyColumnFilter(int op, int type, in
         if( depth == CV_32F )
             return Ptr<BaseColumnFilter>(new MorphColumnFilter<MinOp<float>,
                                          ErodeColumnVec32f>(ksize, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseColumnFilter>(new MorphColumnFilter<MinOp<double>,
+                                         ErodeColumnVec64f>(ksize, anchor));
     }
     else
     {
@@ -917,6 +933,9 @@ cv::Ptr<cv::BaseColumnFilter> cv::getMorphologyColumnFilter(int op, int type, in
         if( depth == CV_32F )
             return Ptr<BaseColumnFilter>(new MorphColumnFilter<MaxOp<float>,
                                          DilateColumnVec32f>(ksize, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseColumnFilter>(new MorphColumnFilter<MaxOp<double>,
+                                         DilateColumnVec64f>(ksize, anchor));
     }
 
     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
@@ -940,6 +959,8 @@ cv::Ptr<cv::BaseFilter> cv::getMorphologyFilter(int op, int type, InputArray _ke
             return Ptr<BaseFilter>(new MorphFilter<MinOp<short>, ErodeVec16s>(kernel, anchor));
         if( depth == CV_32F )
             return Ptr<BaseFilter>(new MorphFilter<MinOp<float>, ErodeVec32f>(kernel, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseFilter>(new MorphFilter<MinOp<double>, ErodeVec64f>(kernel, anchor));
     }
     else
     {
@@ -951,6 +972,8 @@ cv::Ptr<cv::BaseFilter> cv::getMorphologyFilter(int op, int type, InputArray _ke
             return Ptr<BaseFilter>(new MorphFilter<MaxOp<short>, DilateVec16s>(kernel, anchor));
         if( depth == CV_32F )
             return Ptr<BaseFilter>(new MorphFilter<MaxOp<float>, DilateVec32f>(kernel, anchor));
+        if( depth == CV_64F )
+            return Ptr<BaseFilter>(new MorphFilter<MaxOp<double>, DilateVec64f>(kernel, anchor));
     }
 
     CV_Error_( CV_StsNotImplemented, ("Unsupported data type (=%d)", type));
@@ -983,13 +1006,18 @@ cv::Ptr<cv::FilterEngine> 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<FilterEngine>(new FilterEngine(filter2D, rowFilter, columnFilter,
index 0f445ef..28f04a2 100644 (file)
@@ -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 );
-}*/
-
 }
 
 /*****************************************************************************************/
index 61d1e36..49f0f6b 100644 (file)
@@ -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(); }
+