cv::Mat assignment or copy with guarantee that there is no reallocation

asked 2018-01-29 04:48:49 -0500

Adam C gravatar image

updated 2018-01-29 06:25:51 -0500

I have code in the form of

cv::Mat function(const cv::Mat&);

cv::Mat a = ..;
cv::Mat b = ..;
cv::Rect roi = ..;
function(b, roi).copyTo(a(roi)); // here

on the line marked with "here" the matrix "a" will be reallocated if dimensions or data type do not match. In my case I only want to change a subregion, so reallocation would be a programming error. As far as I see, a temporary matrix would be created and a would not change at all.

another example:

cv::Mat matrixHeader = someOtherMat(roi)
matrixHeader = a | b;   // someOtherMat won't be changed if there is a type mismatch, which is unexpected and without warning

is there a copy method in opencv that throws if the matrices are not compatible?

edit retag flag offensive close merge delete

Comments

use clone method.

what do you mean by "my case that would be a programming error"?

LBerger gravatar imageLBerger ( 2018-01-29 05:43:08 -0500 )edit

clone is reallocation and not copy, so it doesn't apply.

I actually had a bug in the example code. I fixed and edited to make my intent more clear.

Adam C gravatar imageAdam C ( 2018-01-29 06:20:06 -0500 )edit

An example can be reproduced. i cannot with your example. I understand your problem like this :

    Mat a(6, 6,CV_8UC1,Scalar(1));
    Mat b(7, 7, CV_32FC1,Scalar(3.5));
    cout << a << "\n";
    cout << b<< "\n";
    Rect r1(1, 1, 4, 4);
    Rect r2(1, 1, 4, 4);
    try {
        cout << "before copy "<<int64(a.data) << "\n";
    b(r1).copyTo(a(r2));
    cout << "after copy " << int64(a.data) << "\n";
    cout << a << "\n";
    cout << b << "\n";

    }
    catch (cv::Exception &e)
    {
        cout << "sizes are not equal\n";
    }

No exception. Of course if b and rect size are different there is an exception

LBerger gravatar imageLBerger ( 2018-01-29 06:55:14 -0500 )edit

Indeed.

Is the documentation wrong? It says (cv::Mat::copyTo):

Before copying the data, the method invokes :
m.create(this->size(), this->type()); 
so that the destination matrix is reallocated if needed.

and (cv::Mat::create):

1. If the current array shape and the type match the new ones, return immediately. Otherwise, de-reference the previous data by calling Mat::release.
2. Initialize the new header.
3. Allocate the new data of total()*elemSize() bytes.

or am I misunderstanding something?

Adam C gravatar imageAdam C ( 2018-01-29 07:20:21 -0500 )edit

From what i see in the source code, (copy.cpp function Mat::copyTo and mat.inl.hpp function _OutputArray::_OutputArray), it indeed does not behave as documented.

Output array is initialised with flags called FIXED_TYPE + FIXED_SIZE. copyTo checks for fixedType, but not for FIXED_SIZE. The code path using the create function is still there, but it is not taken. instead there is said exception (plus "OpenCV Error: Assertion failed" warnings in 3.2, depending on what mismatches, but not always).

Adam C gravatar imageAdam C ( 2018-01-29 08:08:09 -0500 )edit

you can test the code below ( see memory usage )

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

using namespace cv;

Mat getROI(const Mat image, Rect rect)
{
    return image(rect);
}

int main()
{
    Mat image(20000, 20000, CV_8UC3);
    Mat roi = getROI(image, Rect(0, 0, 19000, 19000));
    imshow("image", image);
    imshow("roi", roi);
    waitKey();
}
sturkmen gravatar imagesturkmen ( 2018-01-29 08:16:04 -0500 )edit

there is a reallocation when it is mat object and not ROI. In a roi there is no real data opencv plays with rows and column and stride.

In b.copyTo(a) a is reallocated.

LBerger gravatar imageLBerger ( 2018-01-29 11:59:12 -0500 )edit

I don't think so... If it were reallocated, you wouldn't see it change the original. But the copyTo still affects the original image. So b.copyTo(a) is still using the original memory.

Tetragramm gravatar imageTetragramm ( 2018-01-29 22:33:26 -0500 )edit

a is not using original if a is smaller than b :

    Mat a(6, 6,CV_8UC1,Scalar(1));
    Mat b(7, 7, CV_32FC1,Scalar(3.5));
    try {
        cout << "before copy "<<int64(a.data) << "\n";
    b.copyTo(a);
    cout << "after copy " << int64(a.data) << "\n";
LBerger gravatar imageLBerger ( 2018-01-30 08:41:57 -0500 )edit

Ah, I think I see the confusion. Yes, that will reallocate. No there is not a built-in copy function that will throw, but you can build a simple check that will.

For example:

if(a.rows != b.rows || a.cols != b.cols || a.type() != b.type())
    throw;
Tetragramm gravatar imageTetragramm ( 2018-01-30 17:44:08 -0500 )edit