Georgi Angelov
Georgi Angelov

Reputation: 4388

std::vector does not create new reference of cv::Mat for more than one vector entries - data gets overriden when initializing the matrices

So here is something that I came upon that really bothered me and made me try and figure out why does it work this way:

If have the following super simplistic, error prone code just to show an example:

std::vector<cv::Mat> newData(3,cv::Mat(height, width, cv::DataType<T>::type));

int counter = 0;
for(int b=0; b<3; b++){
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            newData[b].at<int>(i,j) = counter++;
            std::cout << newData[b].at<T>(i,j) << std::endl;
        }       
    }
}

for(int b=0; b<3; b++){
    std::cout << newData[b] << std::endl;
}

Printout:

[18, 19, 20;
  21, 22, 23;
  24, 25, 26]
[18, 19, 20;
  21, 22, 23;
  24, 25, 26]
[18, 19, 20;
  21, 22, 23;
  24, 25, 26]

Why is the same reference being used for the different vector entries? I am forced to create the different matrices separately as opposed to creating them along with the vector itself.

Is there a way to avoid this?

Thanks

Upvotes: 4

Views: 1410

Answers (1)

juanchopanza
juanchopanza

Reputation: 227370

The issue is that cv::Mat has reference semantics, so copying cv::Mat objects results in the copies sharing the data with the original. Thus initializing a vector in this way

std::vector<cv::Mat> newData(N, a_cv_mat);

will result in the vector containing N cv::Mats all sharing the same data as a_cv_mat.

To avoid having cv::Mat objects that refer to the same data, you can initialize the vector using a brace enclosed initializaiton list:

std::vector<cv::Mat> newData{cv::Mat(height, width, cv::DataType<T>::type),
                             cv::Mat(height, width, cv::DataType<T>::type),
                             cv::Mat(height, width, cv::DataType<T>::type)};

If you do not know the number of elements at compile time, you can emplace them into the vector one by one:

std::vector<cv::Mat> newData
newData.emplace_back(height, width, cv::DataType<T>::type);

If you don't have C++11 support, you can push each matrix into the vector:

std::vector<cv::Mat> newData
newData.push_back(cv::Mat(height, width, cv::DataType<T>::type));

Upvotes: 5

Related Questions