diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index f14382059c..36f400e34f 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -452,6 +452,260 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , BinaryOpTest, ) ); +///////////// Mixed type arithmetics //////// + +typedef perf::TestBaseWithParam>> ArithmMixedTest; + +PERF_TEST_P_(ArithmMixedTest, add) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Mat b = Mat(sz, srcType); + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + declare.time(50); + + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: add can be without saturation on 32S + a /= 2; + b /= 2; + } + + TEST_CYCLE() cv::add(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, addScalarDouble) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Scalar b; + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: add can be without saturation on 32S + a /= 2; + b /= 2; + } + + TEST_CYCLE() cv::add(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, addScalarSameType) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Scalar b; + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) < CV_32S) + { + b = Scalar(1, 0, 3, 4); // don't pass non-integer values for 8U/8S/16U/16S processing + } + else if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: add can be without saturation on 32S + a /= 2; + b = Scalar(1, 0, -3, 4); // don't pass non-integer values for 32S processing + } + + TEST_CYCLE() cv::add(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, subtract) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Mat b = Mat(sz, srcType); + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: subtract can be without saturation on 32S + a /= 2; + b /= 2; + } + + TEST_CYCLE() cv::subtract(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, subtractScalarDouble) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Scalar b; + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: subtract can be without saturation on 32S + a /= 2; + b /= 2; + } + + TEST_CYCLE() cv::subtract(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, subtractScalarSameType) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a = Mat(sz, srcType); + cv::Scalar b; + cv::Mat c = Mat(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) < CV_32S) + { + b = Scalar(1, 0, 3, 4); // don't pass non-integer values for 8U/8S/16U/16S processing + } + else if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //see ticket 1529: subtract can be without saturation on 32S + a /= 2; + b = Scalar(1, 0, -3, 4); // don't pass non-integer values for 32S processing + } + + TEST_CYCLE() cv::subtract(a, b, c, /* mask */ noArray(), dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, multiply) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a(sz, srcType), b(sz, srcType), c(sz, dstType); + + declare.in(a, b, WARMUP_RNG).out(c); + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //According to docs, saturation is not applied when result is 32bit integer + a /= (2 << 16); + b /= (2 << 16); + } + + TEST_CYCLE() cv::multiply(a, b, c, /* scale */ 1.0, dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, multiplyScale) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a(sz, srcType), b(sz, srcType), c(sz, dstType); + double scale = 0.5; + + declare.in(a, b, WARMUP_RNG).out(c); + + if (CV_MAT_DEPTH(dstType) == CV_32S) + { + //According to docs, saturation is not applied when result is 32bit integer + a /= (2 << 16); + b /= (2 << 16); + } + + TEST_CYCLE() cv::multiply(a, b, c, scale, dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, divide) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat a(sz, srcType), b(sz, srcType), c(sz, dstType); + double scale = 0.5; + + declare.in(a, b, WARMUP_RNG).out(c); + + TEST_CYCLE() cv::divide(a, b, c, scale, dstType); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P_(ArithmMixedTest, reciprocal) +{ + auto p = GetParam(); + Size sz = get<0>(p); + int srcType = get<0>(get<1>(p)); + int dstType = get<1>(get<1>(p)); + + cv::Mat b(sz, srcType), c(sz, dstType); + double scale = 0.5; + + declare.in(b, WARMUP_RNG).out(c); + + TEST_CYCLE() cv::divide(scale, b, c, dstType); + + SANITY_CHECK_NOTHING(); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest, + testing::Combine( + testing::Values(szVGA, sz720p, sz1080p), + testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F} + ) + ) +); + ///////////// Rotate //////////////////////// typedef perf::TestBaseWithParam> RotateTest; diff --git a/modules/core/src/arithm.cpp b/modules/core/src/arithm.cpp index 1a2eb3d997..08e1f613ef 100644 --- a/modules/core/src/arithm.cpp +++ b/modules/core/src/arithm.cpp @@ -1081,7 +1081,7 @@ static ExtendedTypeFunc getMulExtFunc(int src1Type, int src2Type, int dstType) { return mul8u16uWrapper; } - else if (src1Type == CV_8U && src2Type == CV_8S && dstType == CV_16S) + else if (src1Type == CV_8S && src2Type == CV_8S && dstType == CV_16S) { return mul8s16sWrapper; } diff --git a/modules/core/test/test_arithm.cpp b/modules/core/test/test_arithm.cpp index ed15c5dcba..6e4151c0cc 100644 --- a/modules/core/test/test_arithm.cpp +++ b/modules/core/test/test_arithm.cpp @@ -15,7 +15,12 @@ const int ARITHM_MAX_SIZE_LOG = 10; struct BaseElemWiseOp { - enum { FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, SUPPORT_MASK=16, SCALAR_OUTPUT=32, SUPPORT_MULTICHANNELMASK=64 }; + enum + { + FIX_ALPHA=1, FIX_BETA=2, FIX_GAMMA=4, REAL_GAMMA=8, + SUPPORT_MASK=16, SCALAR_OUTPUT=32, SUPPORT_MULTICHANNELMASK=64, + MIXED_TYPE=128 + }; BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta, Scalar _gamma=Scalar::all(0), int _context=1) : ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {} @@ -101,14 +106,15 @@ struct BaseAddOp : public BaseElemWiseOp void refop(const vector& src, Mat& dst, const Mat& mask) { - Mat temp; + int dstType = (flags & MIXED_TYPE) ? dst.type() : src[0].type(); if( !mask.empty() ) { - cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, temp, src[0].type()); + Mat temp; + cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, temp, dstType); cvtest::copy(temp, dst, mask); } else - cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, dst, src[0].type()); + cvtest::add(src[0], alpha, src.size() > 1 ? src[1] : Mat(), beta, gamma, dst, dstType); } }; @@ -118,10 +124,8 @@ struct AddOp : public BaseAddOp AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { - if( mask.empty() ) - cv::add(src[0], src[1], dst); - else - cv::add(src[0], src[1], dst, mask); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::add(src[0], src[1], dst, mask, dtype); } }; @@ -131,10 +135,8 @@ struct SubOp : public BaseAddOp SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { - if( mask.empty() ) - cv::subtract(src[0], src[1], dst); - else - cv::subtract(src[0], src[1], dst, mask); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::subtract(src[0], src[1], dst, mask, dtype); } }; @@ -144,10 +146,8 @@ struct AddSOp : public BaseAddOp AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { - if( mask.empty() ) - cv::add(src[0], gamma, dst); - else - cv::add(src[0], gamma, dst, mask); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::add(src[0], gamma, dst, mask, dtype); } }; @@ -157,10 +157,8 @@ struct SubRSOp : public BaseAddOp SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat& mask) { - if( mask.empty() ) - cv::subtract(gamma, src[0], dst); - else - cv::subtract(gamma, src[0], dst, mask); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::subtract(gamma, src[0], dst, mask, dtype); } }; @@ -174,7 +172,7 @@ struct ScaleAddOp : public BaseAddOp } double getMaxErr(int depth) { - return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-4 : 1e-12; + return depth < CV_32F ? 1 : depth == CV_32F ? 3e-5 : 1e-12; } }; @@ -184,11 +182,8 @@ struct AddWeightedOp : public BaseAddOp AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { - cv::addWeighted(src[0], alpha, src[1], beta, gamma[0], dst); - } - double getMaxErr(int depth) - { - return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-10; + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::addWeighted(src[0], alpha, src[1], beta, gamma[0], dst, dtype); } }; @@ -204,15 +199,35 @@ struct MulOp : public BaseElemWiseOp } void op(const vector& src, Mat& dst, const Mat&) { - cv::multiply(src[0], src[1], dst, alpha); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::multiply(src[0], src[1], dst, alpha, dtype); } void refop(const vector& src, Mat& dst, const Mat&) { - cvtest::multiply(src[0], src[1], dst, alpha); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cvtest::multiply(src[0], src[1], dst, alpha, dtype); } - double getMaxErr(int depth) +}; + +struct MulSOp : public BaseElemWiseOp +{ + MulSOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} + void getValueRange(int depth, double& minval, double& maxval) { - return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12; + minval = depth < CV_32S ? cvtest::getMinVal(depth) : depth == CV_32S ? -1000000 : -1000.; + maxval = depth < CV_32S ? cvtest::getMaxVal(depth) : depth == CV_32S ? 1000000 : 1000.; + minval = std::max(minval, -30000.); + maxval = std::min(maxval, 30000.); + } + void op(const vector& src, Mat& dst, const Mat&) + { + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::multiply(src[0], alpha, dst, /* scale */ 1.0, dtype); + } + void refop(const vector& src, Mat& dst, const Mat&) + { + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cvtest::multiply(Mat(), src[0], dst, alpha, dtype); } }; @@ -221,15 +236,20 @@ struct DivOp : public BaseElemWiseOp DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { - cv::divide(src[0], src[1], dst, alpha); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::divide(src[0], src[1], dst, alpha, dtype); + if (flags & MIXED_TYPE) + { + // div by zero result is implementation-defined + // since it may involve conversions to/from intermediate format + Mat zeroMask = src[1] == 0; + dst.setTo(0, zeroMask); + } } void refop(const vector& src, Mat& dst, const Mat&) { - cvtest::divide(src[0], src[1], dst, alpha); - } - double getMaxErr(int depth) - { - return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12; + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cvtest::divide(src[0], src[1], dst, alpha, dtype); } }; @@ -238,15 +258,20 @@ struct RecipOp : public BaseElemWiseOp RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {} void op(const vector& src, Mat& dst, const Mat&) { - cv::divide(alpha, src[0], dst); + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cv::divide(alpha, src[0], dst, dtype); + if (flags & MIXED_TYPE) + { + // div by zero result is implementation-defined + // since it may involve conversions to/from intermediate format + Mat zeroMask = src[0] == 0; + dst.setTo(0, zeroMask); + } } void refop(const vector& src, Mat& dst, const Mat&) { - cvtest::divide(Mat(), src[0], dst, alpha); - } - double getMaxErr(int depth) - { - return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12; + int dtype = (flags & MIXED_TYPE) ? dst.type() : -1; + cvtest::divide(Mat(), src[0], dst, alpha, dtype); } }; @@ -1613,6 +1638,107 @@ INSTANTIATE_TEST_CASE_P(Core_MinMaxLoc, ElemWiseTest, ::testing::Values(ElemWise INSTANTIATE_TEST_CASE_P(Core_reduceArgMinMax, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new reduceArgMinMaxOp))); INSTANTIATE_TEST_CASE_P(Core_CartToPolarToCart, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new CartToPolarToCartOp))); +// Mixed Type Arithmetic Operations + +typedef std::tuple> SomeType; +class ArithmMixedTest : public ::testing::TestWithParam {}; + +TEST_P(ArithmMixedTest, accuracy) +{ + auto p = GetParam(); + ElemWiseOpPtr op = std::get<0>(p); + int srcDepth = std::get<0>(std::get<1>(p)); + int dstDepth = std::get<1>(std::get<1>(p)); + + op->flags |= BaseElemWiseOp::MIXED_TYPE; + int testIdx = 0; + RNG rng((uint64)ARITHM_RNG_SEED); + for( testIdx = 0; testIdx < ARITHM_NTESTS; testIdx++ ) + { + vector size; + op->getRandomSize(rng, size); + bool haveMask = ((op->flags & BaseElemWiseOp::SUPPORT_MASK) != 0) && rng.uniform(0, 4) == 0; + + double minval=0, maxval=0; + op->getValueRange(srcDepth, minval, maxval); + int ninputs = op->ninputs; + vector src(ninputs); + for(int i = 0; i < ninputs; i++ ) + src[i] = cvtest::randomMat(rng, size, srcDepth, minval, maxval, true); + Mat dst0, dst, mask; + if( haveMask ) + { + mask = cvtest::randomMat(rng, size, CV_8UC1, 0, 2, true); + } + + dst0 = cvtest::randomMat(rng, size, dstDepth, minval, maxval, false); + dst = cvtest::randomMat(rng, size, dstDepth, minval, maxval, true); + cvtest::copy(dst, dst0); + + op->generateScalars(dstDepth, rng); + + op->refop(src, dst0, mask); + op->op(src, dst, mask); + + double maxErr = op->getMaxErr(dstDepth); + ASSERT_PRED_FORMAT2(cvtest::MatComparator(maxErr, op->context), dst0, dst) << "\nsrc[0] ~ " << + cvtest::MatInfo(!src.empty() ? src[0] : Mat()) << "\ntestCase #" << testIdx << "\n"; + } +} + + +INSTANTIATE_TEST_CASE_P(Core_AddMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new AddOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_AddScalarMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new AddSOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_AddWeightedMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new AddWeightedOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_SubMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new SubOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_SubScalarMinusArgMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new SubRSOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_MulMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new MulOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_MulScalarMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new MulSOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_DivMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new DivOp)), + ::testing::Values(std::tuple{CV_8U, CV_16U}, + std::tuple{CV_8S, CV_16S}, + std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); +INSTANTIATE_TEST_CASE_P(Core_RecipMixed, ArithmMixedTest, + ::testing::Combine(::testing::Values(ElemWiseOpPtr(new RecipOp)), + ::testing::Values(std::tuple{CV_8U, CV_32F}, + std::tuple{CV_8S, CV_32F}))); TEST(Core_ArithmMask, uninitialized) { diff --git a/modules/ts/include/opencv2/ts.hpp b/modules/ts/include/opencv2/ts.hpp index a768d0047b..83ef6fc7da 100644 --- a/modules/ts/include/opencv2/ts.hpp +++ b/modules/ts/include/opencv2/ts.hpp @@ -300,8 +300,8 @@ Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool Mat randomMat(RNG& rng, const vector& size, int type, double minVal, double maxVal, bool useRoi); void add(const Mat& a, double alpha, const Mat& b, double beta, Scalar gamma, Mat& c, int ctype, bool calcAbs=false); -void multiply(const Mat& a, const Mat& b, Mat& c, double alpha=1); -void divide(const Mat& a, const Mat& b, Mat& c, double alpha=1); +void multiply(const Mat& a, const Mat& b, Mat& c, double alpha=1, int ctype=-1); +void divide(const Mat& a, const Mat& b, Mat& c, double alpha=1, int ctype=-1); void convert(const Mat& src, cv::OutputArray dst, int dtype, double alpha=1, double beta=0); void copy(const Mat& src, Mat& dst, const Mat& mask=Mat(), bool invertMask=false); diff --git a/modules/ts/src/ts_func.cpp b/modules/ts/src/ts_func.cpp index cd02766148..a4713290b1 100644 --- a/modules/ts/src/ts_func.cpp +++ b/modules/ts/src/ts_func.cpp @@ -2551,30 +2551,31 @@ void max(const Mat& src1, double val, Mat& dst) } -template static void -muldiv_(const _Tp* src1, const _Tp* src2, _Tp* dst, size_t total, double scale, char op) +template static void +muldiv_(const SrcType* src1, const SrcType* src2, DstType* dst, size_t total, double scale, char op) { - if( op == '*' ) - for( size_t i = 0; i < total; i++ ) - dst[i] = saturate_cast<_Tp>((scale*src1[i])*src2[i]); - else if( src1 ) - for( size_t i = 0; i < total; i++ ) - dst[i] = src2[i] ? saturate_cast<_Tp>((scale*src1[i])/src2[i]) : 0; - else - for( size_t i = 0; i < total; i++ ) - dst[i] = src2[i] ? saturate_cast<_Tp>(scale/src2[i]) : 0; + for( size_t i = 0; i < total; i++ ) + { + double m1 = src1 ? (double)src1[i] : 1.0; + double m2 = src2 ? (double)src2[i] : 1.0; + if (op == '/') + { + m2 = abs(m2) > FLT_EPSILON ? (1.0 / m2) : 0; + } + dst[i] = saturate_cast(scale * m1 * m2); + } } -static void muldiv(const Mat& src1, const Mat& src2, Mat& dst, double scale, char op) +static void muldiv(const Mat& src1, const Mat& src2, Mat& dst, int ctype, double scale, char op) { - dst.create(src2.dims, src2.size, src2.type()); + dst.create(src2.dims, src2.size, (ctype >= 0 ? ctype : src2.type())); CV_Assert( src1.empty() || (src1.type() == src2.type() && src1.size == src2.size) ); const Mat *arrays[]={&src1, &src2, &dst, 0}; Mat planes[3]; NAryMatIterator it(arrays, planes); size_t total = planes[1].total()*planes[1].channels(); - size_t i, nplanes = it.nplanes, depth = src2.depth(); + size_t i, nplanes = it.nplanes, srcDepth = src2.depth(), dstDepth = dst.depth(); for( i = 0; i < nplanes; i++, ++it ) { @@ -2582,44 +2583,70 @@ static void muldiv(const Mat& src1, const Mat& src2, Mat& dst, double scale, cha const uchar* sptr2 = planes[1].ptr(); uchar* dptr = planes[2].ptr(); - switch( depth ) + if (srcDepth == dstDepth) { - case CV_8U: - muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, scale, op); - break; - case CV_8S: - muldiv_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, scale, op); - break; - case CV_16U: - muldiv_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, scale, op); - break; - case CV_16S: - muldiv_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, scale, op); - break; - case CV_32S: - muldiv_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, scale, op); - break; - case CV_32F: - muldiv_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, scale, op); - break; - case CV_64F: - muldiv_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, scale, op); - break; - default: - CV_Error(Error::StsUnsupportedFormat, ""); + switch( srcDepth ) + { + case CV_8U: + muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, scale, op); + break; + case CV_8S: + muldiv_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, scale, op); + break; + case CV_16U: + muldiv_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, scale, op); + break; + case CV_16S: + muldiv_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, scale, op); + break; + case CV_32S: + muldiv_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, scale, op); + break; + case CV_32F: + muldiv_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, scale, op); + break; + case CV_64F: + muldiv_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, scale, op); + break; + default: + CV_Error(Error::StsUnsupportedFormat, ""); + } + } + else + { + if (srcDepth == CV_8U && dstDepth == CV_16U) + { + muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (ushort*)dptr, total, scale, op); + } + else if (srcDepth == CV_8S && dstDepth == CV_16S) + { + muldiv_((const schar*)sptr1, (const schar*)sptr2, (short*)dptr, total, scale, op); + } + else if (srcDepth == CV_8U && dstDepth == CV_32F) + { + muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (float*)dptr, total, scale, op); + } + else if (srcDepth == CV_8S && dstDepth == CV_32F) + { + muldiv_((const schar*)sptr1, (const schar*)sptr2, (float*)dptr, total, scale, op); + } + else + { + CV_Error(Error::StsUnsupportedFormat, "This format combination is not supported yet"); + } } } } -void multiply(const Mat& src1, const Mat& src2, Mat& dst, double scale) +void multiply(const Mat& src1, const Mat& src2, Mat& dst, double scale, int ctype) { - muldiv( src1, src2, dst, scale, '*' ); + muldiv( src1, src2, dst, ctype, scale, '*' ); } -void divide(const Mat& src1, const Mat& src2, Mat& dst, double scale) +void divide(const Mat& src1, const Mat& src2, Mat& dst, double scale, int ctype) { - muldiv( src1, src2, dst, scale, '/' ); + muldiv( src1, src2, dst, ctype, scale, '/' ); }