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.

pull/13383/head
Vadim Pisarevsky 13 years ago
parent 6d0c022347
commit 1371c96935
  1. 42
      modules/imgproc/doc/filtering.rst
  2. 3
      modules/imgproc/src/filter.cpp
  3. 68
      modules/imgproc/src/morph.cpp
  4. 11
      modules/imgproc/src/templmatch.cpp
  5. 104
      modules/imgproc/test/test_filter.cpp

@ -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.

@ -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));
}

@ -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,

@ -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 );
}*/
}
/*****************************************************************************************/

@ -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(); }

Loading…
Cancel
Save