OpenCV 之 Mat 类
数字图像可看作一个数值矩阵, 其中每个元素代表一个像素点,如下:
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),部分矩阵头如下:
int flags; // signaling the contents of the matrix
int dims; // dimensions
int rows, cols; // rows and columns
MatSize size; //
MatStep step; //
矩阵指针如下,指向包含所有像素值的矩阵
uchar* data; // pointer to the data
2.2 赋值算子
Mat 类中的赋值算子 "=" 和 拷贝构造函数,涉及的是浅拷贝,因此,当执行这两个操作时,仅仅是复制了矩阵头。
如果想要深拷贝,达到复制图像矩阵的目的,应使用 clone() 或 copyTo() 函数,如下图所示 (摘自参考资料 -- 4):
2.3 代码示例
简单验证如下,将矩阵 m3 由 copyTo() 函数复制给 m1,而 m2 由 m1 直接赋值,二者指向的是同样的数据。因此,如果改变了 m1,则 m2 对应的矩阵数值,也会进行相应的改变。
Mat m1(, , CV_32FC1, Scalar(1.1f) );
cout << "m1 = " << endl << " " << m1 << endl << endl;
// using assign operator
Mat m2 = m1;
cout << "m2 = " << endl << " " << m2 << endl << endl; Mat m3(, 3, CV_32FC1, Scalar(3.3f) );
m3.copyTo(m1);
cout << "m1 = " << endl << " " << m1 << endl << endl;
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)
第一个 {} 表示数据的类型:
CV_8U - 8-bit 无符号整数 ( 0..255 )
CV_8S - 8-bit 有符号整数 ( -128..127 )
CV_16U - 16-bit 无符号整数 ( 0..65535 )
CV_16S - 16-bit 有符号整数 ( -32768..32767 )
CV_32S - 32-bit 有符号整数 ( -2147483648..2147483647 )
CV_32F - 32-bit 浮点数 ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit 浮点数 ( -DBL_MAX..DBL_MAX, INF, NAN )
第二个 {} 或 (n),表示的是通道:
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
Mat m(, , CV_32FC3, Scalar(1.1f, 2.2f, 3.3f) );
cout << "m = " << endl << " " << m << endl << endl;
输出的矩阵如下:
3.2.2 create 函数
使用 Mat() + create() + setTo(),也可以构建如上的数值矩阵
Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
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
m.setTo(Scalar(1.1f, 2.2f,3.3f));
cout << "m = " << endl << " " << m << endl << endl;
3.2.3 特殊矩阵
单位矩阵 (ones),对角矩阵 (eye),零矩阵 (zeros),如下所示:
// 单位矩阵
Mat O = Mat::ones(, , CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
// 零矩阵
Mat Z = Mat::zeros(, , CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
// 对角矩阵
Mat E = Mat::eye(, , CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
4 Mat 遍历
4.1 at<>() 函数
常用来遍历 Mat 元素的基本函数为 at<>(),其中 <> 内的数据类型,取决于 Mat 中元素的数据类型,二者的对应关系如下:
CV_8U -- Mat.at<uchar>(y,x)
CV_8S -- Mat.at<schar>(y,x)
CV_16U -- Mat.at<ushort>(y,x)
CV_16S -- Mat.at<short>(y,x)
CV_32S -- Mat.at<int>(y,x)
CV_32F -- Mat.at<float>(y,x)
CV_64F -- Mat.at<double>(y,x)
简单的遍历如下,使用了 Qt 的 qDebug() 来显示输出
Mat m1 = Mat::eye(, , CV_32FC1);
// use qDebug()
qDebug() << "Element (3,3) is : " << m1.at<float>(,); 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 高效遍历
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
int channels = I.channels();
int nRows = I.rows;
int nCols = I.cols * channels;
if (I.isContinuous())
{
nCols *= nRows;
nRows = ;
}
int i,j;
uchar* p;
for(i=; i<nRows; ++i)
{
p = I.ptr<uchar>(i);
for (j = ; j<nCols; ++j)
{
p[j] = table[p[j]];
}
}
return I;
}
4.2.2 迭代器遍历
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U);
const int channels = I.channels();
switch(channels)
{
case :
{
MatIterator_<uchar> it, end;
for(it=I.begin<uchar>(), end=I.end<uchar>(); it!=end; ++it)
*it = table[*it];
break;
}
case :
{
MatIterator_<Vec3b> it, end;
for(it=I.begin<Vec3b>(), end=I.end<Vec3b>(); it!=end; ++it)
{
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
(*it)[] = table[(*it)[]];
}
}
}
return I;
}
4.2.3 耗时计算
比较上面两种方法的耗时,可使用如下代码来进行计算:
double t = (double)getTickCount();
// do something ...
t = ((double)getTickCount() - t)/getTickFrequency();
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
OpenCV 之 Mat 类的更多相关文章
- opencv中mat类介绍
The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It ...
- OpenCV之Mat类使用总结
#前言 Mat 是Opencv中很常用的一个图像容器类,图像在计算机中的存储形式是二进制字节流,其本质的存储形式如下图所示: 而一张图片是由很多像素点组成,单个像素点又会因为图像格式的不同而不同.例如 ...
- OpenCV cv::Mat类
using namespace cv; 1.Mat的声明: Mat m=Mat(rows, cols, type); Mat m=Mat(Size(width,height), type); type ...
- opencv关于Mat类中的Scalar()---颜色赋值
这个 CvScalar就是一个可以用来存放4个double数值的数组(O'Reilly的书上写的是4个整型成员):一般用来存放像素值(不一定是灰度值哦)的,最多可以存放4个通道的. typedef s ...
- 【视频开发】OpenCV中Mat,图像二维指针和CxImage类的转换
在做图像处理中,常用的函数接口有OpenCV中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...
- vector类转换Mat类
前言 一个个数据push back到vector之后,可以使用Mat()函数将vector类型转换为Mat类型. 在opencv中Mat类的构造函数中有一个构造函数可以直接把vector类转换为Mat ...
- 图像识别与OpenCV——Mat类与Mat_类的内存管理
Mat_类是对Mat类的一个包装,其定义如下: template<typename _Tp> class Mat_ : public Mat { public: //只定义了几个方法 // ...
- 如何将OpenCV中的Mat类绑定为OpenGL中的纹理
https://blog.csdn.net/TTTTzTTTT/article/details/53456324 如果要调用外接的USB摄像头获取图像通常使用OpenCV来调用,如何调用摄像头请参考本 ...
- opencv学习之路(4)、Mat类介绍,基本绘图函数
一.Mat类创建 Mat img;//创建无初始化矩阵 Mat img1(,,CV_8UC1);//200行,100列(长200,宽100) Mat img2(Size(,),CV_8UC3,Scal ...
随机推荐
- python报错“UnicodeEncodeError: 'ascii' codec can't encode characters in position 22-26: ordinal not in range(128)”问题解决
方案是在python的Lib\site-packages文件夹下新建一个sitecustomize.py,内容为: # encoding=utf8 import sys reload(sys) sys ...
- 这可能是最low的发布dotnet core站点到centos7
前言 不得不说:我在chrome上写了好长一段,贴了23张图,然后一个crash..我想说我电脑上的chrome已经crash太多次了 以后一定要搞离线编辑的. 正文 什么是.net core,bal ...
- 第三章(jQuery中的DOM操作)
3.1 DOM 操作分类 ①DOM Core 包括(getElementById() , getElementsByTagName() , getAttribute() , setAttribute( ...
- 前端如何将H5页面打包成本地app?
大家都知道H5页面怎么在手机浏览器里测试,但是如何能打包成APP测试呢?因为本人的手机是IOS系统,如果用Xcode打包需要开发证书,貌似99美元一年.意外发现一个叫Hbuild的软件,简单好用,下面 ...
- 以芯片直读方式得到的全盘镜像解析及ext4日志区域解析
之前在centos中分析了/dev/sda1下的结构,但当对象是一块以芯片直读方式作出来的全盘镜像呢? 这次以安卓手机的全盘镜像为对象,尝试按照ext4文件系统结构手动解析,加强对ext4文件系统.E ...
- MinGW(GCC)编译DLL文件
这两天用CB(Code::Blocks)写个小程序,要编译出DLL供VB(6)使用.CB使用mingw-gcc作为编译器,在库文件的产出上跟VC.VS之类的IDE略有不同. 由于C语言的基础知识不是太 ...
- Carbondata源码系列(一)文件生成过程
在滴滴的两年一直在加班,人也变懒了,就很少再写博客了,最近在进行Carbondata和hive集成方面的工作,于是乎需要对Carbondata进行深入的研究. 于是新开一个系列,记录自己学习Carbo ...
- RabbitMQ知多少
1.引言 RabbitMQ--Rabbit Message Queue的简写,但不能仅仅理解其为消息队列,消息代理更合适.RabbitMQ 是一个由 Erlang 语言开发的AMQP(高级消息队列协议 ...
- es6的新内容
前端学习总结(十八)ES6--新一代的javascript 发表于2016/6/11 21:44:27 2733人阅读 分类: javascript 简介 ECMAScript 6(以下简称ES6) ...
- CSS 样式书写规范+特殊符号
虽然我只是刚踏入web前端开发圈子.在一次次任务里头,我发觉每一次的css命名都有所不同和不知所措.脑海就诞生了一个想法--模仿大神的css命名样式. 毕竟日后工作上,是需要多个成员共同协作的.如果没 ...