我已经彻底地搜索过了,还没有找到一个简单的答案。
将opencv矩阵(cv::Mat)作为参数传递给函数,我们传递的是一个智能指针。我们对函数内部的输入矩阵所做的任何更改都会改变函数作用域之外的矩阵。
我读到,通过传递一个矩阵作为常量引用,它在函数中不会被改变。但一个简单的例子说明了这一点:
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";
}显然,即使作为const cv::Mat&发送,A也会被更改。
这并不让我感到惊讶,因为在函数I2中是I1智能指针的简单副本,因此I2中的任何更改都会改变I1。
让我困惑的是,我不明白将cv::Mat、const cv::Mat、const cv::Mat&或cv::Mat&作为参数发送给函数有什么实际区别。
我知道如何覆盖这一点(用Output = Input.clone();替换Output = Input;可以解决这个问题),但仍然不理解上面提到的区别。
谢谢你们!
发布于 2014-05-06 13:00:56
这都是因为OpenCV使用了。
OpenCV会自动处理所有内存。
首先,函数和方法使用的std::vector、Mat和其他数据结构都有析构函数,可以在需要时释放底层内存缓冲区。这意味着析构函数并不总是像在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。有关更多详细信息,请查看Memory management and reference counting:
Mat是一种保持矩阵/图像特征(行数和列数、数据类型等)和指向数据的指针的结构。因此,没有什么能阻止我们拥有与相同数据相对应的多个Mat实例。Mat保留一个引用计数,该计数指示当Mat的特定实例被销毁时,是否必须释放数据。
将cv::Mat、const cv::Mat、const cv::Mat&或cv::Mat&作为参数发送到函数之间的区别:
cv::Mat Input:传递Input头部的副本。它的头部不会在此函数外部更改,但可以在函数内更改。例如:}
const cv::Mat Input:sillyFunc(cv::Mat Input,cv::Mat& Output){ Input = cv::Mat::ones(4,4,CV_32F);// OK,但只在函数内部更改//... void传入一份Input的header,其header不会在函数外部或内部更改,例如:}
const cv::Mat& Input:sillyFunc(const cv::Mat Input,cv::Mat& Output){ Input = cv::Mat::ones(4,4,CV_32F);//错误,即使在函数内更改//... void传递Input标头的引用。保证Input的标头不会在函数外部或内部更改。例如:}
cv::Mat& Input:(const cv::Mat& sillyFunc,cv::Mat& Output){ Input = cv::Mat::ones(4,4,CV_32F);//尝试更改header时出错... void传入Input的header引用。对Input header的更改发生在函数外部和内部。例如:void sillyFunc(cv::Mat& Input,cv::Mat& Output){ Input = cv::Mat::ones(4,4,CV_32F);//完全正常并且确实发生了变化... }
P.S.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
}发布于 2014-05-06 10:28:02
当您将智能指针作为引用传递时,理论上可以节省一些处理时间,因为智能指针的复制构造函数不会被调用。
https://stackoverflow.com/questions/23468537
复制相似问题