Mat - 基本图像容器

世间的图像是各种各样的,但是到了计算机的世界里所有的图像都简化为了数值矩以及矩阵信息。作为一个计算视觉库,OpenCV的主要目的就是处理和操作这些信息,来获取更高级的信息,也就是潜在的价值。因此,我们需要首先学习OpenCV是如何存储并操作图像的

Mat

2001年OpenCV刚刚出现的时候是基于C语言接口而建。 为了在内存(Memory)中存放图像, 当时采用的是名为IplImage(Intel Image Processing Library IPL)的C语言结构体,最大的问题就是需要手动的进行内存管理。在OpenCV2.0版本中出现了C++接口, 利用自动内存管理(说法不严谨)给出了解决问题的新方法。

Mat是一个类,包含两部分数据部分:

  • (1)矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)

    其中矩阵头的尺寸是常数值,矩阵本身的尺寸根据图像的大小而定。

    当在程序中传递图像并创建拷贝时,大的开销是由矩阵造成的,而不是信息头,但是在OpenCV函数中矩阵的传递又是非常常见的。

  • (2)指针(指向存储所有像素值的矩阵)

为了解决矩阵开销问题,OpenCV采用了引用计数机制。其思路就是每个Mat对象都有自己的头信息, 但共享同一个矩阵,通过矩阵指针指向同一个地址而实现。而拷贝构造函数而只拷贝信息头和矩阵指针,不拷贝矩阵。

Mat A, C;                                  //只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); //这里为矩阵开辟内存 Mat B(A); //使用拷贝构造函数 C = A; //赋值运算符

说明: A, B, C三个Mat对象都指向了同一个也是唯一一个数据矩阵。虽然他们的信息头不同, 但通过任何一个对象所做的改变都会影响其他对象。实际上,不同的对象只是访问相同数据矩阵的不同途径而已。

还可以创建只引用部分数据的信息头,比如创建一个感兴趣区域(ROI),你只需要创建包含边界信息的信息头:

Mat D(A, Rect(10, 10, 100, 100));          //使用一个矩阵
Mat E = A(Range:all(), Range(1, 3)); //使用行和列表示边界

如果矩阵属于多个Mat对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人考虑了一个Mat对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数为零的时候,矩阵会被清理。但某些时候我们仍然会想拷贝矩阵本身(不只是信息头和矩阵指针),这个时候可以使用clone()或者copyTo()函数。

Mat F = A.clone();
Mat G;
A.copyTo(G);

现在改变F或者G就不会影响Mat信息头所指向的矩阵。总结一下,你需要记住的是:

  • OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。

  • 使用OpenCV的C++接口时不需要考虑内存释放问题。

  • 赋值运算符和拷贝构造函数(constructor--ctor)只拷贝信息头。

  • 使用函数clone()或者copyTo()来拷贝一副图像的矩阵。

显示地创建一个Mat对象

  • Mat()构造函数
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255));
cout << "M = " << endl << " " << M << endl;

对于二维多通道图像,首先要定义其尺寸,即行数和列数。

然后需要指定元素的数据类型以及每个矩阵点的通道数。

CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]

比如CV_8UC3表示使用8位的unsigned char型,每个像素由三个元素组成的三通道。预先定义的通道数可以多达四个。 Scalar是short型vector。指定这个可以使用指定的定制化值来初始化矩阵。当然,如果你需要更多维数,你可以使用宏并把通道数放在小括号中,如下所示

在C\C++中通过构造函数进行初始化

int sz[3] = {2, 2, 2};
Mat L(3, sz, CV_8UC1, Scalar::all(0));

上面的例子演示了如何创建一个超过两维的矩阵:指定维数,然后传递一个指向一个数组的指针,这个数组包含每个维度的尺寸;其余的相同

为已存在IplImage指针创建信息头:

IplImage* img = cvLoadImage("greatwave.jpg", 1);
Mat mtx(img); //convert IplImage* -> Mat
  • Create() function: 函数
M.create(4, 4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;

这个创建方法不能为矩阵设初值,它只是在改变尺寸时重新为矩阵数据开辟内存。

  • MATLAB形式的初始化: zeros(),eyes(),ones();
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl; Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl; Mat Z = Mat::zeros(3,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
  • 对于小矩阵可以使用逗号分隔的方式初始化
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
  • 使用clone()或copyTo()为一个存在的Mat对象创建一个新的信息头
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;

格式化打印

  • 调用函数randu()来对一个矩阵使用随机数填充,需要指定随机数的上界和下界:
Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));
  • 默认打印方式
cout << "R (default) = " << endl << R << endl << endl;

