1
votes

I'm writing an OpenCV application in C++ Visual Studio and am having issues with a custom class that contains a Mat as its member. I've simplified the problematic code down to the example below, which still exhibits the issue.

The problematic Mat is defined as a private member and is initialized with the proper values in the class constructor. A test with std::cout shows that the values are indeed correct. When I then try to use this Mat in a member function its values are scrambled nonsense. Of course, I would need them to be the same as were defined in the constructor.

I'm assuming this is something to do with how the Mat class works, as this doesn't happen with regular types such as int, float,... I've googled around but can't find any indication of why this might happen. Clearly, something is wrong about how I'm saving the Mat in the constructor but I can't find a useful alternative. The values are shown by std::cout look random, but the *.at shows all the values to be the same (or similar) which seems more like some sort of overflow. Either way, it's wrong.

Thanks for your time!

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;

class test {
    Mat stayStill;
    int number;
public:
    test(double value, int someNumber) {
        number = someNumber;
        double data[] = {
                0,  value,      0,
            value,      0,  value,
                0,  value,      0
        };
        stayStill = Mat(3, 3, CV_64FC1, data);
        cout << endl << "number: " << number;
        cout << endl << "stayStill as defined on init" << endl << stayStill;
    }
    void show() {
        cout << endl << "stayStill as experienced in member function" << endl << stayStill << endl;    
        cout << endl << "and once more just to be sure:" << endl;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                cout << stayStill.at<double>(i, j) << " ";
            }
            cout << endl;
        }
        cout << endl << "size: " << stayStill.size();
        cout << endl << "type: " << stayStill.type();
        cout << endl << "number: " << number << endl;
    }
};

int main() {
    test a(3, 5);
    a.show();
    return 0;
}

And the output:

number: 5
stayStill as defined on init
[0, 3, 0;
 3, 0, 3;
 0, 3, 0]

stayStill as experienced in member function
[-9.255963134931783e+61, 1.253977097799596e-311, 1.253977098748202e-311;
 -9.255963134931783e+61, -9.255963134931783e+61, -9.255963134931783e+61;
 -9.255963134931783e+61, -nan, -9.255963134931783e+61]

and once more just to be sure if it's wrong:
-9.25596e+61 -9.25596e+61 -9.25596e+61
-9.25596e+61 -9.25596e+61 -9.25596e+61
-9.25596e+61 -9.25596e+61 -9.25596e+61

size: [3 x 3]
type: 6
number: 5
2

2 Answers

0
votes

Matrices in OpenCV works using reference counter technique. When copy operations are called, they copies only pointer to source matrix data, not the whole underlying data.

stayStill = Mat(3, 3, CV_64FC1, data);
            ^^^

in the line above temporary Mat is created - it holds data, then staySill takes pointer to this temporary, and finally this pointer is dangling because at the end of full expression temporary Mat is destroyed.

You can introduce new named Mat instance:

Mat m(3, 3, CV_64FC1, data);

and make deep copies by copyTo:

m.copyTo(stayStill);
1
votes

The assignment operator of Mat performs a shallow copy; so your member variable stayStill will contain references to a temporary object (which will have gone out of scope when you call show later). This yields undefined behaviour.

You can make a deep copy with Mat::copyTo(). e.g.

Mat temp(3, 3, CV_64FC1, data);
temp.copyTo(stayStill);