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.
Differences between sending cv::Mat
, const cv::Mat
, const cv::Mat&
or cv::Mat&
as arguments to a function:
-
cv::Mat Input
: pass a copy ofInput
‘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 ofInput
‘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 ofInput
‘s header. Guarantees thatInput
‘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 ofInput
‘s header. Changes toInput
‘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
}