使用“const cv::Mat"、“cv::Mat"、“cv::Mat"的区别或“const cv::Mat"作为函数参数?

2021-12-10 00:00:00 opencv function matrix parameters c++

我已经彻底搜索过,但没有找到一个直接的答案.

I've searched thoroughly and have not found a straightforward answer to this.

将 opencv 矩阵 (cv::Mat) 作为参数传递给函数,我们传递的是智能指针.我们对函数内的输入矩阵所做的任何更改也会改变函数作用域外的矩阵.

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

";
}

很明显,即使 A 是作为 const cv::Mat& 发送的,它也被改变了.

Clearly, A is altered even though it is sent as a const cv::Mat&.

这并不令我感到惊讶,因为在函数 I2 中是 I1 智能指针的简单副本,因此 I2 中的任何更改将改变I1.

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.

令我困惑的是,我不明白发送 cv::Matconst cv::Matconst cv 之间存在什么实际区别::Mat&cv::Mat& 作为函数的参数.

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.

我知道如何覆盖它(将 Output = Input; 替换为 Output = Input.clone(); 可以解决问题)但仍然不明白上面提到的区别.

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.

谢谢各位!

推荐答案

这都是因为 OpenCV 使用了 自动内存管理.

It's all because OpenCV uses Automatic Memory Management.

OpenCV 自动处理所有内存.

OpenCV handles all the memory automatically.

首先,std::vectorMat 和其他函数和方法使用的数据结构都有析构函数,可以在需要时释放底层内存缓冲区.这意味着析构函数并不总是像 Mat 那样释放缓冲区.他们考虑了可能的数据共享.析构函数递减与矩阵数据缓冲区关联的参考计数器.当且仅当引用计数器达到零时,即当没有其他结构引用同一缓冲区时,才会释放缓冲区.同样,当复制一个 Mat 实例时,并没有真正复制实际数据.相反,引用计数器递增以记住存在相同数据的另一个所有者.还有 Mat::clone 方法可以创建矩阵数据的完整副本.

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.

也就是说,为了让两个cv::Mat指向不同的东西,你需要为它们分别分配内存.例如,以下内容将按预期工作:

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 包含一个 int* refcount 指向引用计数器.查看内存管理和引用计数了解更多详情:

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 是一种保持矩阵/图像特征(行和列数、数据类型等)和指向数据的指针的结构.因此,没有什么可以阻止我们将多个 Mat 实例对应于相同的数据.Mat 保持一个引用计数,当一个特定的 Mat 实例被销毁时,它告诉数据是否必须被释放.

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.

<小时>

发送cv::Matconst cv::Matconst cv::Mat&cv的区别::Mat& 作为函数的参数:


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

  1. cv::Mat Input:传递Input 标题的副本.它的标头不会在此函数之外更改,但可以在函数内更改.例如:

  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
    //...
}

  • const cv::Mat Input:传递Input 标题的副本.它的标题不会在函数外部或内部更改.例如:

  • 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:传递一个 Input 的头部引用.保证 Input 的标头不会在函数外部或内部更改.例如:

  • 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:传递一个 Input 的头部引用.Input 的标头发生在函数内部和外部.例如:

  • 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
        ...
    }
    

  • PS2:我必须指出,在所有四种情况下(cv::Matconst cv::Matconst cv::Mat&cv::Mat&),只限制访问 Mat 的头部,而不限制访问它指向的数据.例如,您可以在所有四种情况下更改其数据,并且其数据确实会在函数外部和内部发生变化:

    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
    }
    

    相关文章