Ask Your Question
2

Changing Mat dimensions inplace

asked 2015-06-27 19:53:24 -0500

Amro_ gravatar image

updated 2015-09-30 12:24:37 -0500

As part of a larger project, I have some code that does something similar to the following:

cv::Mat some_function()
{
    // get some ND-array
    Mat mat1 = get_some_mat();
    vector<int> dims(mat1.size.p, mat1.size.p + mat1.dims);

    // ... shuffle the dimensions stored in "dims" (the total() remains the same)
    std::swap(dims[0], dims[1]);    // only an example...

    // create new mat header for the same data (no copying)
    Mat mat2(dims.size(), dims.data(), mat1.type(), mat1.data);

    // return matrix with the shuffled dimensions
    mat1 = mat2;    // <-- is this part safe??
    return mat1;    //
}

Is the last assignment safe, or will it mess up the ref count and leak memory?

I know the safer option is to just clone the data as in:

    // return matrix with the shuffled dimensions
    return mat2.clone();

but I'm trying to avoid making unnecessary copies of data...

I'm open to suggestion if there are better ways to reshape the ND-array in-place ...

You can assume the following is true:

CV_Assert(mat1.isContinuous() && mat1.dims > 3 && mat1.channels() == 1);

N.B: I am working with OpenCV 3.0.0


EDIT:

Another thing I tried is to directly overwrite the Mat size and step with those from the shuffled array:

    // return matrix with the shuffled dimensions
    std::copy(mat2.size.p, mat2.size.p + mat2.dims, mat1.size.p);
    std::copy(mat2.step.p, mat2.step.p + mat2.dims, mat1.step.p);
    return mat1;

But it's not quite right.. Apparently something in the mat1.flags field needs to be updated as well! Any help?


EDIT2:

I found an overload of the Mat::reshape method for ND-arrays, unfortunately it's not yet fully implemented and currently throws a CV_StsNotImplemented error!

    // return matrix with the shuffled dimensions
    return mat1.reshape(mat2.channels(), mat2.dims, mat2.size.p);

See the source code (either 3.0 or the master branch)...


Details

Let me explain a bit more about my use case. In my actual code, I'm trying to convert the last dimension of any 1-channel ND-array into channels of a ND-array with one less dimension but multi-channels, without copying the data. (Again we can assume that the array is continuous and the destination number of channels is less than CV_CN_MAX).

As an example, If I had the following array:

cv::Mat get_some_mat()
{
    int dims[] = {2,3,4,5};   // I'm really working with arbitrary number of dimensions
    Mat matnd(4, dims, CV_64F, Scalar::all(0));
    return matnd;
}

I want to convert it from 2x3x4x5 array with 1-channel into a 2x3x4 array with 5-channels, i.e:

int dims[] = {2,3,4};
Mat mat(3, dims, CV_64FC(5), Scalar::all(0));

And I want to avoid unneeded copies... The above Mat::reshape method would have been great, but it's not implemented at the moment!

The regular Mat::reshape leaves an undesired singleton dimension at the end:

matnd.reshape(5, 0);    // returns 2x3x4x1 with 5-channel (note the "1" at the end)

Thanks and sorry for the long post :)

edit retag flag offensive close merge delete

Comments

1

first, there are no real ND arrays:

typedef Mat MatND;

then, what you do is for sure unsafe and dangerous.

if you construct a Mat like this:

Mat m(H,W,T, data_pointer);

it will get constructed with an empty refcount (or allocator in 3.0). so, when you return from your function, data_pointer will go out of scope, and you got a 'dangling pointer' .(the mat1 = mat2 part does not change anything, you only got 2 Mat's with this problem now.)

berak gravatar imageberak ( 2015-06-28 02:07:29 -0500 )edit

@berak:

first, there are no real ND arrays: typedef Mat MatND;

I know that MatND is just a typedef for Mat, but Mat itself can be multi-dimensions and/or multi-channels. For example:

int dims[] = {2,3,4,5,6};
cv::Mat mat1(5, dims, CV_64FC(7), Scalar::all(0));

... is a 5-dimensional array with 7-channels.

@berak:

then, what you do is for sure unsafe and dangerous.

what is the correct way to reshape the ND-array without making a second copy of the data?

Amro_ gravatar imageAmro_ ( 2015-06-28 05:27:22 -0500 )edit

sorry, but i got no idea.

berak gravatar imageberak ( 2015-06-28 06:31:20 -0500 )edit

@berak: no problem, thank you for the help :) I noticed you are a regular on the site, do you know any devs on the OpenCV team we can ping on this thread? I'm sure they'll easily have an answer.

Amro_ gravatar imageAmro_ ( 2015-06-28 07:38:14 -0500 )edit

the devs rarely come her, unfortunately. (they had their share of it years ago, when this all started, i guess..)

but imho, you should (at least try to) implement the missing parts of that reshape() overload (didn't see it, sorry), and try to make a pull request, so others won't have that problem.

berak gravatar imageberak ( 2015-06-28 09:33:17 -0500 )edit

If the data_pointer is a pointer to data which lies on the heap (i.e. no local vector) than you can make any matrix header on top of it you like. However, you are yourself responsible to delete the data_pointer when you don't need the data anymore. So, imho just take care on the scopes and you should be fine.

Guanta gravatar imageGuanta ( 2015-06-28 13:40:44 -0500 )edit

1 answer

Sort by ยป oldest newest most voted
1

answered 2016-01-25 16:39:53 -0500

Amro_ gravatar image

Solved; In OpenCV 3.1.0, the Mat::reshape was extended to handle ND-arrays:

edit flag offensive delete link more

Question Tools

1 follower

Stats

Asked: 2015-06-27 19:53:24 -0500

Seen: 4,340 times

Last updated: Jan 25 '16