OpenCV multiplying matrices (cv::Mat) of different types

Viewed 797

Error:

what(): OpenCV(4.2.0) ../modules/core/src/arithm.cpp:691: error: (-5:Bad argument) When the input arrays in add/subtract/multiply/divide functions have different types, the output array type must be explicitly specified in function 'arithm_op'

Code:

// cx is a cv::Mat containing a 1280x720x1 matrix of floats

// what i want is an output of 0.0 where cx <= 0.5 and (cx-0.5)*2 otherwise

auto c0 = (cx > 0.5).mul((cx - 0.5) * 2);

I tried:

auto c0 = (cx > 0.5).mul((cx - 0.5) * 2, CV_32F);

Which didn't work.

1 Answers

The main issue is that the type of (cx > 0.5) is CV_8U and not CV_32F.

Take a look at the following code sample:

//Initialize 3x3 matrix for example
cv::Mat cx = (cv::Mat_<float>(3, 3) << 5.0f, 0, 0, 0, 5.0f, 0, 0, 0, 5.0f);

//The type of "logical matrix" result of (cx > 0.5f) is UINT8 (255 where true).
cv::Mat cx_above05 = (cx > 0.5f);

I am using Image Watch and Visual Studio 2017 for convenience.

Here is the result of cv::Mat cx_above05 = (cx > 0.5f):

enter image description here

As you can see, the type is UINT8 and the value is 255 where cx>0.5 and 0 where not.


You can use convertTo for converting the type to CV_32F and dividing by 255:

cx_above05.convertTo(cx_above05 , CV_32F, 1.0/255.0);  //Convert type to float (1.0f where true).

Result:

enter image description here

As you can see, the type is FLOAT32, and 255 values goes to 1.0f


Complete code sample:

cv::Mat cx = (cv::Mat_<float>(3, 3) << 5.0f, 0, 0, 0, 5.0f, 0, 0, 0, 5.0f);  //Initialize 3x3 matrix for example      
cv::Mat cx_above05 = (cx > 0.5f); //The type of "logical matrix" result of (cx > 0.5f) is UINT8 (255 where true).
cx_above05.convertTo(cx_above05 , CV_32F, 1.0/255.0); //Convert type to float (1.0f where true).
cv::Mat c0 = cx_above05.mul((cx - 0.5f) * 2.0f);

Result:
enter image description here


Instead of multiplying by the mask we can use the trick described in OpenCV pow documentation:

cv::Mat cx = (cv::Mat_<float>(3, 3) << 5.0f, 0, 0, 0, 5.0f, 0, 0, 0, 5.0f);  //Initialize 3x3 matrix for example      
cv::Mat mask = (cx <= 0.5f); //The type of "logical matrix" result of (cx <= 0.5f) is UINT8 (255 where true).
cv::Mat c0 = (cx - 0.5f) * 2.0f;
cv::subtract(c0, c0, c0, mask);

I found a more elegant solution here (using setTo):

cv::Mat c0 = (cx - 0.5f) * 2.0f;
c0.setTo(0, cx <= 0.5f);

One line solution:

The type of the expression ((cx - 0.5f) * 2.0f) is cv::MatExpr, and we can cast it to cv::Mat:

cv::Mat c0 = ((cv::Mat)((cx - 0.5f) * 2.0f)).setTo(0, cx <= 0.5f);
Related