mrgloom
mrgloom

Reputation: 21622

Opencv Mat addition strange behaviour

Why this 2 lines of code are not equivalent? because of some saturation cast?

Type of Mat's is CV_8U, bgr is vector of 1 channel Mat's, and gray is one channel image.

vector<Mat> bgr;
Mat gray;

1

gray+= (bgr[0]-128)*2;

2

gray= (bgr[0]-128)*2+gray;

Here is a minimal code for reproducing:

void Test()
{
    Mat A= Mat::zeros(100,100,CV_8UC1);
    A(Rect(20,20,60,60)).setTo(128);
    Mat R= Mat::zeros(100,100,CV_8UC1);
    randu(R, Scalar::all(0), Scalar::all(255));

    //v1
    //A+= (R-128)*2;
    //v2
    A= A+(R-128)*2;

    imwrite("A.png", A);
}

Upvotes: 4

Views: 235

Answers (1)

Miki
Miki

Reputation: 41765

Short answer: YES, it's because of saturate_cast.


Basically, in your version 1 you are doing:

Mat1b gray; 
// ... gray somehow initialized
Mat1b temp = saturate_cast<uchar>(bgr * 2 - 256);
gray += temp;

while in your version 2 you're doing:

Mat1b gray; 
// ... gray somehow initialized
gray = saturate_cast<uchar>((2 * bgr + 1 * gray) - 256);

You see that saturate_cast operates in different places. To avoid this kind of problem, my recommendation is to use always version 2, where all computation is done on double values, and the cast happens only at the end.

The code should illustrate this:

    #include <opencv2\opencv.hpp>
    using namespace cv;

    int main()
    {
        Mat1b bgr(10, 10, uchar(100));
        Mat1b gray1(10, 10, uchar(100));
        Mat1b gray2(10, 10, uchar(100));
        Mat1b gray3(10, 10, uchar(100));
        Mat1b gray4(10, 10, uchar(100));

        // *****************************************
        // v1
        // *****************************************

        gray1 += (bgr - 128) * 2;

        // MatExpr e1(a = bgr, alpha = 1, beta = 0, s = -128) <- MatExpr operator - (bgr, 128);     // (bgr - 128)
        // MatExpr e2(a = bgr, alpha = 2, beta = 0, s = -256) <- MatExpr operator * (e1, 2);        // (bgr - 128) * 2 -> bgr * 2 - 256
        // Mat_<uchar>& operator += (bgr, e2);

        // temp = saturate_cast<uchar>(bgr * 2 - 256);
        // gray += temp;

        // *****************************************
        // v2
        // *****************************************

        gray2 = (bgr - 128) * 2 + gray2;

        // MatExpr e1(a = bgr, alpha = 1, beta = 0, s = -128) <- MatExpr operator - (rgb, 128);             // (bgr - 128)
        // MatExpr e2(a = bgr, alpha = 2, beta = 0, s = -256) <- MatExpr operator * (e1, 2);                // (bgr - 128) * 2 -> = bgr * 2 - 256
        // MatExpr e3(a = bgr, b = gray, alpha = 2, beta = 1, s = -256) <- MatExpr operator + (e2, gray);   // (bgr - 128) * 2 + gray -> (2 * bgr + 1 * gray)   - 256 

        // gray = saturate_cast<uchar>((2 * bgr + 1 * gray) - 256);


        // *****************************************
        // v3 equivalent to v1
        // *****************************************

        Mat1b temp3 = (bgr - 128) * 2;
        gray3 = gray3 + temp3;


        // *****************************************
        // v4 equivalent to v2
        // *****************************************

        Mat1d bgrd;
        bgr.convertTo(bgrd, CV_64F);
        Mat1d temp4 = (bgrd - 128) * 2;
        Mat1d gray4d;
        gray4.convertTo(gray4d, CV_64F);
        gray4d = gray4d + temp4;
        gray4d.convertTo(gray4, CV_8U);

        // gray1 == gray3
        // gray2 == gray4

        return 0;
    }

Upvotes: 4

Related Questions