数字图像可看作一个数值矩阵, 其中每个元素代表一个像素点,如下:

OpenCV 中,用 Mat 表示数值矩阵,Mat 是很关键的一种数据结构,因为 OpenCV 中的大部分函数都和 Mat 有关:

有的是 Mat 的成员函数;有的把 Mat 作为参数;还有的将 Mat 作为返回值。

1  Mat 简介

Mat 表示的是 N 维稠密矩阵,与稠密矩阵相对的是稀疏矩阵(只存储非零的像素值),后者常用于直方图处理中,OpenCV 中对应为 cv::SparseMat

如下所示:第一个为稠密矩阵的存储方式,存储所有的像素数值;第二个为稀疏矩阵的存储方式,只存储非零的像素值

$\quad \begin{bmatrix} 0 & 2 & 0 \\ 1 & 0 & 1 \\ 0 & 2 & 0 \end{bmatrix} $        $\quad \begin{bmatrix}  & 2 &  \\ 1 &  & 1 \\  & 2 &  \end{bmatrix} $

当 N=1 时,所有像素存储为一行;当 N=2 时,所有像素按照一行行的顺序排列;当 N=3 时,所有像素按照一面面的顺序排列,其中一行行的像素构成一个平面。

下图左,为灰度图的存储方式;图右,为 RGB 图像的存储方式,注意其存储顺序为 BGR (Blue->Green->Red)

    

2  Mat 特点

2.1  组成

Mat 类包含两部分,一是 矩阵头 (matrix header),二是 矩阵指针 (pointer to matrix),部分矩阵头如下:

  1. int flags; // signaling the contents of the matrix
  2. int dims; // dimensions
  3. int rows, cols; // rows and columns
  4. MatSize size; //
  5. MatStep step; //

矩阵指针如下,指向包含所有像素值的矩阵

  1. uchar* data; // pointer to the data

2.2  赋值算子

Mat 类中的赋值算子 "=" 和 拷贝构造函数,涉及的是浅拷贝,因此,当执行这两个操作时,仅仅是复制了矩阵头。

如果想要深拷贝,达到复制图像矩阵的目的,应使用 clone() 或 copyTo() 函数,如下图所示 (摘自参考资料 -- 4):

2.3  代码示例

简单验证如下,将矩阵 m3 由 copyTo() 函数复制给 m1,而 m2 由 m1 直接赋值,二者指向的是同样的数据。因此,如果改变了 m1,则 m2 对应的矩阵数值,也会进行相应的改变。

  1. Mat m1(, , CV_32FC1, Scalar(1.1f) );
  2. cout << "m1 = " << endl << " " << m1 << endl << endl;
    // using assign operator
  3. Mat m2 = m1;
  4. cout << "m2 = " << endl << " " << m2 << endl << endl;
  5.  
  6. Mat m3(, 3, CV_32FC1, Scalar(3.3f) );
  7. m3.copyTo(m1);
  8. cout << "m1 = " << endl << " " << m1 << endl << endl;
  9. cout << "m2 = " << endl << " " << m2 << endl << endl;

3  Mat 创建

3.1  数据类型

在创建 Mat 之前,首先了解 Mat 中元素的数据类型,其格式为 CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2, 3} 或 CV_{8U, 16S, 16U, 32S, 32F, 64F}C(n)

第一个 {} 表示数据的类型:

  1. CV_8U - 8-bit 无符号整数 ( 0..255 )
  2. CV_8S - 8-bit 有符号整数 ( -128..127 )
  3. CV_16U - 16-bit 无符号整数 ( 0..65535 )
  4. CV_16S - 16-bit 有符号整数 ( -32768..32767 )
  5. CV_32S - 32-bit 有符号整数 ( -2147483648..2147483647 )
  6. CV_32F - 32-bit 浮点数 ( -FLT_MAX..FLT_MAX, INF, NAN )
  7. CV_64F - 64-bit 浮点数 ( -DBL_MAX..DBL_MAX, INF, NAN )

第二个 {} 或 (n),表示的是通道:

  1. CV_8UC3 等价于 CV_8UC(3) - 3通道 8-bit 无符号整数

3.2  创建方式

3.2.1  构造函数

创建一个 3 行 5 列,3 通道 32 位,浮点型的矩阵,通道 1, 2, 3 的值分别为 1.1f,2.2f,3.3f

  1. Mat m(, , CV_32FC3, Scalar(1.1f, 2.2f, 3.3f) );
  2. cout << "m = " << endl << " " << m << endl << endl;

输出的矩阵如下:

