差异使用“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?

查看:647
本文介绍了差异使用“const cv :: Mat&”,“cv :: Mat&”,“cv :: Mat”或“const cv :: Mat”作为函数参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我通过opencv矩阵( cv :: Mat )作为函数的参数,我们传递一个智能指针。我们对函数中的输入矩阵所做的任何更改也会更改函数范围外的矩阵。



我通过将矩阵作为const引用读取,在函数中没有改变。但是一个简单的例子表明它是:

  void sillyFunc(const cv :: Mat& Input,cv :: Mat& {
Output = Input;
输出+ = 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& 发送,也会改变。



I2 中的一个简单副本是 I1 的智能指针,因此 I2 会改变 I1



发现 cv :: Mat const cv :: Mat const cv :: Mat& cv :: Mat& 作为函数的参数。


$ b b

我知道如何覆盖这个(用 Output = Input.clone()替换 Output = Input; >

>解决方案

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


OpenCV会自动处理所有内存。



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


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

  void sillyFunc(const cv :: Mat& Input,cv :: Mat&输出){
Output = Input.clone(); //输入,输出现在有独立的内存
输出+ = 1;
}

PS cv :: Mat 包含指向引用计数器的 int * refcount 。有关详细信息,请参阅内存管理和引用计数: / p>

数据类型等)和指向数据的指针。所以没有什么能阻止我们拥有对应于相同数据的 Mat 的几个实例。 Mat 保存引用计数,该引用计数会告知当 Mat 的特定实例被销毁时是否必须释放数据。







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




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

      void sillyFunc(cv :: Mat Input,cv :: Mat& Output){
    Input = cv :: Mat :: ones(4,4,CV_32F); // OK,但只在函数内改变
    // ...
    }


  2. const cv :: Mat输入:传递输入的标题副本。它的头部不会在函数之外或之内改变。例如:

      void sillyFunc(const cv :: Mat Input,cv :: Mat& Output){
    Input = cv :: Mat :: ones(4,4,CV_32F); //错误,即使在函数内更改
    // ...
    }


  3. const cv :: Mat&输入:传递输入的引用。保证输入的头不会在函数之外或之内改变。例如:

      void sillyFunc(const cv :: Mat& Input,cv :: Mat& Output){
    Input = cv :: Mat :: ones(4,4,CV_32F); //尝试更改标题时出错
    ...
    }


  4. cv :: Mat&输入:传递输入的引用。对输入的更改发生在函数的外部和内部。例如:

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


PS2 :我必须指出,在所有四种情况下( cv :: Mat const cv :: Mat const cv :: Mat& cv :: Mat& ),只有Mat的头的访问被限制,而不是它指向的数据。例如,您可以在所有四种情况下更改其数据,其数据确实会在函数之外和之内变化:

  / ***将在所有四种情况下工作*** / 
// 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; //其数据将在此处更改
}


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!

解决方案

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
}

这篇关于差异使用“const cv :: Mat&amp;”,“cv :: Mat&amp;”,“cv :: Mat”或“const cv :: Mat”作为函数参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