opencv图像的拷贝分为深拷贝和浅拷贝
(一)图像浅拷贝
所谓浅拷贝拷贝的其实只是源数据的地址,拷贝后的数据和源数据共享的是一段内存,公用一样的数据。这也意味着源数据发生改变之后拷贝后的数据也跟着改变。
1.1拷贝构造函数
Mat(const Mat& m);
1.2运算符重载=
Mat& operator = (const Mat& m);
1.3使用示例
这里的使用示例比较简单,具体与深拷贝的不同在下面展示
Mat mat = ...
Mat mat1;
Mat mat2;
//使用拷贝构造
mat1(mat);
//运算符重载
mat2 = mat;
(二)图像深拷贝
将源数据完全拷贝到新的内存中,拷贝前后数据相互独立互不影响。
2.1 copyTo()函数
该函数可以将源图像复制到目标图像,并可以选择使用掩码mask
指定像素的复制区域。如果目标图像存在并且大小与源图像一样,则直接进行拷贝操作,如果大小不一样则先申请一块内存然后再进行拷贝。
(1)函数原型
void copyTo( OutputArray m, InputArray mask ) const;
(2)参数解释
m
:目标矩阵。如果在操作之前它的大小或类型不正确,则会重新分配内存。
mask
: 与当前矩阵大小相同的操作掩码。其非零元素指示需要复制的矩阵元素。掩码必须是 CV_8U 类型,并且可以具有一个或多个通道。
(3)使用示例
下面这段代码中初始化一个矩阵mat,分别用深拷贝和浅拷贝对图像进行了拷贝,并进行了修改,对比了修改之后深浅拷贝的数据发现浅拷贝共享同一份数据,深拷贝拥有独立的数据。
cv::Mat mat(5,5,CV_32F);
cv::Mat mat_shallow;
cv::Mat mat_copyTo;
for (int i = 0; i < 5; i++)
{
mat.at<float>(i,0) = (float)(i);
mat.at<float>(i,1) = (float)(i+1);
mat.at<float>(i,2) = (float)(i+2);
mat.at<float>(i,3) = (float)(i+3);
mat.at<float>(i,4) = (float)(i+4);
}
std::cout<<"源数据:"<<std::endl;
std::cout<<mat<<std::endl;
//浅拷贝
mat_shallow = mat;
//深拷贝
mat.copyTo(mat_copyTo);
//修改数据
for (int i = 1; i < 4; i++)
{
mat.at<float>(i,0) = (float)(0);
mat.at<float>(i,1) = (float)(0);
mat.at<float>(i,2) = (float)(0);
mat.at<float>(i,3) = (float)(0);
mat.at<float>(i,4) = (float)(0);
}
std::cout<<"修改后的源数据:"<<std::endl;
std::cout<<mat<<std::endl;
std::cout<<"浅拷贝数据被修改:"<<std::endl;
std::cout<<mat_shallow<<std::endl;
std::cout<<"copyTo深拷贝数据不动,和修改前的源数据一样:"<<std::endl;
std::cout<<mat_copyTo<<std::endl;
运行结果:
2.2 copyTo()配合Rect完成ROI(Region of interest)操作
利用矩形操作获取感兴趣的区域,然后再调用copyTo函数将该区域进行拷贝
cv::Mat mat(5,5,CV_32F);
cv::Mat mat_ROI(5,5,CV_32F);
for (int i = 0; i < 5; i++)
{
mat.at<float>(i,0) = (float)(i);
mat.at<float>(i,1) = (float)(i+1);
mat.at<float>(i,2) = (float)(i+2);
mat.at<float>(i,3) = (float)(i+3);
mat.at<float>(i,4) = (float)(i+4);
}
std::cout<<"源数据:"<<std::endl;
std::cout<<mat<<std::endl;
//创建interest区域,矩形,左上角为(1,1),大小为3*3
cv::Rect rect(1,1,3,3);
// 获取源数据interest对应的部分
cv::Mat ROI = mat(rect);
// 利用copyto函数和ROI区域进行拷贝
ROI.copyTo(mat_ROI);
std::cout<<"ROI区域:"<<std::endl;
std::cout<<ROI<<std::endl;
// copy后的矩阵大小与原来定义的大小不一样,会先申请新的内存然后再拷贝
std::cout<<"copyTo之后的mat:"<<std::endl;
std::cout<<mat_ROI<<std::endl;
结果:
2.3 copyTo()配合mask完成ROI(Region of interest)操作
copyTo()函数在进行拷贝的时候提供了mask的操作,mask大小必须和源图像大小一样,mask中非零元素就是要拷贝的元素。我们可以采用创建mask的方式实现和上面一样的效果。
cv::Mat mat(5,5,CV_32F);
cv::Mat mask;
mask = cv::Mat::zeros(5,5,CV_8U);
cv::Mat mat_ROI = cv::Mat::zeros(5,5,CV_32F);
for (int i = 0; i < 5; i++)
{
mat.at<float>(i,0) = (float)(i);
mat.at<float>(i,1) = (float)(i+1);
mat.at<float>(i,2) = (float)(i+2);
mat.at<float>(i,3) = (float)(i+3);
mat.at<float>(i,4) = (float)(i+4);
}
std::cout<<"源数据:"<<std::endl;
std::cout<<mat<<std::endl;
//利用mask创建interest区域,左上角为(1,1),大小为3*3
mask(cv::Range(1, 4), cv::Range(1, 4)) = cv::Scalar(1);
// 利用copyto函数和ROI区域进行拷贝
mat.copyTo(mat_ROI,mask);
std::cout<<"ROI区域:mask:"<<std::endl;
std::cout<<mask<<std::endl;
std::cout<<"copyTo之后的mat:"<<std::endl;
std::cout<<mat_ROI<<std::endl;
结果:
2.4 clone()
clone同样进行深拷贝,可以看到其调用的就是copyTo()函数,复制一份和源图一模一样的图像,不再像copyTo()有一些其他的操作。
inline
Mat Mat::clone() const
{
Mat m;
copyTo(m);
return m;
}
(三)总结
在OpenCV中进行数据操作时,常用到copyTo()和clone()两个函数。clone函数实现完全的深拷贝,在内存中分配新的空间。
而copyTo函数同样实现深拷贝,但是否申请新的内存空间取决于目标矩阵头中的大小信息是否与源矩阵一致。若一致,则直接进行拷贝而不申请新空间;若不一致,则先申请空间后进行拷贝。