diff --git a/modules/core/include/opencv2/core/private.hpp b/modules/core/include/opencv2/core/private.hpp index c71ec626e6..a5993a36a7 100644 --- a/modules/core/include/opencv2/core/private.hpp +++ b/modules/core/include/opencv2/core/private.hpp @@ -221,6 +221,18 @@ static inline IppiSize ippiSize(const cv::Size & _size) return size; } +static inline IppiPoint ippiPoint(const cv::Point & _point) +{ + IppiPoint point = { _point.x, _point.y }; + return point; +} + +static inline IppiPoint ippiPoint(int x, int y) +{ + IppiPoint point = { x, y }; + return point; +} + static inline IppiBorderType ippiGetBorderType(int borderTypeNI) { return borderTypeNI == cv::BORDER_CONSTANT ? ippBorderConst : diff --git a/modules/imgproc/include/opencv2/imgproc/hal/hal.hpp b/modules/imgproc/include/opencv2/imgproc/hal/hal.hpp index 0b0886f6d9..eca34e7e73 100644 --- a/modules/imgproc/include/opencv2/imgproc/hal/hal.hpp +++ b/modules/imgproc/include/opencv2/imgproc/hal/hal.hpp @@ -44,6 +44,22 @@ struct CV_EXPORTS SepFilter2D virtual ~SepFilter2D() {} }; + +struct CV_EXPORTS MorphContext +{ + static Ptr create(int op, int src_type, int dst_type, int max_width, int max_height, + int kernel_type, uchar * kernel_data, size_t kernel_step, + int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix, bool allowInplace); + virtual void apply(uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2) = 0; + virtual ~MorphContext() {} +}; + + //! @} }} diff --git a/modules/imgproc/src/filterengine.hpp b/modules/imgproc/src/filterengine.hpp index fbb3c1ace4..91106684fc 100644 --- a/modules/imgproc/src/filterengine.hpp +++ b/modules/imgproc/src/filterengine.hpp @@ -37,6 +37,8 @@ the use of this software, even if advised of the possibility of such damage. #ifndef __OPENCV_IMGPROC_FILTERENGINE_HPP__ #define __OPENCV_IMGPROC_FILTERENGINE_HPP__ +#include "opencv2/imgproc.hpp" + namespace cv { diff --git a/modules/imgproc/src/hal_replacement.hpp b/modules/imgproc/src/hal_replacement.hpp index 6d944b1537..e043c4027b 100644 --- a/modules/imgproc/src/hal_replacement.hpp +++ b/modules/imgproc/src/hal_replacement.hpp @@ -21,6 +21,14 @@ inline int hal_ni_sepFilterFree(cvhalFilter2D *) { return CV_HAL_ERROR_NOT_IMPLE #define cv_hal_sepFilter hal_ni_sepFilter #define cv_hal_sepFilterFree hal_ni_sepFilterFree +inline int hal_ni_morphInit(cvhalFilter2D **, int, int, int, int, int, int, uchar *, size_t, int, int, int, int, int, const double[4], int, bool, bool) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +inline int hal_ni_morph(cvhalFilter2D *, uchar *, size_t, uchar *, size_t, int, int, int, int, int, int, int, int, int, int) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } +inline int hal_ni_morphFree(cvhalFilter2D *) { return CV_HAL_ERROR_NOT_IMPLEMENTED; } + +#define cv_hal_morphInit hal_ni_morphInit +#define cv_hal_morph hal_ni_morph +#define cv_hal_morphFree hal_ni_morphFree + #include "custom_hal.hpp" #endif // OPENCV_IMGPROC_HAL_REPLACEMENT_HPP diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index c68ccc9d2d..d70edd0715 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -43,11 +43,15 @@ #include "precomp.hpp" #include #include "opencl_kernels_imgproc.hpp" +#include +#include "hal_replacement.hpp" /****************************************************************************************\ Basic Morphological Operations: Erosion & Dilation \****************************************************************************************/ +using namespace std; + namespace cv { @@ -584,19 +588,11 @@ typedef MorphFVec DilateVec32f; #else -#ifdef HAVE_TEGRA_OPTIMIZATION -using tegra::ErodeRowVec8u; -using tegra::DilateRowVec8u; - -using tegra::ErodeColumnVec8u; -using tegra::DilateColumnVec8u; -#else typedef MorphRowNoVec ErodeRowVec8u; typedef MorphRowNoVec DilateRowVec8u; typedef MorphColumnNoVec ErodeColumnVec8u; typedef MorphColumnNoVec DilateColumnVec8u; -#endif typedef MorphRowNoVec ErodeRowVec16u; typedef MorphRowNoVec DilateRowVec16u; @@ -1081,311 +1077,399 @@ cv::Mat cv::getStructuringElement(int shape, Size ksize, Point anchor) namespace cv { -class MorphologyRunner : public ParallelLoopBody +// ===== 1. replacement implementation + +struct ReplacementMorphImpl : public hal::MorphContext { -public: - MorphologyRunner(Mat _src, Mat _dst, int _nStripes, int _iterations, - int _op, Mat _kernel, Point _anchor, - int _rowBorderType, int _columnBorderType, const Scalar& _borderValue) : - borderValue(_borderValue) + cvhalFilter2D * ctx; + bool isInitialized; + bool init(int op, int src_type, int dst_type, int max_width, int max_height, + int kernel_type, uchar * kernel_data, size_t kernel_step, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix, bool allowInplace) { - src = _src; - dst = _dst; + int res = cv_hal_morphInit(&ctx, op, src_type, dst_type, max_width, max_height, + kernel_type, kernel_data, kernel_step, kernel_width, kernel_height, + anchor_x, anchor_y, + borderType, borderValue, + iterations, isSubmatrix, allowInplace); + isInitialized = (res == CV_HAL_ERROR_OK); + return isInitialized; + } + void apply(uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2) + { + if (isInitialized) + { + int res = cv_hal_morph(ctx, src_data, src_step, dst_data, dst_step, width, height, + roi_width, roi_height, + roi_x, roi_y, + roi_width2, roi_height2, + roi_x2, roi_y2); + if (res != CV_HAL_ERROR_OK) + CV_Error(Error::StsNotImplemented, "Failed to run HAL morph implementation"); + } + } + ~ReplacementMorphImpl() + { + if (isInitialized) + { + int res = cv_hal_morphFree(ctx); + if (res != CV_HAL_ERROR_OK) + CV_Error(Error::StsNotImplemented, "Failed to run HAL morph implementation"); + } + } +}; - nStripes = _nStripes; - iterations = _iterations; +// ===== 2. IPP implementation - op = _op; - kernel = _kernel; - anchor = _anchor; - rowBorderType = _rowBorderType; - columnBorderType = _columnBorderType; - } +#ifdef HAVE_IPP - void operator () ( const Range& range ) const - { - int row0 = std::min(cvRound(range.start * src.rows / nStripes), src.rows); - int row1 = std::min(cvRound(range.end * src.rows / nStripes), src.rows); +#if IPP_VERSION_X100 >= 810 - /*if(0) - printf("Size = (%d, %d), range[%d,%d), row0 = %d, row1 = %d\n", - src.rows, src.cols, range.start, range.end, row0, row1);*/ +template struct IppMorphTrait {}; - Mat srcStripe = src.rowRange(row0, row1); - Mat dstStripe = dst.rowRange(row0, row1); +#if IPP_VERSION_X100 >= 900 - Ptr f = createMorphologyFilter(op, src.type(), kernel, anchor, - rowBorderType, columnBorderType, borderValue ); +#define INIT_TRAIT(cvtype, ipptype, flavor, channels, zerodef)\ +template <>\ +struct IppMorphTrait\ +{\ + typedef Ipp##ipptype ipp_data_type;\ + enum { cn = channels };\ + IppDataType getDataType() {return ipp##ipptype;}\ +\ + IppStatus getMorphSize(IppiSize roiSize, IppiSize maskSize, int* pSpecSize, int* pBufferSize) {return ippiMorphologyBorderGetSize_##flavor(roiSize, maskSize, pSpecSize, pBufferSize);}\ + IppStatus morphInit(IppiSize roiSize, const Ipp8u* pMask, IppiSize maskSize, IppiMorphState* pMorphSpec, Ipp8u* pBuffer) {return ippiMorphologyBorderInit_##flavor(roiSize, pMask, maskSize, pMorphSpec, pBuffer);}\ + IppStatus filterGetMinSize(IppiSize dstRoiSize, IppiSize maskSize, IppDataType dataType, int numChannels, int* pBufferSize) {return ippiFilterMinBorderGetBufferSize(dstRoiSize, maskSize, dataType, numChannels, pBufferSize);}\ + IppStatus filterGetMaxSize(IppiSize dstRoiSize, IppiSize maskSize, IppDataType dataType, int numChannels, int* pBufferSize) {return ippiFilterMaxBorderGetBufferSize(dstRoiSize, maskSize, dataType, numChannels, pBufferSize);}\ + IppStatus filterMinBorder(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize dstRoiSize, IppiSize maskSize, IppiPoint, Ipp8u* pBuffer) { ipp_data_type zerodef; return ippiFilterMinBorder_##flavor(pSrc, srcStep, pDst, dstStep, dstRoiSize, maskSize, ippBorderRepl, zero, pBuffer); }\ + IppStatus filterMaxBorder(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize dstRoiSize, IppiSize maskSize, IppiPoint, Ipp8u* pBuffer) { ipp_data_type zerodef; return ippiFilterMaxBorder_##flavor(pSrc, srcStep, pDst, dstStep, dstRoiSize, maskSize, ippBorderRepl, zero, pBuffer); }\ + IppStatus morphDilate(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize roiSize, const IppiMorphState* pMorphSpec, Ipp8u* pBuffer) { ipp_data_type zerodef; return ippiDilateBorder_##flavor(pSrc, srcStep, pDst, dstStep, roiSize, ippBorderRepl, zero, pMorphSpec, pBuffer); }\ + IppStatus morphErode(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize roiSize, const IppiMorphState* pMorphSpec, Ipp8u* pBuffer) { ipp_data_type zerodef; return ippiErodeBorder_##flavor(pSrc, srcStep, pDst, dstStep, roiSize, ippBorderRepl, zero, pMorphSpec, pBuffer); }\ +}; - { - Point ofs; - Size wsz(srcStripe.cols, srcStripe.rows); - srcStripe.locateROI( wsz, ofs ); +#else - f->apply( srcStripe, dstStripe, wsz, ofs ); - } +#define INIT_TRAIT(cvtype, ipptype, flavor, channels, zerodef)\ +template <>\ +struct IppMorphTrait\ +{\ + typedef Ipp##ipptype ipp_data_type;\ + enum { cn = channels };\ + IppDataType getDataType() {return ipp##ipptype;}\ +\ + IppStatus getMorphSize(IppiSize roiSize, IppiSize maskSize, int* pSpecSize, int* pBufferSize) {return ippiMorphologyBorderGetSize_##flavor(roiSize.width, maskSize, pSpecSize, pBufferSize);}\ + IppStatus morphInit(IppiSize roiSize, const Ipp8u* pMask, IppiSize maskSize, IppiMorphState* pMorphSpec, Ipp8u* pBuffer) {return ippiMorphologyBorderInit_##flavor(roiSize.width, pMask, maskSize, pMorphSpec, pBuffer);}\ + IppStatus filterGetMinSize(IppiSize dstRoiSize, IppiSize maskSize, IppDataType, int, int* pBufferSize) {return ippiFilterMinGetBufferSize_##flavor(dstRoiSize.width, maskSize, pBufferSize);}\ + IppStatus filterGetMaxSize(IppiSize dstRoiSize, IppiSize maskSize, IppDataType, int, int* pBufferSize) {return ippiFilterMinGetBufferSize_##flavor(dstRoiSize.width, maskSize, pBufferSize);}\ + IppStatus filterMinBorder(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize dstRoiSize, IppiSize maskSize, IppiPoint anchor, Ipp8u* pBuffer) { return ippiFilterMinBorderReplicate_##flavor(pSrc, srcStep, pDst, dstStep, dstRoiSize, maskSize, anchor, pBuffer); }\ + IppStatus filterMaxBorder(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize dstRoiSize, IppiSize maskSize, IppiPoint anchor, Ipp8u* pBuffer) { return ippiFilterMaxBorderReplicate_##flavor(pSrc, srcStep, pDst, dstStep, dstRoiSize, maskSize, anchor, pBuffer); }\ + IppStatus morphDilate(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize roiSize, const IppiMorphState* pMorphSpec, Ipp8u* pBuffer) {return ippiDilateBorder_##flavor(pSrc, srcStep, pDst, dstStep, roiSize, ippBorderRepl, 0, pMorphSpec, pBuffer);}\ + IppStatus morphErode(const ipp_data_type* pSrc, int srcStep, ipp_data_type* pDst, int dstStep, IppiSize roiSize, const IppiMorphState* pMorphSpec, Ipp8u* pBuffer) {return ippiErodeBorder_##flavor(pSrc, srcStep, pDst, dstStep, roiSize, ippBorderRepl, 0, pMorphSpec, pBuffer);}\ +}; - { - Point ofs; - Size wsz(dstStripe.cols, dstStripe.rows); - dstStripe.locateROI( wsz, ofs ); +#endif - for( int i = 1; i < iterations; i++ ) - f->apply( dstStripe, dstStripe, wsz, ofs ); - } - } +INIT_TRAIT(CV_8UC1, 8u, 8u_C1R, 1, zero = 0) +INIT_TRAIT(CV_8UC3, 8u, 8u_C3R, 3, zero[3] = {0}) +INIT_TRAIT(CV_8UC4, 8u, 8u_C4R, 4, zero[4] = {0}) +INIT_TRAIT(CV_32FC1, 32f, 32f_C1R, 1, zero = 0) +INIT_TRAIT(CV_32FC3, 32f, 32f_C3R, 3, zero[3] = {0}) +INIT_TRAIT(CV_32FC4, 32f, 32f_C4R, 4, zero[4] = {0}) -private: - Mat src; - Mat dst; - int nStripes; - int iterations; +#undef INIT_TRAIT - int op; - Mat kernel; - Point anchor; - int rowBorderType; - int columnBorderType; - Scalar borderValue; -}; +//-------------------------------------- -#ifdef HAVE_IPP -static bool ipp_MorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kernel, - const Size& ksize, const Point &anchor, bool rectKernel) +struct IppMorphBaseImpl : public hal::MorphContext { -#if IPP_VERSION_X100 >= 810 - int type = src.type(); - const Mat* _src = &src; - Mat temp; - if (src.data == dst.data) - { - src.copyTo(temp); - _src = &temp; - } - - IppiSize roiSize = {src.cols, src.rows}; - IppiSize kernelSize = {ksize.width, ksize.height}; + virtual bool init(int _op, int _src_type, int dst_type, int max_width, int max_height, + int kernel_type, uchar * kernel_data, size_t kernel_step, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix, bool allowInplace) = 0; +}; - if (!rectKernel) +template +struct IppMorphImpl : public IppMorphBaseImpl +{ + IppMorphTrait trait; + typedef typename IppMorphTrait::ipp_data_type ipp_data_type; + IppAutoBuffer specBuf; + IppAutoBuffer workBuf; + IppiSize kernelSize; + bool rectKernel; + IppiPoint anchor; + int op; + int src_type; + int border; + + bool init(int _op, int _src_type, int dst_type, int max_width, int max_height, + int kernel_type, uchar * kernel_data, size_t kernel_step, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix, bool allowInplace) { -#if IPP_VERSION_X100 >= 900 - if (((kernel.cols - 1) / 2 != anchor.x) || ((kernel.rows - 1) / 2 != anchor.y)) - return false; - #define IPP_MORPH_CASE(cvtype, flavor, data_type) \ - case cvtype: \ - {\ - int specSize = 0, bufferSize = 0;\ - if (0 > ippiMorphologyBorderGetSize_##flavor(roiSize, kernelSize, &specSize, &bufferSize))\ - return false;\ - IppiMorphState *pSpec = (IppiMorphState*)ippMalloc(specSize);\ - Ipp8u *pBuffer = (Ipp8u*)ippMalloc(bufferSize);\ - if (0 > ippiMorphologyBorderInit_##flavor(roiSize, kernel.ptr(), kernelSize, pSpec, pBuffer))\ - {\ - ippFree(pBuffer);\ - ippFree(pSpec);\ - return false;\ - }\ - bool ok = false;\ - if (op == MORPH_ERODE)\ - ok = (0 <= ippiErodeBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0],\ - roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ - else\ - ok = (0 <= ippiDilateBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0],\ - roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ - ippFree(pBuffer);\ - ippFree(pSpec);\ - return ok;\ - }\ - break; -#else - if (((kernel.cols - 1) / 2 != anchor.x) || ((kernel.rows - 1) / 2 != anchor.y)) + border = borderType; // TODO: remove + anchor = ippiPoint(anchor_x, anchor_y); + CV_UNUSED(dst_type); + src_type = _src_type; + + Mat kernel(Size(kernel_width, kernel_height), kernel_type, kernel_data, kernel_step); + int depth = CV_MAT_DEPTH(src_type), cn = CV_MAT_CN(src_type); + + if( !( depth == CV_8U || depth == CV_32F ) + || !(cn == 1 || cn == 3 || cn == 4) + || !( borderType == cv::BORDER_REPLICATE + || (borderType == cv::BORDER_CONSTANT && Vec(borderValue) == morphologyDefaultBorderValue() && kernel.size() == Size(3,3))) + || !( op == MORPH_DILATE || op == MORPH_ERODE) + || isSubmatrix + || allowInplace) return false; - #define IPP_MORPH_CASE(cvtype, flavor, data_type) \ - case cvtype: \ - {\ - int specSize = 0, bufferSize = 0;\ - if (0 > ippiMorphologyBorderGetSize_##flavor(roiSize.width, kernelSize, &specSize, &bufferSize))\ - return false;\ - IppiMorphState *pSpec = (IppiMorphState*)ippMalloc(specSize);\ - Ipp8u *pBuffer = (Ipp8u*)ippMalloc(bufferSize);\ - if (0 > ippiMorphologyBorderInit_##flavor(roiSize.width, kernel.ptr(), kernelSize, pSpec, pBuffer))\ - {\ - ippFree(pBuffer);\ - ippFree(pSpec);\ - return false;\ - }\ - bool ok = false;\ - if (op == MORPH_ERODE)\ - ok = (0 <= ippiErodeBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0],\ - roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ - else\ - ok = (0 <= ippiDilateBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0],\ - roiSize, ippBorderRepl, 0, pSpec, pBuffer));\ - ippFree(pBuffer);\ - ippFree(pSpec);\ - return ok;\ - }\ - break; -#endif - CV_SUPPRESS_DEPRECATED_START - switch (type) + + // In case BORDER_CONSTANT, IPPMorphReplicate works correct with kernels of size 3*3 only + if( borderType == cv::BORDER_CONSTANT && kernel.data ) { - IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u); - IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u); - IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u); - IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f); - IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f); - IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f); - default: - ; + int x, y; + for( y = 0; y < kernel.rows; y++ ) + { + if( kernel.at(y, anchor.x) != 0 ) + continue; + for( x = 0; x < kernel.cols; x++ ) + { + if( kernel.at(y,x) != 0 ) + return false; + } + } + for( x = 0; x < kernel.cols; x++ ) + { + if( kernel.at(anchor.y, x) != 0 ) + continue; + for( y = 0; y < kernel.rows; y++ ) + { + if( kernel.at(y,x) != 0 ) + return false; + } + } + } - CV_SUPPRESS_DEPRECATED_END - #undef IPP_MORPH_CASE - } - else - { -#if IPP_VERSION_X100 != 900 // Problems with accuracy in 9.0.0 -#if IPP_VERSION_X100 >= 900 - if (((kernelSize.width - 1) / 2 != anchor.x) || ((kernelSize.height - 1) / 2 != anchor.y)) // Arbitrary anchor is no longer supporeted since IPP 9.0.0 - return false; - #define IPP_MORPH_CASE(cvtype, flavor, data_type, cn) \ - case cvtype: \ - {\ - if (op == MORPH_ERODE)\ - {\ - int bufSize = 0;\ - if (0 > ippiFilterMinBorderGetBufferSize(roiSize, kernelSize, ipp##data_type, cn, &bufSize))\ - return false;\ - AutoBuffer buf(bufSize + 64);\ - uchar* buffer = alignPtr((uchar*)buf, 32);\ - return (0 <= ippiFilterMinBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0], roiSize, kernelSize, ippBorderRepl, 0, buffer));\ - }\ - else\ - {\ - int bufSize = 0;\ - if (0 > ippiFilterMaxBorderGetBufferSize(roiSize, kernelSize, ipp##data_type, cn, &bufSize))\ - return false;\ - AutoBuffer buf(bufSize + 64);\ - uchar* buffer = alignPtr((uchar*)buf, 32);\ - return (0 <= ippiFilterMaxBorder_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0], roiSize, kernelSize, ippBorderRepl, 0, buffer));\ - }\ - }\ - break; -#else - IppiPoint point = {anchor.x, anchor.y}; - - #define IPP_MORPH_CASE(cvtype, flavor, data_type, cn) \ - case cvtype: \ - {\ - int bufSize = 0;\ - if (0 > ippiFilterMinGetBufferSize_##flavor(src.cols, kernelSize, &bufSize))\ - return false;\ - AutoBuffer buf(bufSize + 64);\ - uchar* buffer = alignPtr((uchar*)buf, 32);\ - if (op == MORPH_ERODE)\ - return (0 <= ippiFilterMinBorderReplicate_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\ - return (0 <= ippiFilterMaxBorderReplicate_##flavor(_src->ptr(), (int)_src->step[0], dst.ptr(), (int)dst.step[0], roiSize, kernelSize, point, buffer));\ - }\ - break; -#endif + Size ksize = !kernel.empty() ? kernel.size() : Size(3,3); - CV_SUPPRESS_DEPRECATED_START - switch (type) + rectKernel = false; + if( kernel.empty() ) { - IPP_MORPH_CASE(CV_8UC1, 8u_C1R, 8u, 1); - IPP_MORPH_CASE(CV_8UC3, 8u_C3R, 8u, 3); - IPP_MORPH_CASE(CV_8UC4, 8u_C4R, 8u, 4); - IPP_MORPH_CASE(CV_32FC1, 32f_C1R, 32f, 1); - IPP_MORPH_CASE(CV_32FC3, 32f_C3R, 32f, 3); - IPP_MORPH_CASE(CV_32FC4, 32f_C4R, 32f, 4); - default: - ; + ksize = Size(1+iterations*2,1+iterations*2); + anchor = ippiPoint(iterations, iterations); + rectKernel = true; + iterations = 1; + } + else if( iterations >= 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) + { + ksize = Size(ksize.width + (iterations-1)*(ksize.width-1), + ksize.height + (iterations-1)*(ksize.height-1)), + anchor = ippiPoint(anchor.x*iterations, anchor.y*iterations); + kernel = Mat(); + rectKernel = true; + iterations = 1; } - CV_SUPPRESS_DEPRECATED_END - #undef IPP_MORPH_CASE -#endif - } -#else - CV_UNUSED(op); CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(kernel); CV_UNUSED(ksize); CV_UNUSED(anchor); CV_UNUSED(rectKernel); -#endif - return false; -} -static bool ipp_MorphOp(int op, InputArray _src, OutputArray _dst, - const Mat& _kernel, Point anchor, int iterations, - int borderType, const Scalar &borderValue) -{ - Mat src = _src.getMat(), kernel = _kernel; - int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + // TODO: implement the case of iterations > 1. + if( iterations > 1 ) + return false; - if( !( depth == CV_8U || depth == CV_32F ) || !(cn == 1 || cn == 3 || cn == 4) || - !( borderType == cv::BORDER_REPLICATE || (borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue() && - kernel.size() == Size(3,3)) ) || !( op == MORPH_DILATE || op == MORPH_ERODE) || _src.isSubmatrix() ) - return false; + IppiSize roiSize = {max_width, max_height}; + kernelSize = ippiSize(ksize); + op = _op; - // In case BORDER_CONSTANT, IPPMorphReplicate works correct with kernels of size 3*3 only - if( borderType == cv::BORDER_CONSTANT && kernel.data ) - { - int x, y; - for( y = 0; y < kernel.rows; y++ ) + IppStatus res; + if (!rectKernel) { - if( kernel.at(y, anchor.x) != 0 ) - continue; - for( x = 0; x < kernel.cols; x++ ) + if (((kernel.cols - 1) / 2 != anchor.x) || ((kernel.rows - 1) / 2 != anchor.y)) + return false; + int specSize = 0, bufferSize = 0; + res = trait.getMorphSize(roiSize, kernelSize, &specSize, &bufferSize); + if (res >= 0) { - if( kernel.at(y,x) != 0 ) - return false; + specBuf.Alloc(specSize); + workBuf.Alloc(bufferSize); + res = trait.morphInit(roiSize, kernel.ptr(), kernelSize, specBuf, workBuf); + if (res >= 0) + return true; } } - for( x = 0; x < kernel.cols; x++ ) + else { - if( kernel.at(anchor.y, x) != 0 ) - continue; - for( y = 0; y < kernel.rows; y++ ) + if (((kernelSize.width - 1) / 2 != anchor.x) || ((kernelSize.height - 1) / 2 != anchor.y)) + return false; + if (op == MORPH_ERODE) { - if( kernel.at(y,x) != 0 ) - return false; + int bufSize = 0; + res = trait.filterGetMinSize(roiSize, kernelSize, trait.getDataType(), trait.cn, &bufSize); + if (res >= 0) + { + workBuf.Alloc(bufSize); + return true; + } + } + else + { + int bufSize = 0; + res = trait.filterGetMaxSize(roiSize, kernelSize, trait.getDataType(), trait.cn, &bufSize); + if (res >= 0) + { + workBuf.Alloc(bufSize); + return true; + } } } - + return false; } - Size ksize = !kernel.empty() ? kernel.size() : Size(3,3); - _dst.create( src.size(), src.type() ); - Mat dst = _dst.getMat(); + void apply(uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2) + { + CV_UNUSED(roi_width); CV_UNUSED(roi_height); CV_UNUSED(roi_x); CV_UNUSED(roi_y); + CV_UNUSED(roi_width2); CV_UNUSED(roi_height2); CV_UNUSED(roi_x2); CV_UNUSED(roi_y2); + if (src_data == dst_data) + CV_Error(Error::StsBadArg, "IPP Morph inplace is not alowed"); + + IppiSize roiSize = {width, height}; + + IppStatus res; + if (!rectKernel) + { + if (op == MORPH_ERODE) + res = (trait.morphErode((ipp_data_type*)src_data, (int)src_step, (ipp_data_type*)dst_data, (int)dst_step, roiSize, specBuf, workBuf)); + else + res = (trait.morphDilate((ipp_data_type*)src_data, (int)src_step, (ipp_data_type*)dst_data, (int)dst_step, roiSize, specBuf, workBuf)); + } + else + { + if (op == MORPH_ERODE) + res = (trait.filterMinBorder((ipp_data_type*)src_data, (int)src_step, (ipp_data_type*)dst_data, (int)dst_step, roiSize, kernelSize, anchor, workBuf)); + else + res = (trait.filterMaxBorder((ipp_data_type*)src_data, (int)src_step, (ipp_data_type*)dst_data, (int)dst_step, roiSize, kernelSize, anchor, workBuf)); + } + if (res < 0) + CV_Error(Error::StsBadArg, "Failed to run IPP morph"); + } +}; - if( iterations == 0 || kernel.rows*kernel.cols == 1 ) +static IppMorphBaseImpl * createIppImpl(int type) +{ + switch (type) { - src.copyTo(dst); - return true; + case CV_8UC1: return new IppMorphImpl(); + case CV_8UC3: return new IppMorphImpl(); + case CV_8UC4: return new IppMorphImpl(); + case CV_32FC1: return new IppMorphImpl(); + case CV_32FC3: return new IppMorphImpl(); + case CV_32FC4: return new IppMorphImpl(); } + return 0; +} + +#endif // IPP_VERSION_X100 >= 810 +#endif // HAVE_IPP + +// ===== 3. Fallback implementation - bool rectKernel = false; - if( kernel.empty() ) +struct OcvMorphImpl : public hal::MorphContext +{ + Ptr f; + int iterations; + int src_type; + int dst_type; + bool init(int op, int _src_type, int _dst_type, int, int, + int kernel_type, uchar * kernel_data, size_t kernel_step, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double _borderValue[4], + int _iterations, bool, bool) { - ksize = Size(1+iterations*2,1+iterations*2); - anchor = Point(iterations, iterations); - rectKernel = true; - iterations = 1; + iterations = _iterations; + src_type = _src_type; + dst_type = _dst_type; + Mat kernel(Size(kernel_width, kernel_height), kernel_type, kernel_data, kernel_step); + Point anchor(anchor_x, anchor_y); + Vec borderValue(_borderValue); + f = createMorphologyFilter(op, src_type, kernel, anchor, borderType, borderType, borderValue ); + return true; } - else if( iterations >= 1 && countNonZero(kernel) == kernel.rows*kernel.cols ) + + void apply(uchar * src_data, size_t src_step, uchar * dst_data, size_t dst_step, int width, int height, + int roi_width, int roi_height, int roi_x, int roi_y, + int roi_width2, int roi_height2, int roi_x2, int roi_y2) { - ksize = Size(ksize.width + (iterations-1)*(ksize.width-1), - ksize.height + (iterations-1)*(ksize.height-1)), - anchor = Point(anchor.x*iterations, anchor.y*iterations); - kernel = Mat(); - rectKernel = true; - iterations = 1; + Mat src(Size(width, height), src_type, src_data, src_step); + Mat dst(Size(width, height), dst_type, dst_data, dst_step); + { + Point ofs(roi_x, roi_y); + Size wsz(roi_width, roi_height); + f->apply( src, dst, wsz, ofs ); + } + { + Point ofs(roi_x2, roi_y2); + Size wsz(roi_width2, roi_height2); + for( int i = 1; i < iterations; i++ ) + f->apply( dst, dst, wsz, ofs ); + } } +}; - // TODO: implement the case of iterations > 1. - if( iterations > 1 ) - return false; +// ===== HAL interface implementation - return ipp_MorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ); -} +namespace hal { + +Ptr MorphContext ::create(int op, int src_type, int dst_type, int max_width, int max_height, + int kernel_type, uchar * kernel_data, size_t kernel_step, int kernel_width, int kernel_height, + int anchor_x, int anchor_y, + int borderType, const double borderValue[4], + int iterations, bool isSubmatrix, bool allowInplace) +{ + { + ReplacementMorphImpl * impl = new ReplacementMorphImpl(); + if (impl->init(op, src_type, dst_type, max_width, max_height, + kernel_type, kernel_data, kernel_step, kernel_width, kernel_height, + anchor_x, anchor_y, + borderType, borderValue, iterations, isSubmatrix, allowInplace)) + { + return Ptr(impl); + } + delete impl; + } +#if defined(HAVE_IPP) && IPP_VERSION_X100 >= 810 + CV_IPP_CHECK() + { + IppMorphBaseImpl * impl = createIppImpl(src_type); + if (impl) + { + if (impl->init(op, src_type, dst_type, max_width, max_height, + kernel_type, kernel_data, kernel_step, kernel_width, kernel_height, + anchor_x, anchor_y, + borderType, borderValue, iterations, isSubmatrix, allowInplace)) + { + return Ptr(impl); + } + delete impl; + } + } #endif + { + OcvMorphImpl * impl = new OcvMorphImpl(); + impl->init(op, src_type, dst_type, max_width, max_height, + kernel_type, kernel_data, kernel_step, kernel_width, kernel_height, + anchor_x, anchor_y, + borderType, borderValue, iterations, isSubmatrix, allowInplace); + return Ptr(impl); + } +} + +} // cv::hal #ifdef HAVE_OPENCL @@ -1763,22 +1847,24 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, iterations = 1; } - CV_IPP_RUN(IPP_VERSION_X100 >= 810, ipp_MorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue)) - Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); - int nStripes = 1; -#if defined HAVE_TEGRA_OPTIMIZATION - if (src.data != dst.data && iterations == 1 && //NOTE: threads are not used for inplace processing - (borderType & BORDER_ISOLATED) == 0 && //TODO: check border types - src.rows >= 64 ) //NOTE: just heuristics - nStripes = 4; -#endif - - parallel_for_(Range(0, nStripes), - MorphologyRunner(src, dst, nStripes, iterations, op, kernel, anchor, borderType, borderType, borderValue)); + Point s_ofs; + Size s_wsz(src.cols, src.rows); + src.locateROI(s_wsz, s_ofs); + Point d_ofs; + Size d_wsz(dst.cols, dst.rows); + dst.locateROI(d_wsz, d_ofs); + + Ptr ctx = hal::MorphContext::create(op, src.type(), dst.type(), src.cols, src.rows, + kernel.type(), kernel.data, kernel.step, kernel.cols, kernel.rows, + anchor.x, anchor.y, borderType, borderValue.val, iterations, + src.isSubmatrix(), src.data == dst.data); + ctx->apply(src.data, src.step, dst.data, dst.step, src.cols, src.rows, + s_wsz.width, s_wsz.height, s_ofs.x, s_ofs.y, + d_wsz.width, d_wsz.height, d_ofs.x, d_ofs.y); } }