失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OpenCV学习笔记(五):Mat结构

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

时间:2021-02-17 20:48:01

相关推荐

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 plaincopy#include<opencv2/opencv.hpp>usingnamespacecv;intmain(){MatA,C;A=imread("1.jpg",1);//两种copy方式MatB(A);C=A;imshow("PictureB",B);imshow("PictureC",C);//图像模糊一下,总要增加点区别的咯,不然怎么看得出变化blur(C,C,Size(7,7),Point(-1,-1));imshow("PictureA",A);imshow("PictureBsecond",B);waitKey(0);return0;}</span>

运行后的结果如下:

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

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

[cpp]view plaincopyMatD=A(Rect(10,10,200,200));imshow("PictureD",D);</span>

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

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

[cpp]view plaincopyMatA;A=imread("1.jpg",1);MatB=A.clone();blur(B,B,Size(10,10),Point(-1,-1));//模糊MatC;A.copyTo(C);cvtColor(C,C,CV_BGR2GRAY);//灰度化imshow("PictureA",A);imshow("PictureB",B);imshow("PictureC",C);waitKey(0);</span>

显示的结果为:

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

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

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

[cpp]view plaincopyMatM(200,200,CV_32FC3,Scalar(255,0,0));</span>

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

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

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

[cpp]view plaincopyintsz[3]={2,2,2};MatM(3,sz,CV_8UC3,Scalar::all(0));</span>

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

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

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

[cpp]view plaincopyIplImage*srcimg=cvLoadImage("1.jpg",1);MatM(srcimg);//方法二:MatM=srcimg;</span>

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

[cpp]view plaincopyIplImage*srcimg=cvLoadImage("1.jpg",1);MatM=cvarrToMat(srcimg);</span>

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

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

方法四:利用create函数

[cpp]view plaincopyMatM;M.create(4,4,CV_8UC(2));cout<<"M="<<endl<<""<<M<<endl<<endl;</span>

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

方法五:采用Matlab形式

[cpp]view plaincopyMatA=Mat::eye(4,4,CV_8UC1);//对角矩阵MatB=Mat::ones(3,3,CV_32FC3);//单位矩阵MatC=Mat::zeros(3,3,CV_64FC2);//零矩阵</span>

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

[cpp]view plaincopyMatA=(Mat_<double>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);</span>

参考文献:

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

2./wangguchangqing/p/4016179.html

3./zcftech/archive//04/10/3013027.html

4./edver/p/5187190.html

5./link?url=A4OyKRnJEMRKiWDZAvrCIT8FfVtRLqftsuM4WzOFxs-eC9txuzkwuxfQtZJvWX6Tn9EiUFOfAKTq5zxxjo2nmSTmj3P4rimmYbOONcWB-dO

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

如果觉得《OpenCV学习笔记(五):Mat结构》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。