opencv / opencv

Open Source Computer Vision Library
https://opencv.org
Apache License 2.0
75.95k stars 55.62k forks source link

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

Open cocositu opened 3 weeks ago

cocositu commented 3 weeks ago

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.
    1. 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

Kumataro commented 3 weeks ago

~Maybe duplicated https://github.com/opencv/opencv/issues/25165~

Kumataro commented 3 weeks ago

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 ?

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 commented 2 weeks ago

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