使用"const cv::Mat &"、"cv::Mat &"、"cv::Mat"或"const cv::Mat"作为函数参数有什么区别?

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

本文关键字:cv Mat const 什么 区别 参数 使用 函数      更新时间:2023-10-16

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

传递opencv矩阵(cv::Mat)作为函数的参数,我们传递了一个智能指针。我们对函数内部的输入矩阵所做的任何改变也会改变函数作用域外的矩阵。

我读到,通过传递矩阵作为const引用,它不会在函数内改变。但是有一个简单的例子可以说明:

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<<"nn";
    cv::Mat B;
    sillyFunc(A,B);
    std::cout<<"A = n"<<A<<"nn";
    std::cout<<"B = n"<<B<<"nn";
}

显然,即使作为const cv::Mat&发送,A也被改变了。

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

让我困惑的是,我不明白发送cv::Mat, const cv::Mat, const cv::Mat&cv::Mat&作为函数的参数之间存在什么实际差异。

我知道如何覆盖这个(用Output = Input.clone();替换Output = Input;将解决问题),但仍然不理解上面提到的差异。

谢谢你们了!

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

OpenCV自动处理所有内存

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

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

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。查看内存管理和引用计数了解更多细节:

Mat是一个保持矩阵/图像特征(行和列数,数据类型等)和指向数据的指针的结构。因此,没有什么可以阻止我们有几个Mat实例对应于相同的数据。Mat保持一个引用计数,该计数告诉当Mat的特定实例被销毁时,数据是否必须被释放。


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

  1. cv::Mat Input:传递Input报头的副本。它的头文件不能在函数外部修改,但可以在函数内部修改。例如:

    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:传递Input头文件的副本。它的头文件不会在函数外部或函数内部被修改。例如:

    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:传递Input报头的引用。保证Input的头不会在函数外部或函数内部被更改。例如:

    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:传递Input报头的引用。对Input头的更改发生在函数外部和内部。例如:

    void sillyFunc(cv::Mat& Input, cv::Mat& Output){
        Input = cv::Mat::ones(4, 4, CV_32F); // totally OK and does change
        ...
    }
    

注:2:我必须指出,在所有四种情况下(cv::Mat, const cv::Mat, const cv::Mat&cv::Mat&),只有对Mat头的访问受到限制,而不是对它指向的数据。例如,您可以在所有四种情况下更改它的数据,并且它的数据确实会在函数外部和内部更改:

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