3.2.2  create 函数

使用 Mat() + create() + setTo(),也可以构建如上的数值矩阵

  1. Mat m;
    // Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
  2. m.create(,,CV_32FC3);
    // Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0
  3. m.setTo(Scalar(1.1f, 2.2f,3.3f));
  4. cout << "m = " << endl << " " << m << endl << endl;

3.2.3  特殊矩阵

单位矩阵 (ones),对角矩阵 (eye),零矩阵 (zeros),如下所示:

  1. // 单位矩阵
  2. Mat O = Mat::ones(, , CV_32F);
  3. cout << "O = " << endl << " " << O << endl << endl;
  4. // 零矩阵
  5. Mat Z = Mat::zeros(, , CV_8UC1);
  6. cout << "Z = " << endl << " " << Z << endl << endl;
  7. // 对角矩阵
  8. Mat E = Mat::eye(, , CV_64F);
  9. cout << "E = " << endl << " " << E << endl << endl;

4  Mat 遍历

4.1  at<>() 函数

常用来遍历 Mat 元素的基本函数为 at<>(),其中 <> 内的数据类型,取决于 Mat 中元素的数据类型,二者的对应关系如下:

  1. CV_8U -- Mat.at<uchar>(y,x)
  2. CV_8S -- Mat.at<schar>(y,x)
  3. CV_16U -- Mat.at<ushort>(y,x)
  4. CV_16S -- Mat.at<short>(y,x)
  5. CV_32S -- Mat.at<int>(y,x)
  6. CV_32F -- Mat.at<float>(y,x)
  7. CV_64F -- Mat.at<double>(y,x)

简单的遍历如下,使用了 Qt 的 qDebug() 来显示输出

  1. Mat m1 = Mat::eye(, , CV_32FC1);
    // use qDebug()
  2. qDebug() << "Element (3,3) is : " << m1.at<float>(,);
  3.  
  4. Mat m2 = Mat::eye(, , CV_32FC2);
    // use qDebug()
    qDebug() << "Element (3,3) is " << m2.at<cv::Vec2f>(,)[] << "," << m2.at<cv::Vec2f>(,)[];

注意:at<>() 函数中 () 内,行索引号在前,列索引号在后,也即 (y, x)

4.2  遍历方式

4.2.1  高效遍历

  1. Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
  2. {
  3. // accept only char type matrices
  4. CV_Assert(I.depth() == CV_8U);
  5. int channels = I.channels();
  6. int nRows = I.rows;
  7. int nCols = I.cols * channels;
  8. if (I.isContinuous())
  9. {
  10. nCols *= nRows;
  11. nRows = ;
  12. }
  13. int i,j;
  14. uchar* p;
  15. for(i=; i<nRows; ++i)
  16. {
  17. p = I.ptr<uchar>(i);
  18. for (j = ; j<nCols; ++j)
  19. {
  20. p[j] = table[p[j]];
  21. }
  22. }
  23. return I;
  24. }

4.2.2  迭代器遍历

  1. Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
  2. {
  3. // accept only char type matrices
  4. CV_Assert(I.depth() == CV_8U);
  5. const int channels = I.channels();
  6. switch(channels)
  7. {
  8. case :
  9. {
  10. MatIterator_<uchar> it, end;
  11. for(it=I.begin<uchar>(), end=I.end<uchar>(); it!=end; ++it)
  12. *it = table[*it];
  13. break;
  14. }
  15. case :
  16. {
  17. MatIterator_<Vec3b> it, end;
  18. for(it=I.begin<Vec3b>(), end=I.end<Vec3b>(); it!=end; ++it)
  19. {
  20. (*it)[] = table[(*it)[]];
  21. (*it)[] = table[(*it)[]];
  22. (*it)[] = table[(*it)[]];
  23. }
  24. }
  25. }
  26. return I;
  27. }

4.2.3  耗时计算

比较上面两种方法的耗时,可使用如下代码来进行计算:

  1. double t = (double)getTickCount();
  2. // do something ...
  3. t = ((double)getTickCount() - t)/getTickFrequency();
  4. qDebug() << "Times passed in seconds: " << t << endl; // using qDebug()

参考资料:

1.  <Learning OpenCV3> chapter 4

2.  OpenCV Tutorials / The Core Functionality (core module) / Mat - The Basic Image Container

3.  OpenCV Tutorials / The Core Functionality (core module) / How to scan images, lookup tables and time measurement with OpenCV

4.  OpenCV基础篇之Mat数据结构