OpenCV从入门到放弃系列之——core模块.核心功能(一)的更多相关文章

  1. OpenCV从入门到放弃系列之——如何扫描图像、利用查找表和计时

    目的 如何遍历图像中的每一个像素? OpenCV的矩阵值是如何存储的? 如何测试我们所实现算法的性能? 查找表是什么?为什么要用它? 测试用例 颜色空间缩减.具体做法就是:将现有颜色空间值除以某个输入 ...

  2. OpenCV从入门到放弃系列之——图像的基本操作

    读取.修改.保存图像 图像读取函数imread(); 图像颜色空间的转换cvtColor(); 图像保存至硬盘imwrite(); /********************************* ...

  3. [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world

    [大数据从入门到放弃系列教程]在IDEA的Java项目里,配置并加入Scala,写出并运行scala的hello world 原文链接:http://www.cnblogs.com/blog5277/ ...

  4. [大数据从入门到放弃系列教程]第一个spark分析程序

    [大数据从入门到放弃系列教程]第一个spark分析程序 原文链接:http://www.cnblogs.com/blog5277/p/8580007.html 原文作者:博客园--曲高终和寡 **** ...

  5. php从入门到放弃系列-01.php环境的搭建

    php从入门到放弃系列-01.php环境的搭建 一.为什么要学习php 1.php语言适用于中小型网站的快速开发: 2.并且有非常成熟的开源框架,例如yii,thinkphp等: 3.几乎全部的CMS ...

  6. php从入门到放弃系列-04.php页面间值传递和保持

    php从入门到放弃系列-04.php页面间值传递和保持 一.目录结构 二.两次页面间传递值 在两次页面之间传递少量数据,可以使用get提交,也可以使用post提交,二者的区别恕不赘述. 1.get提交 ...

  7. php从入门到放弃系列-03.php函数和面向对象

    php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: function functionName( ...

  8. php从入门到放弃系列-02.php基础语法

    php从入门到放弃系列-02.php基础语法 一.学习语法,从hello world开始 PHP(全称:PHP:Hypertext Preprocessor,即"PHP:超文本预处理器&qu ...

  9. K8S从入门到放弃系列-(16)Kubernetes集群Prometheus-operator监控部署

    Prometheus Operator不同于Prometheus,Prometheus Operator是 CoreOS 开源的一套用于管理在 Kubernetes 集群上的 Prometheus 控 ...

随机推荐

  1. Python之路----------time模块

    时间模块是常用的模块 一.time模块 import time print(time.clock())#返回处理器时间,3.3开始已经屏蔽. print(time.altzone)#返回与UTC时间差 ...

  2. EXC_ARM_DA_ALIGN

    ios 版本上的问题  armv7  ipad2 int64 t = *(int64*)pBuff; 如果pBuff不是8字节对齐的地址就 crash 变通的方法是通过memcpy __sync_fe ...

  3. SLP测试记录

    个人感觉来说这个游戏对我没有什么吸引力...完全不知道用户需求在哪...是我最不喜欢的一个游戏 不过听制作团队之前的介绍,这应该不算是一个游戏,而是一个游戏练习器?所以从游戏的角度来评判的话感觉有很多 ...

  4. 记得初学JS时候练个九九乘法表都写的要死要活

    还记得当初刚接触JS时候,看到视频中老师写了个九九乘法表,觉得好神奇,可是自己在下面动手写了半天还是有各种问题,甚是懊恼啊.今又看到园子里有关于乘法表的博文,出于对过去的不舍与缅怀,遂重写一遍. &l ...

  5. vbs让电脑发音说话

    Dim vbs1 'set vbs1 = WScript.CreateObject("WScript.Shell") set vbs1 = WScript.CreateObject ...

  6. Windows Phone 十二、设计器同步

    在设计阶段为页面添加数据源 Blend或者VS的可视化设计器会跑我们的代码,然后来显示出来,当我们Build之后,设计器会进入页面的构造函数,调用InitializeComponent();方法来将U ...

  7. 在nginx日志的access log中记录post请求的参数值

    背景:有时程序偶出现参数少了或没有提交到下一个链接Url里后出现问题,如何查呢,最好的办法是在nginx上的加post参数,以定位到问题才有可能对某个UIR的代码出现的问题进行排查. og_forma ...

  8. swift基础:第六部分:类与对象

    http://reactnative.cn/docs/0.24/getting-started.html#content(react Native 开发文档) 互联网这个时代,你松懈一天,就会有很多很 ...

  9. Log(android.util.Log)(option+return)

    Log.v() verbose 琐碎,详细 Log.d() debug 调试 Log.i() info 信息,重要,分析行为 Log.w() wain 警告 log.e() error 错误 参数:t ...

  10. Java线程:线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...