OpenCV学习笔记(五):Mat结构

在之前的OpenCV学习笔记(一)用到的几种显示图像的方法中其中一种就是Mat。Mat结构在OpenCV 2.0后才得到广泛应用,相对于OpenCV1.0时代中的IplImage,它有个好处就是不用再手动释放图像内存。而用IplImage格式存储图像的时候就必须在推出前将图像内存release掉,即添加语句cvReleaseImage(&iplImg);,否则会造成内存泄漏。除了这个好处外,它的操作也更加简单,比如用imshow显示图像,imread读取图像等等,跟Matlab有点接近。


Mat是一个类,它由两个数据部分组成:矩阵头和一个指向存储所有像素值的矩阵的指针。其中矩阵头包含了矩阵的尺寸、存储方法、储存地址等信息,由此可以看出矩阵头所占的内存很小,而具体储存所有像素值的矩阵则非常大。举个例子,比如一个班级,矩阵头就相当于储存了班级里有多少人、男女比多少、平均身高、平均体重等信息,而矩阵就储存了班级中所有同学的所有基本信息,每一个同学就相当于是一个像素点,而每个像素点的颜色信息、深度信息等一串数据就相当于是每个同学的身高、体重、性别等一系列信息组成的一串数据。


那么问题来了,假如说我有一张图A,我想要把A图复制给B图,理论上我是不是应该重新开一片内存,用来储存B图的信息?但是仔细想一想,我A图和B图一毛一样,如果我再开一片内存存储B图,那岂不是和存储A图的内存重复啦?是不是会造成内存的浪费。所以OpenCV中的图像存储结构Mat就很好地解决了这个问题。它使用了引用计数机制,该机制的思路就是让每个Mat对象有自己的信息头,但是共享同一个矩阵。也就是让A图和B图的矩阵指针指向同一地址,共用一片内存。复制图像的时候只是复制了矩阵头的信息和矩阵指针,并不是复制了整个矩阵。


这么一来如果不是很熟悉的话使用起来就会有点别扭了。因为通过复制后得到的图像虽然名字不一样,一个叫A,一个叫B,信息头不同,但是通过任何一个对象所做的改变也会影响到其他对象。这跟人们平常想的很不一样。通俗地讲,我现在从图A那边copy出了B,那么假如我在B图上画一笔,那么我在A图上也会出现同样的一笔。如果这发生在现实生活中,有木有很神奇!具体看以下这段代码:


[cpp]  view plain  copy
  1. #include<opencv2/opencv.hpp>  
  2. using namespace cv;  
  3.   
  4. int main()  
  5. {  
  6.     Mat A, C;  
  7.     A = imread("1.jpg",1);  
  8.   
  9.     //两种copy方式  
  10.     Mat B(A);  
  11.     C = A;  
  12.     imshow("Picture B", B);  
  13.     imshow("Picture C", C);  
  14.   
  15.     //图像模糊一下,总要增加点区别的咯,不然怎么看得出变化  
  16.     blur(C, C, Size(7, 7), Point(-1, -1));   
  17.   
  18.     imshow("Picture A", A);  
  19.     imshow("Picture B second", B);  
  20.       
  21.     waitKey(0);  
  22.     return 0;  
  23. }</span>  

运行后的结果如下:




看图的时候请别忘了看上面的窗口名,所以这个时候我们发现,这三张图A,B,C其实已经变成了同一张了,因为我们对C模糊处理后,发现A图和B图同样也变得模糊了。


比如说你想要在图像中的感兴趣区域的话就会变得很方便,比如说插入一句

[cpp]  view plain  copy
  1. Mat D = A(Rect(10, 10, 200, 200));  
  2. imshow("Picture D", D);</span>  


那么显示出的D图像就是A图像的一部分,如果你把这句话插入到了模糊处理之后,那么D图像也是模糊的,如下:



那么有人会觉得这样子不好,有时候我只是想改动B图,A图想让它保留原来的样子,不想让它随着B图变化而变化。那么就需要使用到clone或者copyTo函数,如果使用了这两个函数,它就不再只是复制矩阵头了,它会复制整个矩阵本身,也就是重新开辟了一块内存在储存图像矩阵的所有信息。示例如下:


