数字图像中的每个点都称为像素(对于图像元素),并且每个像素可以存储一个或多个值,这取决于它是否是仅存储一个值的黑白图像(也称为二进制图像,比如只存储0或1),还是存储两个值的灰度图像,或者是存储三个值的彩色图像。这些值通常在整数0~255,但也可以使用其他范围,比如在高动态范围成像(high dynamic range imaging,简称HDRI)或热图像领域中的浮点数0~1。

图像是以矩阵格式存储的,其中的每个像素都有一个位置,并且可以通过列和行的编号来引用。 OpenCVMat 类来达到这个目的。在灰度图像中,使用单个矩阵,如下图所示。


在下图所示的彩色图像中,使用了一个宽度×高度×颜色通道数的矩阵。

Mat 类不仅仅用于存储图像,它还能存储任何类型和不同大小的矩阵。你可以将其用作代数矩阵并用它执行运算。在内存中,矩阵被保存为按列和行排序的数组或值序列。表2-1显示 BGR 图像格式的像素序列。

关于 Mat 类, 首先我们要知道的是:

  • 不必再手动为其幵辟空间。
  • 不必再在不需要时立即将空M释放。

这里指的是手动开辟空间并非必须,但它依旧是存在的—大多数 OpenCV 函数仍会手动地为输出数据幵辟空间。当传递一个已经存在的 Mat 对象时, 开辟好的矩阵空间会被重用。也就是说, 我们每次都使用大小正好的内存来完成任务。

1. Mat 结构

Mat 是一个类, 由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息) 和一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。

矩阵头的尺寸是常数值, 但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,
当在程序中传递图像并创建副本时,大的开销是由矩阵造成的, 而不是信息头。

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

Mat A, C; / / 仅创建信息头部分
A = imread("1.jpg") / / 这里为矩阵开辟内存
Mat B(A); / /使用拷贝构造函教
C = A; / /賦值运算符

以上代码中的所有 Mat 对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其他对象。

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

Mat D(A, Rect(10, 10, 100, 100)) ; / / 使用矩形界定
Mat E = A(Range:all() , Range(1,3)) ; / / 用行和列来界定

如果想复制矩阵本身( 不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo()

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

现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。

总结如下:

  • OpenCV 函数中输出图像的内存分配是自动完成的(如果不特别指定的话);
  • 使用 OpenCVC++ 接口时不需要考虑内存释放问题;
  • 赋值运算符和拷贝构造函数(构造函数)只复制信总头;
  • 使用函数 clone() 或者 copyTo() 来复制一幅图像的矩阵;

2. 像素值的存储方法

存储像素值需要指定颜色空间和数据类型。

  • 颜色空间是指针对一个给定的颜色,如何组合颜色元素以对其编码。最简单的颜色空间要属灰度级空间, 只处理黑色和白色, 对它们进行组合便可以产生不同程度的灰色。

对于彩色方式则有更多种类的颜色空间, 但不论哪种方式都是把颜色分成三个或者四个基元素,通过组合基元素可以产生所有的颜色。 RGB 颜色空间是最常用的一种颜色空间,。它的基色是红色、绿色和蓝色,有时为了表示透明颜色也会加入第四个元素alpha ( A)。

每个组成元素都有自己的定义域, 而定义域収决于其数据类型,如何存储一个元素决定了我们在其定义域上能够控制的精度。最小的数据类型是 char ,占一个字节或者 8 位,可以是有符号型(0 到255 之间)或无符号型( -127 到+127之间)。尽管使用三个 char 型元素已经可以表示 1600 万种可能的颜色(使用 RGB 颜色空间),但若使用 float ( 4 字节,32 位)或 double ( 8 字节,64 位)则能给出更加精细的颜色分辨能力。但同时也要切记, 增加元素的尺寸也会增加图像所占的内存空间。

3. 创建 Mat 对象方法

我们可以通过 Mat 的运算符 << 来查看其值。但要记住, Mat 的运算符 << 只对二维矩阵有效。 Mat 不但是一个非常有用的图像容器类,同时也是一个通用的矩阵类,我们也可以用它来创建和操作多维矩阵。

创建一个 Mat 对象有多种方法, 列举如下:

3.1 使用 Mat 构造函数

最常用的方法是直接使用 Mat() 构造函数,示例如下:

 Mat M(3, 2, CV_8UC3, Scalar(0, 0, 255));cout << "M is "<< endl << M << endl;

输出结果:

M is
[  0,   0, 255,   0,   0, 255;0,   0, 255,   0,   0, 255;0,   0, 255,   0,   0, 255]

对于二维多通道图像, 首先要定义其尺寸,即行数和列数。然后,需要指定存储元素的数据类型以及每个矩阵点的通道数。为此,依据下面的规则有多种定义:

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

即:

cv_[位数][带符号与否][类型前缀]c[通道数]

比如 CV_8UC3 表示使用 8 位的 unsigned char 型,每个像素由三个元素组成三通道。而预先定义的通道数可以多达四个。另外, Scalar 是个 short 型的向量。

受支持的类型取决于要存储的数字类型和通道数,最常见的类型如下:

CV_8UC1;
CV_8UC3;
CV_8UC4;CV_32FC1;
CV_32FC3;
CV_32FC4;

3.2 通过C++构造函数进行初始化

示例代码:

 int sz[3] = {2,1,3};Mat M(3, sz, CV_8UC3, Scalar::all(0));

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

3.3 利用 create() 函数

利用 Mat 类中的 Create() 成员函数进行 Mat 类的初始化操作,示范代码如下。

 Mat M;M.create(4, 3, CV_8UC(2));cout << "M is "<< endl << M << endl;

输出结果:

M is
[  0,   0,   0,   0,   0,   0;0,   0,   0,   0,   0,   0;0,   0,   0,   0,   0,   0;0,   0,   0,   0,   0,   0]

需要注意的是,此创建方法不能为矩阵设初值,只是在改变尺寸时重新为矩阵数据开辟内存而已。

3.4 使用 zeros()、ones()、eyes() 初始化

示例代码:

 Mat E = Mat::eye(3, 3, CV_64F);cout << "E is "<< endl << E << endl;Mat O = Mat::ones(3, 2, CV_32F);cout << "O is "<< endl << O << endl;Mat Z = Mat::zeros(3, 2, CV_8UC1);cout << "Z is "<< endl << Z << endl;

输出结果:

E is
[1, 0, 0;0, 1, 0;0, 0, 1]O is
[1, 1;1, 1;1, 1]Z is
[  0,   0;0,   0;0,   0]

3.5 对小矩阵使用逗号分隔初始化

示例代码

 Mat C = (Mat_<double> (3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0 );cout << "C is "<< endl << C << endl;

输出结果:

C is
[0, -1, 0;-1, 5, -1;0, -1, 0]

3.6 为已存在的对象创建新信息头

使用成员函数 clone() 或者 copyTo() 为一个已存在的 Mat 对象创建一个新的信息头,示范代码如下。

 Mat C = (Mat_<double> (3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0 );Mat RowClone = C.row (1).clone() ;cout << "RowClone is "<< endl << RowClone << endl;

输出结果:

RowClone is
[-1, 5, -1]

4. Mat 对象运算

OpenCVMat 类能够执行所有的矩阵运算。我们可以用 +- 运算符来加上或减去两个相同大小的矩阵,如以下代码块所示:

 Mat a = Mat::eye(Size(3, 2), CV_32F);Mat b = Mat::ones(Size(3, 2), CV_32F);Mat c = a + b;Mat d = a - b;

运算结果如下图:

我们可以用 * 运算符乘以一个标量,或者用 mul 函数乘以矩阵的每个元素,也可以用 * 运算符执行矩阵乘法:

 Mat a = Mat::eye(2, 3, CV_32F);Mat b = Mat::ones(3, 2, CV_32F);// Mat a2 = a * 2;// Mat d = (a + 1).mul(b + 3);// Mat ab = a * b;cout << a * 2 << endl;cout << (a + 1).mul(b + 3) << endl;cout << a * b << endl;// cout << a + b << endl;// cout << a - b << endl;

理论上会有如下结果,但是调试发现报错,待继续分析。

其它运算请参考:
https://docs.opencv.org/2.4/modules/core/doc/core.html

OpenCV 笔记(06)— Mat 结构、像素值存储方法、创建 Mat 对象各种方法、Mat 对象的运算相关推荐

  1. oracle rman本地备份,【学习笔记】Oracle RMAN 备份集存储到远程其它服务器实现方法...

    天萃荷净 应运维DBA要求,将服务器本地Oracle数据库的RMAN备份集备份存储到远程其它服务器中,结合案例详细讲解实现步骤 在linux中,要使用rman备份后传输到远程服务器上,可以选择ftp, ...

  2. opencv(2)- 处理像素值

    opencv中使用Mat数据类型表示图像,这是类似int.float等传统类型的一种opencv中定义的数据类型. opencv常见的数据类型: point:代表二维点,用于图像坐标点.如point( ...

  3. opencv cv2.imwrite() 写入后像素值改变 jpg写入写出像素值变化

    opencv的cv2.imwrite() 写入后像素值发生改变 jpg的问题 在做图像分割的时候,我代码里明明只有8种颜色,但是生成的图片用PS打开后,添加了许多和8种颜色相近的颜色 于是我怀疑是读写 ...

  4. c++使用opencv截取照片并判断像素值

    功能实现为:截取一寸照片底下八分之一处部分,并判断衣服为浅色或深色 代码实现为: int handle_photos(cv::Mat bgr){//选取照片底面部分cv::Rect rect(0,bg ...

  5. java存储键值结构_java-键值存储为主数据库

    我将要开始一个项目,该项目的读写操作非常频繁且频繁.因此,环顾四周,我发现内存数据库正是为此目的而创建的.经过更多调查后,我进入了redis. Redis看起来很酷(虽然刚开始阅读,但是对此有很多了解 ...

  6. python+OpenCv笔记(七):图像的形态学操作(腐蚀与膨胀、开闭运算、礼帽与黑帽)

    一.腐蚀与膨胀 腐蚀就是原图中高亮的部分被蚕食,效果图拥有比原图更小的高亮区域. 腐蚀的作用是:消除物体边界点,使目标缩小,可以消除小于结构元素的噪声点. 膨胀就是使原图中高亮的部分扩张,效果图拥有比 ...

  7. Study notes for OpenCV——第八节 CvMat结构体与矩阵的创建

    一.数据类型CvMat CvMat与IplImage的关系就如同C++中的继承关系,IplImage可以视为从CvMat派生的.CvArr可以视为抽象类. 二.CvMat矩阵结构 首先我们要知道Ope ...

  8. 用OpenCV计算图像的亚像素值

    原理如图: float subPixelValue(cv::Mat &img, float x, float y) {// boundary checkif (x < 0)x = 0;i ...

  9. OpenCV 数组存储图片像素值,便于后期图像处理

    刚开始接触到OpenCV,本身是想学借助OpenCV的库来进行图像处理学习的,后来发现OpenCV将所有的东西都集成起来了,对于打算学习图像处理来说这样反而不是什么好事: 在我看来,图像处理基本基于对 ...

最新文章

  1. R语言可视化分面图、假设检验、单变量分组多水平t检验并指定参考水平、可视化单变量分组多水平分面箱图(faceting boxplot)并添加显著性水平、指定显著性参考水平
  2. 极限脱出 量子计算机,《极限脱出3:零时困境》50条指向zero身份线索
  3. 如何1秒内向Sql Server插入几万条数据
  4. ipsan虚拟存储服务器,HP P4000 IPSAN存储系统
  5. 6410 实现 linux 串口驱动详解
  6. 插入节点insertBefore()
  7. MSPA安装与生态源地提取
  8. Orchard使用中的坎坎坷坷
  9. linux双系统启动项grub,grub双系统启动顺序更改
  10. 确定单峰区间Matlab,0618法matlab实验报告.doc
  11. 如何查看计算机的硬盘序列号,电脑上的硬盘序列号如何查
  12. ai面试的优缺点_AI面试需要注意哪些问题?
  13. 罗振宇2021“时间的朋友”跨年演讲精华版全文
  14. Layim 聊天功能
  15. 竖屏java转横屏_android设置横屏和竖屏的方法
  16. c开头英文语言,字母C开头的英文名
  17. 微信小程序开发入门(二)image标签及图片样式
  18. 微信群发频繁发送消息,请稍后再试?
  19. 内蒙农信携手星环科技建设农信大数据平台,激活金融业务创新
  20. STM32F4系列ADC最大转换速率及操作条件(以STM32F407ZGT6为例)

热门文章

  1. aws lambda使用, aws无服务器部署应用。 aws ecr凭证获取和使用。
  2. 2022-2028年中国自热米饭市场竞争策略及行业投资潜力预测报告
  3. 【PyTorch学习笔记】4:在Tensor上的索引和切片
  4. 智能驾驶操作系统OS
  5. Structured Streaming编程 Programming Guide
  6. CPU的自动调度矩阵乘法
  7. CVPR2020论文点评: AdderNet(加法网络)
  8. 2021年大数据Flink(十九):案例一 基于时间的滚动和滑动窗口
  9. 你哪来这么多事(四):职工信息排序
  10. android RecyclerView 添加可拖动的滚动条