mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge pull request #25671 from savuor:rv/arithm_extend_tests
Tests added for mixed type arithmetic operations #25671 ### Changes * added accuracy tests for mixed type arithmetic operations _Note: div-by-zero values are removed from checking since the result is implementation-defined in common case_ * added perf tests for the same cases * fixed a typo in `getMulExtTab()` function that lead to dead code ### 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. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
parent
1bd5ca1ebe
commit
a7e53aa184
@ -452,6 +452,260 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , BinaryOpTest,
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
///////////// Mixed type arithmetics ////////
|
||||||
|
|
||||||
|
typedef perf::TestBaseWithParam<std::tuple<cv::Size, std::tuple<perf::MatType, perf::MatType>>> 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<perf::MatType, perf::MatType>{CV_8U, CV_16U},
|
||||||
|
std::tuple<perf::MatType, perf::MatType>{CV_8S, CV_16S},
|
||||||
|
std::tuple<perf::MatType, perf::MatType>{CV_8U, CV_32F},
|
||||||
|
std::tuple<perf::MatType, perf::MatType>{CV_8S, CV_32F}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
///////////// Rotate ////////////////////////
|
///////////// Rotate ////////////////////////
|
||||||
|
|
||||||
typedef perf::TestBaseWithParam<std::tuple<cv::Size, int, perf::MatType>> RotateTest;
|
typedef perf::TestBaseWithParam<std::tuple<cv::Size, int, perf::MatType>> RotateTest;
|
||||||
|
@ -1081,7 +1081,7 @@ static ExtendedTypeFunc getMulExtFunc(int src1Type, int src2Type, int dstType)
|
|||||||
{
|
{
|
||||||
return mul8u16uWrapper;
|
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;
|
return mul8s16sWrapper;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,12 @@ const int ARITHM_MAX_SIZE_LOG = 10;
|
|||||||
|
|
||||||
struct BaseElemWiseOp
|
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,
|
BaseElemWiseOp(int _ninputs, int _flags, double _alpha, double _beta,
|
||||||
Scalar _gamma=Scalar::all(0), int _context=1)
|
Scalar _gamma=Scalar::all(0), int _context=1)
|
||||||
: ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {}
|
: ninputs(_ninputs), flags(_flags), alpha(_alpha), beta(_beta), gamma(_gamma), context(_context) {}
|
||||||
@ -101,14 +106,15 @@ struct BaseAddOp : public BaseElemWiseOp
|
|||||||
|
|
||||||
void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
void refop(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
||||||
{
|
{
|
||||||
Mat temp;
|
int dstType = (flags & MIXED_TYPE) ? dst.type() : src[0].type();
|
||||||
if( !mask.empty() )
|
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);
|
cvtest::copy(temp, dst, mask);
|
||||||
}
|
}
|
||||||
else
|
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)) {}
|
AddOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, 1, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
||||||
{
|
{
|
||||||
if( mask.empty() )
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
cv::add(src[0], src[1], dst);
|
cv::add(src[0], src[1], dst, mask, dtype);
|
||||||
else
|
|
||||||
cv::add(src[0], src[1], dst, mask);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,10 +135,8 @@ struct SubOp : public BaseAddOp
|
|||||||
SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {}
|
SubOp() : BaseAddOp(2, FIX_ALPHA+FIX_BETA+FIX_GAMMA+SUPPORT_MASK, 1, -1, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
||||||
{
|
{
|
||||||
if( mask.empty() )
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
cv::subtract(src[0], src[1], dst);
|
cv::subtract(src[0], src[1], dst, mask, dtype);
|
||||||
else
|
|
||||||
cv::subtract(src[0], src[1], dst, mask);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,10 +146,8 @@ struct AddSOp : public BaseAddOp
|
|||||||
AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {}
|
AddSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, 1, 0, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
||||||
{
|
{
|
||||||
if( mask.empty() )
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
cv::add(src[0], gamma, dst);
|
cv::add(src[0], gamma, dst, mask, dtype);
|
||||||
else
|
|
||||||
cv::add(src[0], gamma, dst, mask);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,10 +157,8 @@ struct SubRSOp : public BaseAddOp
|
|||||||
SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {}
|
SubRSOp() : BaseAddOp(1, FIX_ALPHA+FIX_BETA+SUPPORT_MASK, -1, 0, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
void op(const vector<Mat>& src, Mat& dst, const Mat& mask)
|
||||||
{
|
{
|
||||||
if( mask.empty() )
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
cv::subtract(gamma, src[0], dst);
|
cv::subtract(gamma, src[0], dst, mask, dtype);
|
||||||
else
|
|
||||||
cv::subtract(gamma, src[0], dst, mask);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -174,7 +172,7 @@ struct ScaleAddOp : public BaseAddOp
|
|||||||
}
|
}
|
||||||
double getMaxErr(int depth)
|
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)) {}
|
AddWeightedOp() : BaseAddOp(2, REAL_GAMMA, 1, 1, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat&)
|
void op(const vector<Mat>& src, Mat& dst, const Mat&)
|
||||||
{
|
{
|
||||||
cv::addWeighted(src[0], alpha, src[1], beta, gamma[0], dst);
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
}
|
cv::addWeighted(src[0], alpha, src[1], beta, gamma[0], dst, dtype);
|
||||||
double getMaxErr(int depth)
|
|
||||||
{
|
|
||||||
return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-10;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -204,15 +199,35 @@ struct MulOp : public BaseElemWiseOp
|
|||||||
}
|
}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat&)
|
void op(const vector<Mat>& 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<Mat>& src, Mat& dst, const Mat&)
|
void refop(const vector<Mat>& 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<Mat>& 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<Mat>& 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)) {}
|
DivOp() : BaseElemWiseOp(2, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat&)
|
void op(const vector<Mat>& 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<Mat>& src, Mat& dst, const Mat&)
|
void refop(const vector<Mat>& src, Mat& dst, const Mat&)
|
||||||
{
|
{
|
||||||
cvtest::divide(src[0], src[1], dst, alpha);
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
}
|
cvtest::divide(src[0], src[1], dst, alpha, dtype);
|
||||||
double getMaxErr(int depth)
|
|
||||||
{
|
|
||||||
return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -238,15 +258,20 @@ struct RecipOp : public BaseElemWiseOp
|
|||||||
RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
|
RecipOp() : BaseElemWiseOp(1, FIX_BETA+FIX_GAMMA, 1, 1, Scalar::all(0)) {}
|
||||||
void op(const vector<Mat>& src, Mat& dst, const Mat&)
|
void op(const vector<Mat>& 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<Mat>& src, Mat& dst, const Mat&)
|
void refop(const vector<Mat>& src, Mat& dst, const Mat&)
|
||||||
{
|
{
|
||||||
cvtest::divide(Mat(), src[0], dst, alpha);
|
int dtype = (flags & MIXED_TYPE) ? dst.type() : -1;
|
||||||
}
|
cvtest::divide(Mat(), src[0], dst, alpha, dtype);
|
||||||
double getMaxErr(int depth)
|
|
||||||
{
|
|
||||||
return depth <= CV_32S ? 2 : depth < CV_64F ? 1e-5 : 1e-12;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -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_reduceArgMinMax, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new reduceArgMinMaxOp)));
|
||||||
INSTANTIATE_TEST_CASE_P(Core_CartToPolarToCart, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new CartToPolarToCartOp)));
|
INSTANTIATE_TEST_CASE_P(Core_CartToPolarToCart, ElemWiseTest, ::testing::Values(ElemWiseOpPtr(new CartToPolarToCartOp)));
|
||||||
|
|
||||||
|
// Mixed Type Arithmetic Operations
|
||||||
|
|
||||||
|
typedef std::tuple<ElemWiseOpPtr, std::tuple<cvtest::MatDepth, cvtest::MatDepth>> SomeType;
|
||||||
|
class ArithmMixedTest : public ::testing::TestWithParam<SomeType> {};
|
||||||
|
|
||||||
|
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<int> 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<Mat> 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<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_AddScalarMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new AddSOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_AddWeightedMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new AddWeightedOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_SubMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new SubOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_SubScalarMinusArgMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new SubRSOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_MulMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new MulOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_MulScalarMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new MulSOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_DivMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new DivOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_16U},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_16S},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core_RecipMixed, ArithmMixedTest,
|
||||||
|
::testing::Combine(::testing::Values(ElemWiseOpPtr(new RecipOp)),
|
||||||
|
::testing::Values(std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8U, CV_32F},
|
||||||
|
std::tuple<cvtest::MatDepth, cvtest::MatDepth>{CV_8S, CV_32F})));
|
||||||
|
|
||||||
TEST(Core_ArithmMask, uninitialized)
|
TEST(Core_ArithmMask, uninitialized)
|
||||||
{
|
{
|
||||||
|
@ -300,8 +300,8 @@ Mat randomMat(RNG& rng, Size size, int type, double minVal, double maxVal, bool
|
|||||||
Mat randomMat(RNG& rng, const vector<int>& size, int type, double minVal, double maxVal, bool useRoi);
|
Mat randomMat(RNG& rng, const vector<int>& size, int type, double minVal, double maxVal, bool useRoi);
|
||||||
void add(const Mat& a, double alpha, const Mat& b, double beta,
|
void add(const Mat& a, double alpha, const Mat& b, double beta,
|
||||||
Scalar gamma, Mat& c, int ctype, bool calcAbs=false);
|
Scalar gamma, Mat& c, int ctype, bool calcAbs=false);
|
||||||
void multiply(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);
|
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 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);
|
void copy(const Mat& src, Mat& dst, const Mat& mask=Mat(), bool invertMask=false);
|
||||||
|
@ -2551,30 +2551,31 @@ void max(const Mat& src1, double val, Mat& dst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename _Tp> static void
|
template<typename SrcType, typename DstType> static void
|
||||||
muldiv_(const _Tp* src1, const _Tp* src2, _Tp* dst, size_t total, double scale, char op)
|
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++ )
|
||||||
for( size_t i = 0; i < total; i++ )
|
{
|
||||||
dst[i] = saturate_cast<_Tp>((scale*src1[i])*src2[i]);
|
double m1 = src1 ? (double)src1[i] : 1.0;
|
||||||
else if( src1 )
|
double m2 = src2 ? (double)src2[i] : 1.0;
|
||||||
for( size_t i = 0; i < total; i++ )
|
if (op == '/')
|
||||||
dst[i] = src2[i] ? saturate_cast<_Tp>((scale*src1[i])/src2[i]) : 0;
|
{
|
||||||
else
|
m2 = abs(m2) > FLT_EPSILON ? (1.0 / m2) : 0;
|
||||||
for( size_t i = 0; i < total; i++ )
|
}
|
||||||
dst[i] = src2[i] ? saturate_cast<_Tp>(scale/src2[i]) : 0;
|
dst[i] = saturate_cast<DstType>(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) );
|
CV_Assert( src1.empty() || (src1.type() == src2.type() && src1.size == src2.size) );
|
||||||
const Mat *arrays[]={&src1, &src2, &dst, 0};
|
const Mat *arrays[]={&src1, &src2, &dst, 0};
|
||||||
Mat planes[3];
|
Mat planes[3];
|
||||||
|
|
||||||
NAryMatIterator it(arrays, planes);
|
NAryMatIterator it(arrays, planes);
|
||||||
size_t total = planes[1].total()*planes[1].channels();
|
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 )
|
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();
|
const uchar* sptr2 = planes[1].ptr();
|
||||||
uchar* dptr = planes[2].ptr();
|
uchar* dptr = planes[2].ptr();
|
||||||
|
|
||||||
switch( depth )
|
if (srcDepth == dstDepth)
|
||||||
{
|
{
|
||||||
case CV_8U:
|
switch( srcDepth )
|
||||||
muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, scale, op);
|
{
|
||||||
break;
|
case CV_8U:
|
||||||
case CV_8S:
|
muldiv_((const uchar*)sptr1, (const uchar*)sptr2, (uchar*)dptr, total, scale, op);
|
||||||
muldiv_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_8S:
|
||||||
case CV_16U:
|
muldiv_((const schar*)sptr1, (const schar*)sptr2, (schar*)dptr, total, scale, op);
|
||||||
muldiv_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_16U:
|
||||||
case CV_16S:
|
muldiv_((const ushort*)sptr1, (const ushort*)sptr2, (ushort*)dptr, total, scale, op);
|
||||||
muldiv_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_16S:
|
||||||
case CV_32S:
|
muldiv_((const short*)sptr1, (const short*)sptr2, (short*)dptr, total, scale, op);
|
||||||
muldiv_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_32S:
|
||||||
case CV_32F:
|
muldiv_((const int*)sptr1, (const int*)sptr2, (int*)dptr, total, scale, op);
|
||||||
muldiv_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_32F:
|
||||||
case CV_64F:
|
muldiv_((const float*)sptr1, (const float*)sptr2, (float*)dptr, total, scale, op);
|
||||||
muldiv_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, scale, op);
|
break;
|
||||||
break;
|
case CV_64F:
|
||||||
default:
|
muldiv_((const double*)sptr1, (const double*)sptr2, (double*)dptr, total, scale, op);
|
||||||
CV_Error(Error::StsUnsupportedFormat, "");
|
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, '/' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user