OpenCV copyTo、clone、“=”与拷贝构造函数的区别

2022-05-07 09:23:15 浏览数 (1)

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一样的效果,说明之前的说法是正确的,他们共有了同一个矩阵。

0 人点赞