Changing Mat dimensions inplace
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 :)
first, there are no real ND arrays:
then, what you do is for sure unsafe and dangerous.
if you construct a Mat like this:
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:
I know that
MatND
is just a typedef forMat
, butMat
itself can be multi-dimensions and/or multi-channels. For example:... is a 5-dimensional array with 7-channels.
@berak:
what is the correct way to reshape the ND-array without making a second copy of the data?
sorry, but i got no idea.
@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.
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.
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.