Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
295 views
in Technique[技术] by (71.8m points)

c++ - Differences of using "const cv::Mat &", "cv::Mat &", "cv::Mat" or "const cv::Mat" as function parameters?

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 = 
"<<A<<"

";
    cv::Mat B;
    sillyFunc(A,B);
    std::cout<<"A = 
"<<A<<"

";
    std::cout<<"B = 
"<<B<<"

";
}

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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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 of Mat. 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 a Mat 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 the Mat::clone method that creates a full copy of the matrix data.

That said, in order to make two cv::Mats 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 of Mat corresponding to the same data. A Mat keeps a reference count that tells if data has to be deallocated when a particular instance of Mat is destroyed.


Differences between sending cv::Mat, const cv::Mat, const cv::Mat& or cv::Mat& as arguments to a function:

  1. 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
        //...
    }
    
  2. 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
        //...
    }
    
  3. 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
        ...
    }
    
  4. 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
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...