From 459a9c60eda7ad28c4b41eac6ae3a42b67810750 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov <2536374+asmorkalov@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:32:25 +0300 Subject: [PATCH] Merge pull request #25902 from asmorkalov:as/core_mask_cvbool Mask support with CV_Bool in ts and core #25902 Partially cover https://github.com/opencv/opencv/issues/25895 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/include/opencv2/core.hpp | 28 ++-- modules/core/include/opencv2/core/mat.hpp | 6 +- modules/core/src/arithm.cpp | 6 +- modules/core/src/copy.cpp | 5 +- modules/core/src/mean.dispatch.cpp | 8 +- modules/core/src/minmax.dispatch.cpp | 6 +- modules/core/src/norm.cpp | 4 +- modules/core/src/sum.dispatch.cpp | 2 +- modules/core/src/umatrix.cpp | 4 +- modules/core/test/test_arithm.cpp | 196 ++++++++++++++++++++-- modules/ts/src/ts_func.cpp | 8 +- 11 files changed, 218 insertions(+), 55 deletions(-) diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 843576f45a..d01ca82c24 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -352,8 +352,8 @@ result of an incorrect sign in the case of overflow. @param src2 second input array or a scalar. @param dst output array that has the same size and number of channels as the input array(s); the depth is defined by dtype or src1/src2. -@param mask optional operation mask - 8-bit single channel array, that specifies elements of the -output array to be changed. +@param mask optional operation mask - CV_8U, CV_8S or CV_Bool single channel array, that specifies elements +of the output array to be changed. @param dtype optional depth of the output array (see the discussion below). @sa subtract, addWeighted, scaleAdd, Mat::convertTo */ @@ -395,7 +395,7 @@ result of an incorrect sign in the case of overflow. @param src1 first input array or a scalar. @param src2 second input array or a scalar. @param dst output array of the same size and the same number of channels as the input array. -@param mask optional operation mask; this is an 8-bit single channel array that specifies elements +@param mask optional operation mask; this is CV_8U, CV8S or CV_Bool single channel array that specifies elements of the output array to be changed. @param dtype optional depth of the output array @sa add, addWeighted, scaleAdd, Mat::convertTo @@ -669,7 +669,7 @@ independently for each channel, and return it: When all the mask elements are 0's, the function returns Scalar::all(0) @param src input array that should have from 1 to 4 channels so that the result can be stored in Scalar_ . -@param mask optional operation mask. +@param mask optional operation mask ot type CV_8U, CV_8S or CV_Bool. @sa countNonZero, meanStdDev, norm, minMaxLoc */ CV_EXPORTS_W Scalar mean(InputArray src, InputArray mask = noArray()); @@ -691,7 +691,7 @@ then pass the matrix to calcCovarMatrix . Scalar_ 's. @param mean output parameter: calculated mean value. @param stddev output parameter: calculated standard deviation. -@param mask optional operation mask. +@param mask optional operation mask of type CV_8U, CV_8S or CV_Bool. @sa countNonZero, mean, norm, minMaxLoc, calcCovarMatrix */ CV_EXPORTS_W void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, @@ -731,7 +731,7 @@ Hamming norms can only be calculated with CV_8U depth arrays. @param src1 first input array. @param normType type of the norm (see #NormTypes). -@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +@param mask optional operation mask; it must have the same size as src1 and type CV_8UC1, CV_8SC1 or CV_BoolC1. */ CV_EXPORTS_W double norm(InputArray src1, int normType = NORM_L2, InputArray mask = noArray()); @@ -744,7 +744,7 @@ The type of norm to calculate is specified using #NormTypes. @param src1 first input array. @param src2 second input array of the same size and the same type as src1. @param normType type of the norm (see #NormTypes). -@param mask optional operation mask; it must have the same size as src1 and CV_8UC1 type. +@param mask optional operation mask; it must have the same size as src1 and type CV_8UC1, CV_8S1 or CV_BoolC1. */ CV_EXPORTS_W double norm(InputArray src1, InputArray src2, int normType = NORM_L2, InputArray mask = noArray()); @@ -841,7 +841,7 @@ normalization. @param norm_type normalization type (see cv::NormTypes). @param dtype when negative, the output array has the same type as src; otherwise, it has the same number of channels as src and the depth =CV_MAT_DEPTH(dtype). -@param mask optional operation mask. +@param mask optional operation mask of type CV_8U, CV_8S or CV_Bool. @sa norm, Mat::convertTo, SparseMat::convertTo */ CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, @@ -872,7 +872,7 @@ mixChannels, or split. @param maxVal pointer to the returned maximum value; NULL is used if not required. @param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required. @param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required. -@param mask optional mask used to select a sub-array. +@param mask optional mask used to select a sub-array of type CV_8U, CV_8S or CV_Bool. @sa max, min, reduceArgMin, reduceArgMax, compare, inRange, extractImageCOI, mixChannels, split, Mat::reshape */ CV_EXPORTS_W void minMaxLoc(InputArray src, CV_OUT double* minVal, @@ -1359,7 +1359,7 @@ converted to the array type. @param src2 second input array or a scalar. @param dst output array that has the same size and type as the input arrays. -@param mask optional operation mask, 8-bit single channel array, that +@param mask optional operation mask, CV_8U, CV_8S or CV_Bool single channel array, that specifies elements of the output array to be changed. */ CV_EXPORTS_W void bitwise_and(InputArray src1, InputArray src2, @@ -1386,7 +1386,7 @@ converted to the array type. @param src2 second input array or a scalar. @param dst output array that has the same size and type as the input arrays. -@param mask optional operation mask, 8-bit single channel array, that +@param mask optional operation mask, CV_8U, CV_8S or CV_Bool single channel array, that specifies elements of the output array to be changed. */ CV_EXPORTS_W void bitwise_or(InputArray src1, InputArray src2, @@ -1414,7 +1414,7 @@ converted to the array type. @param src2 second input array or a scalar. @param dst output array that has the same size and type as the input arrays. -@param mask optional operation mask, 8-bit single channel array, that +@param mask optional operation mask, CV_8U, CV_8S or CV_Bool single channel array, that specifies elements of the output array to be changed. */ CV_EXPORTS_W void bitwise_xor(InputArray src1, InputArray src2, @@ -1431,7 +1431,7 @@ case of multi-channel arrays, each channel is processed independently. @param src input array. @param dst output array that has the same size and type as the input array. -@param mask optional operation mask, 8-bit single channel array, that +@param mask optional operation mask, CV_8U, CV_8S or CV_Bool single channel array, that specifies elements of the output array to be changed. */ CV_EXPORTS_W void bitwise_not(InputArray src, OutputArray dst, @@ -1472,7 +1472,7 @@ When the operation mask is specified, if the Mat::create call shown above reallo @param dst Destination matrix. If it does not have a proper size or type before the operation, it is reallocated. @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix -elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. +elements need to be copied. The mask has to be of type CV_8U, CV_8S or CV_Bool and can have 1 or multiple channels. */ void CV_EXPORTS_W copyTo(InputArray src, OutputArray dst, InputArray mask); diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 59b95e2f40..f0c421f810 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1232,7 +1232,8 @@ public: @param m Destination matrix. If it does not have a proper size or type before the operation, it is reallocated. @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix - elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels. + elements need to be copied. The mask has to be of type CV_8U, CV_8S or CV_Bool and can have 1 or + multiple channels. */ void copyTo( OutputArray m, InputArray mask ) const; @@ -1269,7 +1270,8 @@ public: This is an advanced variant of the Mat::operator=(const Scalar& s) operator. @param value Assigned scalar converted to the actual array type. @param mask Operation mask of the same size as \*this. Its non-zero elements indicate which matrix - elements need to be copied. The mask has to be of type CV_8U and can have 1 or multiple channels + elements need to be copied. The mask has to be of type CV_8U, CV_8S or CV_Bool and can have 1 or + multiple channels. */ Mat& setTo(InputArray value, InputArray mask=noArray()); diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 9ca9fbe58b..2e038e7da2 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -227,7 +227,7 @@ static void binary_op( InputArray _src1, InputArray _src2, OutputArray _dst, if( haveMask ) { int mtype = _mask.type(); - CV_Assert( (mtype == CV_8U || mtype == CV_8S) && _mask.sameSize(*psrc1)); + CV_Assert( (mtype == CV_8U || mtype == CV_8S || mtype == CV_Bool) && _mask.sameSize(*psrc1)); copymask = getCopyMaskFunc(esz); reallocate = !_dst.sameSize(*psrc1) || _dst.type() != type1; } @@ -739,7 +739,7 @@ static void arithm_op(InputArray _src1, InputArray _src2, OutputArray _dst, if( haveMask ) { int mtype = _mask.type(); - CV_Assert( (mtype == CV_8UC1 || mtype == CV_8SC1) && _mask.sameSize(*psrc1) ); + CV_Assert( (mtype == CV_8UC1 || mtype == CV_8SC1 || mtype == CV_Bool) && _mask.sameSize(*psrc1) ); reallocate = !_dst.sameSize(*psrc1) || _dst.type() != dtype; } @@ -1261,7 +1261,7 @@ static BinaryFuncC getCmpFunc(int depth) (BinaryFuncC)cv::hal::cmp64f, (BinaryFuncC)cv::hal::cmp16f, (BinaryFuncC)cv::hal::cmp16bf, - 0, + (BinaryFuncC)GET_OPTIMIZED(cv::hal::cmp8u), (BinaryFuncC)cv::hal::cmp64u, (BinaryFuncC)cv::hal::cmp64s, (BinaryFuncC)cv::hal::cmp32u, diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index fd9b72db3d..ccfe6a1f68 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -454,7 +454,7 @@ void Mat::copyTo( OutputArray _dst, InputArray _mask ) const } int cn = channels(), mcn = mask.channels(); - CV_Assert( mask.depth() == CV_8U && (mcn == 1 || mcn == cn) ); + CV_Assert( (mask.depth() == CV_8U || mask.depth() == CV_8S || mask.depth() == CV_Bool) && (mcn == 1 || mcn == cn) ); bool colorMask = mcn > 1; if( dims <= 2 ) { @@ -643,7 +643,8 @@ Mat& Mat::setTo(InputArray _value, InputArray _mask) CV_Assert( checkScalar(value, type(), _value.kind(), _InputArray::MAT )); int cn = channels(), mcn = mask.channels(); - CV_Assert( mask.empty() || (mask.depth() == CV_8U && (mcn == 1 || mcn == cn) && size == mask.size) ); + CV_Assert( mask.empty() || ((mask.depth() == CV_8U || mask.depth() == CV_8S || mask.depth() == CV_Bool) && + (mcn == 1 || mcn == cn) && size == mask.size) ); CV_IPP_RUN_FAST(ipp_Mat_setTo_Mat(*this, value, mask), *this) diff --git a/modules/core/src/mean.dispatch.cpp b/modules/core/src/mean.dispatch.cpp index 05a1c2d146..745a4f2d17 100644 --- a/modules/core/src/mean.dispatch.cpp +++ b/modules/core/src/mean.dispatch.cpp @@ -126,7 +126,7 @@ Scalar mean(InputArray _src, InputArray _mask) CV_INSTRUMENT_REGION(); Mat src = _src.getMat(), mask = _mask.getMat(); - CV_Assert( mask.empty() || mask.type() == CV_8U ); + CV_Assert( mask.empty() || mask.type() == CV_8U || mask.type() == CV_8S || mask.type() == CV_Bool); int k, cn = src.channels(), depth = src.depth(); Scalar s; @@ -227,7 +227,7 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv int ddepth = std::max(CV_32S, depth), sqddepth = std::max(CV_32F, depth), dtype = CV_MAKE_TYPE(ddepth, cn), sqdtype = CV_MAKETYPE(sqddepth, cn); - CV_Assert(!haveMask || _mask.type() == CV_8UC1); + CV_Assert(!haveMask || _mask.type() == CV_8U || _mask.type() == CV_8S || _mask.type() == CV_Bool); int wgs2_aligned = 1; while (wgs2_aligned < (int)wgs) @@ -461,14 +461,14 @@ void meanStdDev(InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray CV_INSTRUMENT_REGION(); CV_Assert(!_src.empty()); - CV_Assert( _mask.empty() || _mask.type() == CV_8UC1 ); + CV_Assert( _mask.empty() || _mask.type() == CV_8U || _mask.type() == CV_8S || _mask.type() == CV_Bool ); CV_OCL_RUN(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, ocl_meanStdDev(_src, _mean, _sdv, _mask)) Mat src = _src.getMat(), mask = _mask.getMat(); - CV_Assert(mask.empty() || src.size == mask.size); + CV_Assert(mask.empty() || ((mask.type() == CV_8U || mask.type() == CV_8S || mask.type() == CV_Bool) && src.size == mask.size)); CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_meanStdDev(src, _mean, _sdv, mask)); diff --git a/modules/core/src/minmax.dispatch.cpp b/modules/core/src/minmax.dispatch.cpp index cd778b0283..09bcde0559 100644 --- a/modules/core/src/minmax.dispatch.cpp +++ b/modules/core/src/minmax.dispatch.cpp @@ -171,7 +171,7 @@ bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minLoc if ((haveMask || type == CV_32FC1) && dev.isAMD()) return false; - CV_Assert( (cn == 1 && (!haveMask || _mask.type() == CV_8U)) || + CV_Assert( (cn == 1 && (!haveMask || _mask.type() == CV_8U || _mask.type() == CV_8S || _mask.type() == CV_Bool)) || (cn >= 1 && !minLoc && !maxLoc) ); if (ddepth < 0) @@ -302,8 +302,8 @@ void cv::minMaxIdx(InputArray _src, double* minVal, CV_INSTRUMENT_REGION(); int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - CV_Assert( (cn == 1 && (_mask.empty() || _mask.type() == CV_8U)) || - (cn > 1 && _mask.empty() && !minIdx && !maxIdx) ); + CV_Assert( (cn == 1 && (_mask.empty() || _mask.type() == CV_8U || _mask.type() == CV_8S || _mask.type() == CV_Bool)) || + (cn > 1 && _mask.empty() && !minIdx && !maxIdx) ); CV_OCL_RUN(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2 && (_mask.empty() || _src.size() == _mask.size()), ocl_minMaxIdx(_src, minVal, maxVal, minIdx, maxIdx, _mask)) diff --git a/modules/core/src/norm.cpp b/modules/core/src/norm.cpp index f7a1039ea3..b141c29ede 100644 --- a/modules/core/src/norm.cpp +++ b/modules/core/src/norm.cpp @@ -744,7 +744,7 @@ double norm( InputArray _src, int normType, InputArray _mask ) } } - CV_Assert( mask.empty() || mask.type() == CV_8U ); + CV_Assert( mask.empty() || mask.type() == CV_8U || mask.type() == CV_8S || mask.type() == CV_Bool ); if( normType == NORM_HAMMING || normType == NORM_HAMMING2 ) { @@ -1197,7 +1197,7 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask } } - CV_Assert( mask.empty() || mask.type() == CV_8U ); + CV_Assert( mask.empty() || mask.type() == CV_8U || mask.type() == CV_8S || mask.type() == CV_Bool ); if( normType == NORM_HAMMING || normType == NORM_HAMMING2 ) { diff --git a/modules/core/src/sum.dispatch.cpp b/modules/core/src/sum.dispatch.cpp index 628b6c1873..e56e10117f 100644 --- a/modules/core/src/sum.dispatch.cpp +++ b/modules/core/src/sum.dispatch.cpp @@ -56,7 +56,7 @@ bool ocl_sum( InputArray _src, Scalar & res, int sum_op, InputArray _mask, int ddepth = std::max(sum_op == OCL_OP_SUM_SQR ? CV_32F : CV_32S, depth), dtype = CV_MAKE_TYPE(ddepth, cn); - CV_Assert(!haveMask || _mask.type() == CV_8UC1); + CV_Assert(!haveMask || _mask.type() == CV_8U || _mask.type() == CV_8S || _mask.type() == CV_Bool); int wgs2_aligned = 1; while (wgs2_aligned < (int)wgs) diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index 5babe050ef..f28aa3693f 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -1230,7 +1230,7 @@ void UMat::copyTo(OutputArray _dst, InputArray _mask) const } #ifdef HAVE_OPENCL int cn = channels(), mtype = _mask.type(), mdepth = CV_MAT_DEPTH(mtype), mcn = CV_MAT_CN(mtype); - CV_Assert( mdepth == CV_8U && (mcn == 1 || mcn == cn) ); + CV_Assert( (mdepth == CV_8U || mdepth == CV_8S || mdepth == CV_Bool) && (mcn == 1 || mcn == cn) ); if (ocl::useOpenCL() && _dst.isUMat() && dims <= 2) { @@ -1307,7 +1307,7 @@ UMat& UMat::setTo(InputArray _value, InputArray _mask) if( haveMask ) { mask = _mask.getUMat(); - CV_Assert( mask.size() == size() && mask.type() == CV_8UC1 ); + CV_Assert( mask.size() == size() && (mask.type() == CV_8U || mask.type() == CV_8S || mask.type() == CV_Bool) ); ocl::KernelArg maskarg = ocl::KernelArg::ReadOnlyNoSize(mask), dstarg = ocl::KernelArg::ReadWrite(*this); setK.args(maskarg, dstarg, scalararg); diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index a1295b743d..0452d46e83 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -2462,23 +2462,22 @@ TEST(Compare, regression_16F_do_not_crash) EXPECT_NO_THROW(cv::compare(mat1, mat2, dst, cv::CMP_EQ)); } - TEST(Core_minMaxIdx, regression_9207_1) { const int rows = 4; const int cols = 3; uchar mask_[rows*cols] = { - 255, 255, 255, - 255, 0, 255, - 0, 255, 255, - 0, 0, 255 -}; + 255, 255, 255, + 255, 0, 255, + 0, 255, 255, + 0, 0, 255 + }; uchar src_[rows*cols] = { - 1, 1, 1, - 1, 1, 1, - 2, 1, 1, - 2, 2, 1 -}; + 1, 1, 1, + 1, 1, 1, + 2, 1, 1, + 2, 2, 1 + }; Mat mask(Size(cols, rows), CV_8UC1, mask_); Mat src(Size(cols, rows), CV_8UC1, src_); double minVal = -0.0, maxVal = -0.0; @@ -2490,7 +2489,6 @@ TEST(Core_minMaxIdx, regression_9207_1) EXPECT_EQ(0, maxIdx[1]); } - class TransposeND : public testing::TestWithParam< tuple, perf::MatType> > { public: @@ -2886,11 +2884,11 @@ TEST(Core_Norm, IPP_regression_NORM_L1_16UC3_small) Mat a(sz, CV_MAKE_TYPE(CV_16U, cn), Scalar::all(1)); Mat b(sz, CV_MAKE_TYPE(CV_16U, cn), Scalar::all(2)); uchar mask_[9*4] = { - 255, 255, 255, 0, 255, 255, 0, 255, 0, - 0, 255, 0, 0, 255, 255, 255, 255, 0, - 0, 0, 0, 255, 0, 255, 0, 255, 255, - 0, 0, 255, 0, 255, 255, 255, 0, 255 -}; + 255, 255, 255, 0, 255, 255, 0, 255, 0, + 0, 255, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 255, 0, 255, 0, 255, 255, + 0, 0, 255, 0, 255, 255, 255, 0, 255 + }; Mat mask(sz, CV_8UC1, mask_); EXPECT_EQ((double)9*4*cn, cv::norm(a, b, NORM_L1)); // without mask, IPP works well @@ -3622,7 +3620,169 @@ TEST_P(Core_LUT, accuracy_multi) ASSERT_EQ(0, cv::norm(output, gt, cv::NORM_INF)); } - INSTANTIATE_TEST_CASE_P(/**/, Core_LUT, perf::MatDepth::all()); +CV_ENUM(MaskType, CV_8U, CV_8S, CV_Bool) +typedef testing::TestWithParam Core_MaskTypeTest; + +TEST_P(Core_MaskTypeTest, BasicArithm) +{ + int mask_type = GetParam(); + RNG& rng = theRNG(); + const int MAX_DIM=3; + int sizes[MAX_DIM]; + for( int iter = 0; iter < 100; iter++ ) + { + int dims = rng.uniform(1, MAX_DIM+1); + int depth = rng.uniform(CV_8U, CV_64F+1); + int cn = rng.uniform(1, 6); + int type = CV_MAKETYPE(depth, cn); + int op = rng.uniform(0, depth < CV_32F ? 5 : 2); // don't run binary operations between floating-point values + int depth1 = op <= 1 ? CV_64F : depth; + for (int k = 0; k < MAX_DIM; k++) + { + sizes[k] = k < dims ? rng.uniform(1, 30) : 0; + } + + Mat a(dims, sizes, type), a1; + Mat b(dims, sizes, type), b1; + Mat mask(dims, sizes, mask_type); + Mat mask1; + Mat c, d; + + rng.fill(a, RNG::UNIFORM, 0, 100); + rng.fill(b, RNG::UNIFORM, 0, 100); + + // [-2,2) range means that the each generated random number + // will be one of -2, -1, 0, 1. Saturated to [0,255], it will become + // 0, 0, 0, 1 => the mask will be filled by ~25%. + rng.fill(mask, RNG::UNIFORM, -2, 2); + + a.convertTo(a1, depth1); + b.convertTo(b1, depth1); + // invert the mask + cv::compare(mask, 0, mask1, CMP_EQ); + a1.setTo(0, mask1); + b1.setTo(0, mask1); + + if( op == 0 ) + { + cv::add(a, b, c, mask); + cv::add(a1, b1, d); + } + else if( op == 1 ) + { + cv::subtract(a, b, c, mask); + cv::subtract(a1, b1, d); + } + else if( op == 2 ) + { + cv::bitwise_and(a, b, c, mask); + cv::bitwise_and(a1, b1, d); + } + else if( op == 3 ) + { + cv::bitwise_or(a, b, c, mask); + cv::bitwise_or(a1, b1, d); + } + else if( op == 4 ) + { + cv::bitwise_xor(a, b, c, mask); + cv::bitwise_xor(a1, b1, d); + } + Mat d1; + d.convertTo(d1, depth); + EXPECT_LE(cvtest::norm(c, d1, NORM_INF), DBL_EPSILON); + } +} + +TEST_P(Core_MaskTypeTest, MinMaxIdx) +{ + int mask_type = GetParam(); + const int rows = 4; + const int cols = 3; + uchar mask_[rows*cols] = { + 255, 255, 1, + 255, 0, 255, + 0, 1, 255, + 0, 0, 255 + }; + uchar src_[rows*cols] = { + 1, 1, 1, + 1, 1, 1, + 2, 1, 1, + 2, 2, 1 + }; + Mat mask(Size(cols, rows), mask_type, mask_); + Mat src(Size(cols, rows), CV_8UC1, src_); + double minVal = -0.0, maxVal = -0.0; + int minIdx[2] = { -2, -2 }, maxIdx[2] = { -2, -2 }; + cv::minMaxIdx(src, &minVal, &maxVal, minIdx, maxIdx, mask); + EXPECT_EQ(0, minIdx[0]); + EXPECT_EQ(0, minIdx[1]); + EXPECT_EQ(0, maxIdx[0]); + EXPECT_EQ(0, maxIdx[1]); +} + +TEST_P(Core_MaskTypeTest, Norm) +{ + int mask_type = GetParam(); + int cn = 3; + Size sz(9, 4); // width < 16 + Mat a(sz, CV_MAKE_TYPE(CV_16U, cn), Scalar::all(1)); + Mat b(sz, CV_MAKE_TYPE(CV_16U, cn), Scalar::all(2)); + uchar mask_[9*4] = { + 255, 255, 255, 0, 1, 255, 0, 255, 0, + 0, 255, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 255, 0, 1, 0, 255, 255, + 0, 0, 255, 0, 255, 255, 1, 0, 255 + }; + Mat mask(sz, mask_type, mask_); + + EXPECT_EQ((double)9*4*cn, cv::norm(a, b, NORM_L1)); // without mask, IPP works well + EXPECT_EQ((double)20*cn, cv::norm(a, b, NORM_L1, mask)); +} + +TEST_P(Core_MaskTypeTest, Mean) +{ + int mask_type = GetParam(); + Size sz(9, 4); + Mat a(sz, CV_16UC1, Scalar::all(1)); + uchar mask_[9*4] = { + 255, 255, 255, 0, 1, 255, 0, 255, 0, + 0, 255, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 1, 0, 255, 0, 1, 255, + 0, 0, 255, 0, 255, 255, 255, 0, 255 + }; + Mat mask(sz, mask_type, mask_); + a.setTo(2, mask); + + Scalar result = cv::mean(a, mask); + EXPECT_NEAR(result[0], 2, 1e-6); +} + +TEST_P(Core_MaskTypeTest, MeanStdDev) +{ + int mask_type = GetParam(); + Size sz(9, 4); + Mat a(sz, CV_16UC1, Scalar::all(1)); + uchar mask_[9*4] = { + 255, 255, 255, 0, 1, 255, 0, 255, 0, + 0, 255, 0, 0, 255, 255, 255, 255, 0, + 0, 0, 0, 1, 0, 255, 0, 1, 255, + 0, 0, 255, 0, 255, 255, 255, 0, 255 + }; + Mat mask(sz, mask_type, mask_); + a.setTo(2, mask); + + Scalar m, stddev; + cv::meanStdDev(a, m, stddev, mask); + + EXPECT_NEAR(m[0], 2, 1e-6); + EXPECT_NEAR(stddev[0], 0, 1e-6); +} + +INSTANTIATE_TEST_CASE_P(/**/, Core_MaskTypeTest, MaskType::all()); + + }} // namespace diff --git a/modules/ts/src/ts_func.cpp b/modules/ts/src/ts_func.cpp index da23bef9af..7ead775fe3 100644 --- a/modules/ts/src/ts_func.cpp +++ b/modules/ts/src/ts_func.cpp @@ -431,7 +431,7 @@ void copy(const Mat& src, Mat& dst, const Mat& mask, bool invertMask) } int mcn = mask.channels(); - CV_Assert( src.size == mask.size && mask.depth() == CV_8U + CV_Assert( src.size == mask.size && (mask.depth() == CV_8U || mask.depth() == CV_Bool) && (mcn == 1 || mcn == src.channels()) ); const Mat *arrays[]={&src, &dst, &mask, 0}; @@ -1397,7 +1397,7 @@ double norm(InputArray _src, int normType, InputArray _mask) int normType0 = normType; normType = normType == NORM_L2SQR ? NORM_L2 : normType; - CV_Assert( mask.empty() || (src.size == mask.size && mask.type() == CV_8U) ); + CV_Assert( mask.empty() || (src.size == mask.size && (mask.type() == CV_8U || mask.type() == CV_Bool)) ); CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); const Mat *arrays[]={&src, &mask, 0}; @@ -1506,7 +1506,7 @@ double norm(InputArray _src1, InputArray _src2, int normType, InputArray _mask) CV_CheckTypeEQ(src1.type(), src2.type(), ""); CV_Assert(src1.size == src2.size); - CV_Assert( mask.empty() || (src1.size == mask.size && mask.type() == CV_8U) ); + CV_Assert( mask.empty() || (src1.size == mask.size && (mask.type() == CV_8U || mask.type() == CV_Bool)) ); CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); const Mat *arrays[]={&src1, &src2, &mask, 0}; Mat planes[3]; @@ -2937,7 +2937,7 @@ mean_(const _Tp* src, const uchar* mask, size_t total, int cn, Scalar& sum, int& Scalar mean(const Mat& src, const Mat& mask) { - CV_Assert(mask.empty() || (mask.type() == CV_8U && mask.size == src.size)); + CV_Assert(mask.empty() || ((mask.type() == CV_8U || mask.type() == CV_Bool) && mask.size == src.size)); Scalar sum; int nz = 0;