[cpp]  view plain  copy
  1.        Mat A;  
  2. A = imread("1.jpg",1);  
  3.   
  4. Mat B = A.clone();  
  5. blur(B, B, Size(10, 10), Point(-1, -1)); //模糊  
  6.   
  7. Mat C;  
  8. A.copyTo(C);  
  9. cvtColor(C, C, CV_BGR2GRAY); //灰度化  
  10.   
  11. imshow("Picture A", A);  
  12. imshow("Picture B", B);  
  13. imshow("Picture C", C);  
  14. waitKey(0);</span>  

显示的结果为:




从结果中反映:A图并没有跟着B图变模糊,也没有跟着C图变成灰度图,所以说此时A、B、C三张图是独立存在的。


接下去学习的是显式创建Mat对象的七种方法:


方法一:使用Mat()构造函数


[cpp]  view plain  copy
  1. Mat M(200, 200, CV_32FC3, Scalar(255, 0, 0));</span>  

第一个参数是行数;第二是参数表示列数;第三个参数表示制定元素的数据类型以及通道数,定义为CV_[位数][带符号与否][类型前缀]C[通道数],所有的情况如下图所示:


第四个参数Scalar是个short型向量,可以用来初始化矩阵,也可以用来表示颜色信息。比如说我现在imshow一下输出的是一张200X200的蓝色图像。注:RGB在opencv中储存的顺序是BGR。


方法二:在C\C++中通过构造函数进行初始化。这种构造方法可以构造多维度的矩阵。


[cpp]  view plain  copy
  1. int sz[3] = { 2, 2, 2 };  
  2. Mat M(3, sz, CV_8UC3, Scalar::all(0));</span>  

如上就构造了一个三维的矩阵。第一个参数表示维度数,第二个参数是一个指向数组的指针,该数组包含了每个维度的尺寸。第三个参数和第四个参数跟第一种方法一致。


方法三:为已存在的IplImage指针创建信息头。即实现从IplImage类型到Mat类型的转变。


值得注意的是:以前opencv2.X中IplImage转换到Mat的方法是


[cpp]  view plain  copy
  1. IplImage* srcimg = cvLoadImage("1.jpg", 1);  
  2. Mat M(srcimg);  
  3. //方法二:Mat M = srcimg;</span>  


但是这种方法在opencv3.X中就gg了,反正我这边是报错的,不知道大家在opencv3.0的环境中运行上述代码有没有报错,如果没有请告知,谢谢。那么在3.X中应该怎么实现从IplImage转换到Mat,请看下面的代码:


[cpp]  view plain  copy
  1. IplImage* srcimg = cvLoadImage("1.jpg", 1);  
  2. Mat M = cvarrToMat(srcimg);</span>  

顺便说一下从Mat类型转换到IplImage类型的方法:


[cpp]  view plain  copy
  1. Mat M = imread("1.jpg", 1);  
  2. IplImage img = M; //opencv2.X,只copy了信息头  
  3. IplImage *img = cvCloneImage(&(IplImage)M); //opencv3.X,真正的复制(包括所有数据)</span>  

方法四:利用create函数


[cpp]  view plain  copy
  1. Mat M;  
  2. M.create(4, 4, CV_8UC(2));  
  3. cout << "M=" << endl << " " << M << endl << endl;</span>  

但是这种方法不能给矩阵赋初值,只有在改变尺寸时重新为矩阵数据开辟内存而已。


方法五:采用Matlab形式


[cpp]  view plain  copy
  1. Mat A = Mat::eye(4, 4, CV_8UC1); //对角矩阵  
  2. Mat B = Mat::ones(3, 3, CV_32FC3); //单位矩阵  
  3. Mat C = Mat::zeros(3, 3, CV_64FC2); //零矩阵</span>  


方法六:对小矩阵使用逗号分隔式初始化


[cpp]  view plain  copy
  1. Mat A = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);</span>  


参考文献:

1. 毛星云等著《OpenCV3编程入门》;

2. http://www.cnblogs.com/wangguchangqing/p/4016179.html

3. http://www.cnblogs.com/zcftech/archive/2013/04/10/3013027.html

4. http://www.cnblogs.com/edver/p/5187190.html

5. http://wenku.baidu.com/link?url=A4OyKRnJEMRKiWDZAvrCIT8FfVtRLqftsuM4WzOFxs-eC9txuzkwuxfQtZJvWX6Tn9EiUFOfAKTq5zxxjo2nmSTmj3P4rimmYbOONcWB-dO

版权声明:本文为博主原创文章,未经博主允许不得用于商业转载。 https://blog.csdn.net/wangxuwen2/article/details/51966031

猜你喜欢

转载自blog.csdn.net/a8039974/article/details/80554625