OpenCV 之 Mat 类的更多相关文章

  1. opencv中mat类介绍

    The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It ...

  2. OpenCV之Mat类使用总结

    #前言 Mat 是Opencv中很常用的一个图像容器类,图像在计算机中的存储形式是二进制字节流,其本质的存储形式如下图所示: 而一张图片是由很多像素点组成,单个像素点又会因为图像格式的不同而不同.例如 ...

  3. OpenCV cv::Mat类

    using namespace cv; 1.Mat的声明: Mat m=Mat(rows, cols, type); Mat m=Mat(Size(width,height), type); type ...

  4. opencv关于Mat类中的Scalar()---颜色赋值

    这个 CvScalar就是一个可以用来存放4个double数值的数组(O'Reilly的书上写的是4个整型成员):一般用来存放像素值(不一定是灰度值哦)的,最多可以存放4个通道的. typedef s ...

  5. 【视频开发】OpenCV中Mat,图像二维指针和CxImage类的转换

    在做图像处理中,常用的函数接口有OpenCV中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...

  6. vector类转换Mat类

    前言 一个个数据push back到vector之后,可以使用Mat()函数将vector类型转换为Mat类型. 在opencv中Mat类的构造函数中有一个构造函数可以直接把vector类转换为Mat ...

  7. 图像识别与OpenCV——Mat类与Mat_类的内存管理

    Mat_类是对Mat类的一个包装,其定义如下: template<typename _Tp> class Mat_ : public Mat { public: //只定义了几个方法 // ...

  8. 如何将OpenCV中的Mat类绑定为OpenGL中的纹理

    https://blog.csdn.net/TTTTzTTTT/article/details/53456324 如果要调用外接的USB摄像头获取图像通常使用OpenCV来调用,如何调用摄像头请参考本 ...

  9. opencv学习之路(4)、Mat类介绍,基本绘图函数

    一.Mat类创建 Mat img;//创建无初始化矩阵 Mat img1(,,CV_8UC1);//200行,100列(长200,宽100) Mat img2(Size(,),CV_8UC3,Scal ...

随机推荐

  1. css3特效详解

    好吧,CSS3 3D transform变换,不过如此! 这篇文章发布于 2012年09月7日,星期五,01:05,归类于 css相关. 阅读 408042 次, 今日 34 次 一.写在前面的秋裤 ...

  2. springboot(十四):springboot整合shiro-登录认证和权限管理

    这篇文章我们来学习如何使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求.在Java领域一般有Spring Security ...

  3. Maven转化为Dynamic Web Module

    如今Maven仍然是最常用的项目管理工具,若要将Java Web项目使用Maven进行管理,则首先需要新建Maven项目,然后将其转化为web项目. 在项目右键选择properties,然后点击左侧P ...

  4. go语言返回变量存储地址

    package main import "fmt" func main() { e:= fmt.Println(e) fmt.Println(&e) //&e; 将 ...

  5. Swift初始化空字符串

    为了构造一个很长的字符串,可以创建一个空字符串作为初始值.可以将空的字符串字面量赋值给变量,也可以初始化一个新的String 实例: var emptyString = "" // ...

  6. 基于 HTML5 WebGL 的 3D 网络拓扑图

    在数据量很大的2D 场景下,要找到具体的模型比较困难,并且只能显示出模型的的某一部分,显示也不够直观,这种时候能快速搭建出 3D 场景就有很大需求了.但是搭建 3D 应用场景又依赖于通过 3ds Ma ...

  7. Unity应用架构设计(13)——日志组件的实施

    对于应用程序而言,日志是非常重要的功能,通过日志,我们可以跟踪应用程序的数据状态,记录Crash的日志可以帮助我们分析应用程序崩溃的原因,我们甚至可以通过日志来进行性能的监控.总之,日志的好处很多,特 ...

  8. 浅谈关于特征选择算法与Relief的实现

    一. 背景 1) 问题 在机器学习的实际应用中,特征数量可能较多,其中可能存在不相关的特征,特征之间也可能存在相关性,容易导致如下的后果: 1.     特征个数越多,分析特征.训练模型所需的时间就越 ...

  9. VB6之截图

    今天先把主要逻辑写出来,如果有时间就实现一个真正的截图工具. Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC ...

  10. 轻量级代码生成器-OnlyCoder 第二篇

    最近利用业余时间将OnlyCoder又重新打造了一番,使其使用起来更简单.更顺手. 相关的帮助文档也已发布了版本. 界面改版,UI采用了DotNetBar2组件. 还是先看下UI效果吧. 由于使用了  ...