I've searched thoroughly and have not found a straightforward answer to this.
Passing opencv matrices (cv::Mat
) as arguments to a function, we're passing a smart pointer. Any change we do to the input matrix inside the function alters the matrix outside the function scope as well.
I read that by passing a matrix as a const reference, it is not altered within the function. But a simple example shows it does:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input;
Output += 1;
}
int main( int argc, char** argv ){
cv::Mat A = cv::Mat::ones(3,3,CV_8U);
std::cout<<"A = \n"<<A<<"\n\n";
cv::Mat B;
sillyFunc(A,B);
std::cout<<"A = \n"<<A<<"\n\n";
std::cout<<"B = \n"<<B<<"\n\n";
}
Clearly, A
is altered even though it is sent as a const cv::Mat&
.
This does not surprise me as within the function I2
is a simple copy of I1
's smart pointer and thus any change in I2
will alter I1
.
What does baffle me is that I don't understand what practical difference exists between sending cv::Mat
, const cv::Mat
, const cv::Mat&
or cv::Mat&
as arguments to a function.
I know how to override this (replacing Output = Input;
with Output = Input.clone();
would solve the problem) but still don't understand the above mentioned difference.
Thanks guys!
The Mat class of OpenCV library is used to store the values of an image. It represents an n-dimensional array and is used to store image data of grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms, etc.
Mat is basically a class with two data parts : the matrix header (containing information such as the size of the matrix, the method used for storing, at which address is the matrix stored, and so on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on the method chosen for ...
It's all because OpenCV uses Automatic Memory Management.
OpenCV handles all the memory automatically.
First of all,
std::vector
,Mat
, and other data structures used by the functions and methods have destructors that deallocate the underlying memory buffers when needed. This means that the destructors do not always deallocate the buffers as in case ofMat
. They take into account possible data sharing. A destructor decrements the reference counter associated with the matrix data buffer. The buffer is deallocated if and only if the reference counter reaches zero, that is, when no other structures refer to the same buffer. Similarly, when aMat
instance is copied, no actual data is really copied. Instead, the reference counter is incremented to memorize that there is another owner of the same data. There is also theMat::clone
method that creates a full copy of the matrix data.
That said, in order to make two cv::Mat
s point to different things, you need to allocate memory separately for them. For example, the following will work as expected:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Output = Input.clone(); // Input, Output now have seperate memory
Output += 1;
}
P.S: cv::Mat
contains an int* refcount
that points to the reference counter. Check out Memory management and reference counting for more details:
Mat
is a structure that keeps matrix/image characteristics (rows and columns number, data type etc) and a pointer to data. So nothing prevents us from having several instances ofMat
corresponding to the same data. AMat
keeps a reference count that tells if data has to be deallocated when a particular instance ofMat
is destroyed.
cv::Mat
, const cv::Mat
, const cv::Mat&
or cv::Mat&
as arguments to a function:cv::Mat Input
: pass a copy of Input
's header. Its header will not be changed outside of this function, but can be changed within the function. For example:
void sillyFunc(cv::Mat Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // OK, but only changed within the function
//...
}
const cv::Mat Input
: pass a copy of Input
's header. Its header will not be changed outside of or within the function. For example:
void sillyFunc(const cv::Mat Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // Error, even when changing within the function
//...
}
const cv::Mat& Input
: pass a reference of Input
's header. Guarantees that Input
's header will not be changed outside of or within the function. For example:
void sillyFunc(const cv::Mat& Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // Error when trying to change the header
...
}
cv::Mat& Input
: pass a reference of Input
's header. Changes to Input
's header happen outside of and within the function. For example:
void sillyFunc(cv::Mat& Input, cv::Mat& Output){
Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change
...
}
P.S.2: I must point out that, in all the four situations (cv::Mat
, const cv::Mat
, const cv::Mat&
or cv::Mat&
), only the access to the Mat's header is restrained, not to the data it points to. For example, you can change its data in all the four situations and its data will indeed change outside of and within the function:
/*** will work for all the four situations ***/
//void sillyFunc(cv::Mat Input){
//void sillyFunc(const cv::Mat Input){
//void sillyFunc(const cv::Mat &Input){
void sillyFunc(cv::Mat &Input){
Input.data[0] = 5; // its data will be changed here
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With