Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent and unexpected behavior of cv.multiply() function with color images across OpenCV versions (4.5 to 4.8 vs. 4.9) #25464

Open
3 of 4 tasks
cocositu opened this issue Apr 20, 2024 · 3 comments

Comments

@cocositu
Copy link

cocositu commented Apr 20, 2024

System Information

OpenCV python version: 4.5.3 and 4.8.1 and 4.9.0
Operating System / Platform: Ubuntu 20.04 and windows 10
Python version: 3.9

Detailed description

The cv.multiply() function exhibits inconsistent and unexpected behavior when processing color images in OpenCV versions 4.5 to 4.8 compared to version 4.9. Specifically:

  1. When using an integer as the multiplier, versions 4.5 to 4.8 only the blue channel is correctly multiplied by 2.0, while the green and red channels' pixel values become zero.
  2. When using a one-dimensional NumPy array (np.array([2.0])) as the multiplier, versions 4.5 to 4.8 correctly multiply all channels (BGR) of the image by the given value. However, in OpenCV 4.9, only the blue channel is correctly multiplied, while the green and red channels' pixel values are set to zero.

These anomalies deviate from users' reasonable expectations for the cv.multiply() function, potentially leading to incorrect results or difficult-to-debug issues in applications.

Steps to reproduce

Sample Code

import cv2 as cv
import numpy as np

# Create a color image
gray_img = np.zeros((400, 400, 3), dtype=np.uint8)
gray_img[:, :, 0] = 50  # B
gray_img[:, :, 1] = 50  # G
gray_img[:, :, 2] = 50  # R

# Use an integer as the multiplier
tmp_img_int = cv.multiply(gray_img, 2)

# Use a one-dimensional NumPy array as the multiplier
tmp_img_array = cv.multiply(gray_img, np.array([2.0]))

# Output results
print(tmp_img_int[:2, :2])
print(tmp_img_array[:2, :2])

Expected Result

1 For both integer and one-dimensional NumPy array multipliers, the cv.multiply() function should correctly multiply all channels (BGR) of the color image by the specified value, regardless of the OpenCV version.

Actual Result

OpenCV versions 4.5 to 4.8:

[[[100   0   0]
  [100   0   0]]

 [[100   0   0]
  [100   0   0]]]
[[[100 100 100]
  [100 100 100]]

 [[100 100 100]
  [100 100 100]]]

OpenCV version 4.9:

[[[100 100 100]
  [100 100 100]]

 [[100 100 100]
  [100 100 100]]]
[[[100   0   0]
  [100   0   0]]

 [[100   0   0]
  [100   0   0]]]

Reproduction Steps

  1. Copy the sample code into a Python environment.
  2. Run the code using OpenCV versions 4.5 to 4.8 and then version 4.9.
  3. Observe and compare the output results.

Issue submission checklist

  • I report the issue, it's not a question
  • I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution
  • I updated to the latest OpenCV version and the issue is still there
  • There is reproducer code and related data files (videos, images, onnx, etc)
@cocositu cocositu added the bug label Apr 20, 2024
@Kumataro
Copy link
Contributor

Kumataro commented Apr 20, 2024

Maybe duplicated #25165

@Kumataro
Copy link
Contributor

Kumataro commented Apr 21, 2024

Umm, I read again. This behavious has been changed after 4.9.0, but I think OpenCV 4.9.0 behavious is correct

( More precisely, the case where src2 is Scalar is not described in the multiply function... )

  1. When using an integer as the multiplier, versions 4.5 to 4.8 only the blue channel is correctly multiplied by 2.0, while the green and red channels' pixel values become zero.
  2. When using a one-dimensional NumPy array (np.array([2.0])) as the multiplier, versions 4.5 to 4.8 correctly multiply all channels (BGR) of the image by the given value. However, in OpenCV 4.9, only the blue channel is correctly multiplied, while the green and red channels' pixel values are set to zero.

Please could you see atirhmertric function behavious at https://docs.opencv.org/4.9.0/d2/de8/group__core__array.html#ga10ac1bfb180e2cfda1701d06c24fdbd6 ?

  • Precondition : src1(I) = [50, 50, 50]
  • Try to execute : dst = multiply( src1 , src2).
  • If src2 = 2 then dst = [50x2, 50x2, 50x2] = [100, 100, 100]
  • If src2 = [2,3,4] then dst = [50x2, 50x3, 50x4] = [100, 150, 200]
  • If src2 = [2] then dst = [50x2, 50x0, 50x0] = [100, 0, 0]

From https://docs.opencv.org/4.9.0/d2/de8/group__core__array.html#ga979d898a58d7f61c53003e162e7ad89f

Note
Saturation is not applied when the output array has the depth CV_32S. You may even get result of an incorrect sign in the case of overflow.
(Python) Be careful to difference behaviour between src1/src2 are single number and they are tuple/array. multiply(src,X) means multiply(src,(X,X,X,X)). multiply(src,(X,)) means multiply(src,(X,0,0,0)).

And This behavious is same as C++

#include <iostream>
#include <opencv2/core.hpp>

int main(void)
{
  cv::Mat gray_img(400,400,CV_8UC3, cv::Scalar::all(50));
  cv::Mat tmp_img_int;
  cv::multiply(gray_img, 2, tmp_img_int);
  std::cout << "INT = " << cv::format(tmp_img_int(cv::Rect(0,0,2,2)), cv::Formatter::FMT_NUMPY) << std::endl;

  cv::Mat tmp_img_scalar;
  cv::multiply(gray_img, cv::Scalar(2), tmp_img_scalar);
  std::cout << "SCALAR = " << cv::format(tmp_img_scalar(cv::Rect(0,0,2,2)), cv::Formatter::FMT_NUMPY) << std::endl;
  return 0;
}

Result

$ make && ./a.out
g++ main.cpp -o a.out \
        -I/usr/local/include/opencv4 \
        -lopencv_core
INT = array([[[100, 100, 100], [100, 100, 100]],
       [[100, 100, 100], [100, 100, 100]]], dtype='uint8')
SCALAR = array([[[100,   0,   0], [100,   0,   0]],
       [[100,   0,   0], [100,   0,   0]]], dtype='uint8')

@Kumataro
Copy link
Contributor

@cocositu If OK, please could you close this issue ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants