部分内容转自:OpenCV Tuturialggicci

OpenCV Tuturial中可查看Mat的初始化与打印方法。

Mat本质上是由两个数据部分组成的类:

  1. 矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)
  2. 一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针

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

特性

  • reference counting:当counting为0时,会自动释放内存;
  • shallow copy:当令mat1=mat2时,二者指向的是同一份image data,对mat2的修改会等效作用于mat1上,如果确实要拷贝出一个副本时,需要调用copyTo函数或者clone函数。
Mat A, C;                                 // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A); // 使用拷贝构造函数
C = A; // 赋值运算符

以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际上,不同的对象只是访问相同数据的不同途径而已。注意,这个特性要求OpenCV中的类应该返回副本copyTo(returnMat),否则当某个类的实体发生改变时,该类的其余实体都会发生改变。

  • 你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:
// ROI是某个矩形
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries
// 假如ROI是某些行或者列时:
cv::Mat imageROI= image.rowRange(start,end) ;
cv::Mat imageROI= image.colRange(start,end) ;
// 单行或者单列时:
image.row(rowNum),image.col(colNum)
  • 出于效率优化,每行的结尾可能存在padding,使得每行大小是2的整数次幂,可以通过M.isContinuous()判断是否存在padding(True:不存在padding)。当不存在padding时,Mat image的内存占用为(byte)=image.elemSize() * image.total()

属性

  • data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存 (uchar* data)
  • dims:Mat所代表的矩阵的维度,如 3 * 4 的矩阵为 2 维, 3 * 4 * 5 的为3维
  • channels():通道,矩阵中的每一个矩阵元素拥有的值的个数,比如说 3 * 4 矩阵中一共 12 个元素,如果每个元素有三个值,那么就说这个矩阵是 3 通道的,即 channels = 3。常见的是一张彩色图片有红、绿、蓝三个通道。
  • depth():深度,即每一个像素的位数(bits),在opencv的Mat.depth()中得到的是一个 0 – 6 的数字,分别代表不同的位数:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; 可见 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;
  • step:是一个数组,定义了矩阵的布局,包括padding部分,具体见下面图片分析,另外注意M.step[m-1] == M.elemSize();
  • step1(n) == step[n] / elemSize1,M.step1(m-1)总是等于 channels;
  • elemSize() : 矩阵中每一个元素的数据大小,如果Mat中的数据的数据类型是 CV_8U 那么 elemSize = 1,CV_8UC3 那么 elemSize = 3,CV_16UC2 那么 elemSize = 4;
  • elemSize1(): 表示的是矩阵中数据类型的大小,即 elemSize / channels 的大小
  • total():像素的总数

地址计算

灰度图的每个像素都是0~255的8 bit值。彩色图有BGR三通道,其像素可视为一个三维向量,每个分量也是一个0~255的8 bit值。代码中有时存在第四维alpha,表示透明度。

最小的数据类型可能是 char 类型,这意味着一个字节或 8 位。这可能是有符号(值-127 到 + 127)或无符号(以便可以存储从 0 到 255 之间的值)。虽然这三个组件的情况下已经给 16 万可能的颜色来表示 (如 RGB 的情况下),我们可通过使用浮点数 (4 字节 = 32 位) 或double(8 字节 = 64 位) 数据类型的每个组件获得甚至更精细的控制。

注意:当目标为ROI时,地址计算失效。

addr(Mi0,i1,…im-1)=M.data + M.step[0] * i0 + M.step[1] * i1 + … + M.step[m-1] * im-1

(其中 m = M.dims M的维度)

考虑二维情况(stored row by row)按行存储:

  • M.dims == 2 ;

当数据类型为 CV_8U单通道的 uchar 时:

  • M.channels() == 1 ;
  • M.elemSize() == 1
  • M.elemSize1() ==  1 ;
  • M.step[0] ==  4  ;
  • M.step[1] == 1;
  • M.step1(0) == 4;
  • M.step1(1) == 1;

当数据类型是 CV_8UC3三通道:

  • M.channels() == 3;
  • M.elemSize() == 3
  • M.elemSize1() == 1
  • M.step[0] == 12 ;
  • M.step[1] ==  3;
  • M.step1(0) == 12 ;
  • M.step1(1) ==  3;
 当数据类型为 CV_16SC4,也就是 short 类型:

  • M.dims == 3 ;
  • M.rows == M.cols == –1;
  • M.channels() == 4 ;
  • M.elemSize1() == sizeof(short) == 2 ;
  • M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;
  • M.step[0] == 4 * 6 * M.elemSize() == 192;
  • M.step[1] == 6 * M.elemSize() == 48;
  • M.step[2] == M.elemSize() == 8;
  • M.step1(0) == M.step[0] / M.elemSize() == 48 / 2 == 96 (第一维度(即面的元素个数) * 通道数);
  • M.step1(1) == M.step[1] / M.elemSize() == 12 / 2 == 24(第二维度(即行的元素个数/列宽) * 通道数);
  • M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三维度(即元素) * 通道数);

其他

P.S.1

OpenCV1中采用的IplImage(Intel Image Processing Library)类型应尽量不再使用。可用以下方法将IplImage转为Mat:

IplImage* iplImage = cvLoadImage("c:\\img.jpg");

cv::Mat image(iplImage,false); //false是默认参数,表示浅拷贝,即image指向同一区域,不额外占用空间。

若确实需要使用IplImage时,应注意dangling pointer的问题,可选择:

  • 使用reference counting pointer:cv::Ptr<IplImage> iplImage = cvLoadImage("c:\\img.jpg");
  • 显式销毁指针:cvReleaseImage(&iplImage);

P.S.2

Mat 中的channel是BGR,在Qt中显示的图像需是QImage类型(通道为RGB),可通过以下方式转换:

// change color channel ordering
cv::cvtColor(image,image,CV_BGR2RGB);
// Qt image
QImage img= QImage((const unsigned char*)(image.data), image.cols,image.rows,QImage::Format_RGB888);
// display on label
ui->label->setPixmap(QPixmap::fromImage(img));
// resize the label to fit the image
ui->label->resize(ui->label->pixmap()->size());

P.S.3

Mat的几个method:

cv::Mat image=imread("image.jpg"), result;
// 第i行的地址
uchar* data = image.ptr<uchar>(0);
// 按照image的大小和类型格式化 Mat result
result.create(image.rows,image.cols,image.type());
// ROI访问某行或者某列
result.row(0).setTo(cv::Scalar(0));
result.row(result.rows-1).setTo(cv::Scalar(0));
// 在不占用内存的情况下,改变矩阵的维数
image.reshape(1, // new number of channels
image.cols*image.rows) ; // new number of rows
// True:每行的结尾不存在padding
if (image.isContinuous()){
/*图像的内存空间是连续的*/
nc= nc*nl;
nl= 1; // it is now a 1D array
}

OpenCV学习笔记(二) cv::Mat的更多相关文章

  1. opencv学习笔记二

    1,读取照片(imread()) 2,处理照片(cvtcolor()) 3,命名窗口(namewindow()) 4,显示照片(imshow()) 5,保存照片(imwrite()) #include ...

  2. OpenCV学习笔记二:OpenCV模块一览

    注:本系列博客基于OpenCV 2.9.0.0 一,一览图: 二,模块: /* 基础库 */ 1,opencv_core(链接) ,opencv最基础的库.包含exception,point,rect ...

  3. 【opencv学习笔记二】opencv3.4.0组件结构说明

    在学习opencv使用之前我们先来看一下opencv有哪些组件结构.至于OpenCV组件结构的研究方法, 我们不妨管中窥豹,通过opencv安装路径下include目录里面头文件的分类存放,来一窥Op ...

  4. OpenCV学习笔记二十:opencv_ts模块

    一,简介: OpenCV测试库,用于单元测试.

  5. OpenCV学习笔记(二) - 写入视频、jpg格式

    写入视频: import sys, os import cv2 imgDir = '/Users/xxx/salient/' videoName = 'vname' fps = 30 #帧频 outp ...

  6. opencv学习笔记(二)寻找轮廓

    opencv学习笔记(二)寻找轮廓 opencv中使用findContours函数来查找轮廓,这个函数的原型为: void findContours(InputOutputArray image, O ...

  7. opencv学习笔记(一)IplImage, CvMat, Mat 的关系

    opencv学习笔记(一)IplImage, CvMat, Mat 的关系 opencv中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage,这三种类型都可以代表和显示图像,但是,M ...

  8. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  9. opencv学习笔记(六)直方图比较图片相似度

    opencv学习笔记(六)直方图比较图片相似度 opencv提供了API来比较图片的相似程度,使我们很简单的就能对2个图片进行比较,这就是直方图的比较,直方图英文是histogram, 原理就是就是将 ...

  10. opencv学习笔记(五)镜像对称

    opencv学习笔记(五)镜像对称 设图像的宽度为width,长度为height.(x,y)为变换后的坐标,(x0,y0)为原图像的坐标. 水平镜像变换: 代码实现: #include <ios ...

随机推荐

  1. 根据要求完成表单以及使用servlet处理表单 任务要求 掌握Servlet输出表单和接收表单数据(多值组件的读取)。

    Servlet代码: package com.test; import java.io.IOException; import java.io.PrintWriter; import java.uti ...

  2. C# sftp通过秘钥上传下载

    一.适用场景 我们平时习惯了使用ftp来上传下载文件,尤其是很多Linux环境下,我们一般都会通过第三方的SSH工具连接到Linux,但是当我们需要传输文件到Linux服务器当中,很多人习惯用ftp来 ...

  3. display:table的几个用法(元素平分宽度,垂直居中)

    DIV+CSS的布局已经让表格布局几乎很少用到,除非表格语义性很强的情况. display:table解决了一部分需要使用表格特性但又不需要表格语义的情况, 尤其是DIV+CSS很不方便解决的问题,比 ...

  4. HTML和CSS中一些有趣的

    CSS Rese http://yui.yahooapis.com/3.18.1/build/cssreset/cssreset-min.css <link rel="styleshe ...

  5. [转] python 远程主机强迫关闭了一个现有的连接 socket 超时设置 errno 10054

    python socket.error: [Errno 10054] 远程主机强迫关闭了一个现有的连接.问题解决方案: 前几天使用python读取网页.因为对一个网站大量的使用urlopen操作,所以 ...

  6. idea字体模糊

    用jdk1.8的jre替换idea的jre64,但是记得在lib里加上jdk的lib中的tools.jar. 如图: 然后 将原来jre64的TOOLS.jar拷贝到替换后的jre的lib目录下,重启 ...

  7. 第六十三课、C语言的异常处理

    http://www.cnblogs.com/gui-lin/p/6379101.html 一.异常处理 1.异常的概念 (1).程序在运行过程中可能产生异常 (2).异常(Exception)和Bu ...

  8. 【洛谷2152】[SDOI2009] SuperGCD(Python好题)

    点此看题面 大致题意: 给你两个长度\(\le10000\)的正整数,让你求它们的\(gcd\). Python​ 高精请绕道. 这题的正解应该是Python. 对于这种高精题,肯定是Python最方 ...

  9. 在TreeView控件节点中显示图片

    实现效果: 知识运用: TreeView控件中Nodes集合的Add方法 //创建节点并将节点放入集合中 public virtual TreeNode Add (string key,string ...

  10. 使用Xcode过程中遇到的问题

    前言:记录一下使用Xcode过程中遇到的问题 1.关于开发者的Team的问题,是选用自己的个人Team还是选用公司的付费的Team(本机环境:Xcode9 + iPad :iOS11.0.3) 问题: ...