fix #26138: Saturation between single and multi-channel matrix operation

Description:
The issue was that when doing the operation m = c*(m - b) with m being
an 8-Bit 3-channel matrix (CV_8UC3), the result of c*(m-b) should be a
matrix with values of 4*(100-70)=120. Instead, it is a matrix of zeros
because 4*100 was capped at 255 and after subtracting 4*70 we get -25
which becomes 0 after the final saturation operation.
So instead  I am performing calculations in higher precision (CV_16S or
CV_32F) to prevent capping out at 255 during the operations and after
performing the operation I convert it back to the original type
ensuring that capping at 255 happens only once.
This commit is contained in:
Miguel Agra 2025-03-24 09:56:11 +00:00
parent 57a78cb9df
commit 05dd290e15
2 changed files with 32 additions and 2 deletions

View File

@ -1341,8 +1341,13 @@ void MatOp_AddEx::assign(const MatExpr& e, Mat& m, int _type) const
cv::subtract(e.s, e.a, dst);
else
{
e.a.convertTo(dst, e.a.type(), e.alpha);
cv::add(dst, e.s, dst);
int depth = e.a.depth();
int precType = (depth == CV_8U || depth == CV_16U) ? CV_16S : CV_32F;
cv::Mat prec;
e.a.convertTo(prec, precType);
prec = prec * e.alpha;
cv::add(prec, e.s, prec);
prec.convertTo(dst, e.a.type());
}
if( dst.data != m.data )

View File

@ -2634,4 +2634,29 @@ TEST(Mat, Recreate1DMatWithSameMeta)
EXPECT_NO_THROW(m.create(dims, depth));
}
TEST(Mat, ScalarMultiplicationSaturation) {
for (int c = 1; c <= 9; c++) {
cv::Mat m1 = cv::Mat::zeros(1,1, CV_8UC1);
cv::Mat m3 = cv::Mat::zeros(1,1, CV_8UC3);
m1 += cv::Scalar(100);
m3 += cv::Scalar(100,100,100);
cv::Scalar b1 = cv::Scalar(70);
cv::Scalar b3 = cv::Scalar(70,70,70);
m1 = c * (m1 - b1);
m3 = c * (m3 - b3);
int m1_val = m1.at<uchar>(0,0);
int m3_val = m3.at<cv::Vec3b>(0,0)[0];
if (c * (100 - 70) > 255) {
EXPECT_EQ(m1_val, 255);
} else {
EXPECT_EQ(m1_val, c * (100 - 70));
}
if (c * (100 - 70) > 255) {
EXPECT_EQ(m3_val, 255);
} else {
EXPECT_EQ(m3_val, c * (100 - 70));
}
}
}
}} // namespace