opencv: Problem for Simple Alpha Blending

System information (version)
  • OpenCV => 4.2.0
  • Operating System / Platform => Windows 64 Bit
  • Compiler => Visual Studio 2019
Detailed description

If running following the code for alpha blending, it output following results.

#include <opencv2/opencv.hpp>

int main( int argc, char* argv[] )
{
    // Read Image
    cv::Mat src = cv::imread( "lena.jpg" );

    // Alpha Blend with Blue, Green, and Red
    double alpha = 0.6;
    double beta  = 1.0 - alpha;
    cv::Mat blend_b = alpha * src + beta * cv::Scalar( 255, 0, 0 ); // Blue (issue case!)
    cv::Mat blend_g = alpha * src + beta * cv::Scalar( 0, 255, 0 ); // Green
    cv::Mat blend_r = alpha * src + beta * cv::Scalar( 0, 0, 255 ); // Red

    // Show Image
    cv::imshow( "src"  , src     );
    cv::imshow( "blue" , blend_b );
    cv::imshow( "green", blend_g );
    cv::imshow( "red"  , blend_r );
    cv::waitKey( 0 );

    return 0;
}

The output of alpha blending with green and red is correct result.
These turned greenish color and reddish color.
But, the output of alpha blending with blue has become whitish color.
It is not expected output. It should turn to bluish color.

output1

If changes blend color to the close to blue such as cv::Scalar(255, 1, 0) (just add one to green), it turn correctly to bluish color.

cv::Mat blend_b = alpha * src + beta * cv::Scalar( 255, 1, 0 ); // Close to Blue

output2

Related issues

This seems to be related to this issue #14738.
I think this is due to the difference between calculation results of cv::Mat and cv::MatExpr.
Therefore, I know that this issue can be avoided by changing to following code.

- cv::Mat blend_b = alpha * src + beta * cv::Scalar( 255, 0, 0 ); // Blue
+ cv::Mat blend_b = cv::Mat( alpha * src ) + beta * cv::Scalar( 255, 0, 0 ); // Blue

But, I think this is a bug of corner case. It should be fixed. What do you think? Thanks,

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 18 (11 by maintainers)

Most upvoted comments

Fix may look like this:

-else if( e.s.isReal() && (dst.data != m.data || fabs(e.alpha) != 1))
+else if (e.a.channels() == 1 && e.s.isReal() && (dst.data != m.data || fabs(e.alpha) != 1))

Need to add corresponding simple test and prepare a PR.


BTW, Exploring of similar problem shows that there is similar approach few lines above.

Correction to what I said, and consider smaller example:

	Mat img = Mat::ones(Size(2, 2), CV_8UC3);
	double alpha = 0.5;
	double beta = 1.0 - alpha;

	Mat blend_b = alpha * img + beta * Scalar(255, 0, 0); // @suppress("Invalid arguments")
	Mat blend_g = alpha * img + beta * Scalar(0, 255, 0); // @suppress("Invalid arguments")
	Mat blend_r = alpha * img + beta * Scalar(0, 0, 255);

Input img is:

[  1,   0,   0,   1,   0,   0;
   1,   0,   0,   1,   0,   0]

Bug in MatExpr causes beta * Scalar(255, 0, 0) to be pass the test for isReal() matrix_expressions.cpp As a result, it ends up adding beta * Scalar(255, 0, 0) to all channels of img. So blend_b:

[128, 128, 128, 128, 128, 128;
 128, 128, 128, 128, 128, 128]

blend_g is correctly handled because it is handled in the correct optimization block in that conditional tree (matrix_expressions.cpp):

[  0, 128,   0,   0, 128,   0;
   0, 128,   0,   0, 128,   0]

Similarly blend_r is also correctly handled:

[  0,   0, 128,   0,   0, 128;
   0,   0, 128,   0,   0, 128]

As you can see, because blend_b gets information added to all channels more or less equally, the output image is a faded version of the input that doesn’t look bluish.

Another workaround for RGB image (add 4th non-zero element to Scalar):

-cv::Mat blend_b = alpha * src + beta * cv::Scalar( 255, 0, 0 );
+cv::Mat blend_b = alpha * src + beta * cv::Scalar( 255, 0, 0, 255 );