opencv中为矩阵复制提供了copyTo函数、clone函数、重载运算符和拷贝构造函数,用法非常简单:
代码语言:javascript复制 Mat srcimage = imread("1.jpg");
Mat firstimage,secondimage,thirdimage;
srcimage.copyTo(firstimage);
secondimage = srcimage.clone();
thirdimage = srcimage;
Mat fourthimage(srcimage);
但是在他们四者间存在着较大的区别,我们先写个简单的代码测试一下:
代码语言:javascript复制 Mat srcimage = imread("2.jpg");
Mat firstimage,secondimage,thirdimage;
double t1 = (double)getTickCount();
srcimage.copyTo(firstimage);
t1 = (double)getTickCount() - t1;
double t2 = (double)getTickCount();
secondimage = srcimage.clone();
t2 = (double)getTickCount() - t2;
double t3 = (double)getTickCount();
thirdimage = srcimage;
t3 = (double)getTickCount() - t3;
double t4 = (double)getTickCount();
Mat fourthimage(srcimage);
t4 = (double)getTickCount() - t4;
printf("copyTo execution time = %lfmsn", t1*1000. / getTickFrequency());
printf("clone execution time = %lfmsn", t2*1000. / getTickFrequency());
printf(" = execution time = %lfmsn", t3*1000. / getTickFrequency());
printf(" 构造函数 execution time = %lfmsn", t4*1000. / getTickFrequency());
getchar();
为了让效果明显,选择了一张很大的图片,这样图片的尺寸是3120*4160,实验结果如下:
我们可以看到,copyTo函数、clone函数占用时间差不多,但是“= ”运算符与拷贝构造函数用的时间却非常少,这是为什么呢?
造成这样的情况的原因是因为Mat的数据类型以及它的数据组成造成的。
Mat类
在opencv 2.x之前,OpenCV基于 C 语言接口而建。为了在内存(memory)中存放图像,当时采用名为 IplImage 的C语言结构体作为基本的图像容器。但是该方式有一个极大的弊端就是要手动内存管理,其依据是用户要为开辟和销毁内存负责。虽然对于小型的程序来说手动管理内存不是问题,但一旦代码开始变得越来越庞大,你需要越来越多地纠缠于这个问题,而不是着力解决你的开发目标。 之后,新的Mat类型代替了之前的IplImage,这次改变也带来了Opencv最强大的数据类型—Mat。**Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。**矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝时,大的开销是由矩阵造成的,而不是信息头。所以,除非有必要,否则我们不应该拷贝大的图像,因为这会降低程序速度。 为了搞定这个问题,OpenCV使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。
从上面的话我们可以看到,copyTo函数、clone函数拷贝的不仅仅是信息头,还有矩阵本身,而“= ”运算符与拷贝构造函数仅仅拷贝了信息头,他们指向的其实是一个矩阵,也就是在上的程序中,我们改变srcimage ,thirdimage和fourthimage中的任何一个,另外两个也会跟着变化,我们再改一下代码测试一下(为了屏幕能放下,我们把图片换一下):
代码语言:javascript复制 Mat srcimage = imread("1.jpg");
Mat firstimage,secondimage,thirdimage;
imshow("result1",srcimage);
double t1 = (double)getTickCount();
srcimage.copyTo(firstimage);
Point center = Point(55,55);
int r = 10;
circle(firstimage,center,r,Scalar(0,0,255),-1);
t1 = (double)getTickCount() - t1;
imshow("result2",firstimage);
double t2 = (double)getTickCount();
secondimage = srcimage.clone();
t2 = (double)getTickCount() - t2;
double t3 = (double)getTickCount();
thirdimage = srcimage;
t3 = (double)getTickCount() - t3;
double t4 = (double)getTickCount();
Mat fourthimage(srcimage);
t4 = (double)getTickCount() - t4;
Point a = Point (0,0);
Point b = Point (thirdimage.cols,thirdimage.rows);
line(thirdimage,a,b,Scalar(255,0,0));
printf("copyTo execution time = %lfmsn", t1*1000. / getTickFrequency());
printf("clone execution time = %lfmsn", t2*1000. / getTickFrequency());
printf(" = execution time = %lfmsn", t3*1000. / getTickFrequency());
printf(" 构造函数 execution time = %lfmsn", t4*1000. / getTickFrequency());
imshow("result3",srcimage);
waitKey(0);
getchar();
可以看到,在原图刚刚读入的时候,显示了窗口result1,此时显示的就是读入的图片,firstimage是由srcimage通过copyTo的方法拷贝的,我们把firstimage画一个圆,然后用result2窗口显示,thirdimage是由srcimage通过“=”运算符拷贝的,我们将thirdimage画个线,并用result3再一次显示srcimage,然后我们可以看到,result3中srcimage已经变了,变成了和thirdimage一样的效果,说明之前的说法是正确的,他们共有了同一个矩阵。