OpenCV Mat 简介
Part I
-
Mat是OpenCV最基本的数据结构,Mat即矩阵(Matrix)的缩写,Mat数据结构主要包含2部分:Header和Pointer。Header中主要包含矩阵的大小,存储方式,存储地址等信息;Pointer中存储指向像素值的指针。我们在读取图片的时候就是将图片定义为Mat类型,其重载的构造函数一大堆,
class CV_EXPORTS Mat { public: //! default constructor Mat(); //! constructs 2D matrix of the specified size and type // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) Mat(int _rows, int _cols, int _type); Mat(Size _size, int _type); //! constucts 2D matrix and fills it with the specified value _s. Mat(int _rows, int _cols, int _type, const Scalar& _s); Mat(Size _size, int _type, const Scalar& _s); //! constructs n-dimensional matrix Mat(int _ndims, const int* _sizes, int _type); Mat(int _ndims, const int* _sizes, int _type, const Scalar& _s); //! copy constructor Mat(const Mat& m); //! constructor for matrix headers pointing to user-allocated data Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP); Mat(Size _size, int _type, void* _data, size_t _step=AUTO_STEP); Mat(int _ndims, const int* _sizes, int _type, void* _data, const size_t* _steps=0); //! creates a matrix header for a part of the bigger matrix Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()); Mat(const Mat& m, const Rect& roi); Mat(const Mat& m, const Range* ranges); //! converts old-style CvMat to the new matrix; the data is not copied by default Mat(const CvMat* m, bool copyData=false); //! converts old-style CvMatND to the new matrix; the data is not copied by default Mat(const CvMatND* m, bool copyData=false); //! converts old-style IplImage to the new matrix; the data is not copied by default Mat(const IplImage* img, bool copyData=false); ...... }
要了解如何初始化Mat结构,就应该了解它的构造函数,比如程序中的第一初始化方式调用额就是
Mat(int _rows, int _cols, int _type, const Scalar& _s);
这个构造函数。
IplImage*是C语言操作OpenCV的数据结构,在当时C操纵OpenCV的时候,地位等同于Mat,OpenCV为其提供了一个接口,很方便的直接将IplImage转化为Mat,即使用构造函数
Mat(const IplImage* img, bool copyData=false);
上面程序中的第二种方法就是使用的这个构造函数。
-
关于Mat数据复制:前面说过Mat包括头和数据指针,当使用Mat的构造函数初始化的时候,会将头和数据指针复制(注意:只是指针复制,指针指向的地址不会复制),若要将数据也复制,则必须使用copyTo或clone函数
-
Mat还有几个常用的成员函数,在之后的文章中将会使用到:
//! returns true iff the matrix data is continuous // (i.e. when there are no gaps between successive rows). // similar to CV_IS_MAT_CONT(cvmat->type) bool isContinuous() const;
这了解上面的函数作用前,得了解下OpenCV中存储像素的方法,如下,灰度图(单通道)存储按行列存储,
三通道RGB存储方式如下,每列含有三个通道,
为了加快访问的速度,openCV往往会在内存中将像素数据连续地存储成一行,isContinus()函数的作用就是用于判断是否连续存储成一行。存储成一行有什么好处呢?给定这行的头指针p,则只要使用p++操作就能逐个访问数据。
因此当判断存放在一行的时候,可以通过数据指针++很容易遍历图像像素:
long nRows = M.rows * M.channels(); // channels()也是Mat中一个常用的函数,用于获取通道数(RGB=3,灰度=1) long nCols = M.cols; uchar *p = M.data; // 数据指针 if(M.isContinuous()) { nCols *= nRows; for (long i=0; i < nCols; i++) { *p++ = ...; // 像素赋值或读取操作 } }
请注意以上几个常用的Mat成员遍历和函数:
M.row; // 返回图像行数 M.nCols; // 返回图像列数 M.channels(); //返回通道数 M.isContinuous(); // 返回bool类型表示是否连续存储
程序示例
/* * FileName : MatObj.cpp * Author : xiahouzuoxin @163.com * Version : v1.0 * Date : Thu 15 May 2014 09:12:45 PM CST * Brief : * * Copyright (C) MICL,USTB */ #include <cv.h> #include <highgui.h> #include <iostream> using namespace std; using namespace cv; int main(void) { /* * Create Mat */ Mat M(2,2,CV_8UC3, Scalar(0,0,255)); cout << "M=" << endl << " " << M << endl << endl; /* * Matlab style */ Mat E = Mat::eye(4,4,CV_64F); cout << "E=" << endl << " " << E << endl << endl; E = Mat::ones(4,4,CV_64F); cout << "E=" << endl << " " << E << endl << endl; E = Mat::zeros(4,4,CV_64F); cout << "E=" << endl << " " << E << endl << endl; /* * Convert IplImage to Mat */ IplImage *img = cvLoadImage("../test_imgs/Lena.jpg"); Mat L(img); namedWindow("Lena.jpg", CV_WINDOW_AUTOSIZE); imshow("Lena.jpg", L); waitKey(0); /* * Init Mat with separated data */ Mat C = (Mat_<int>(3,3) << 0,1,2,3,4,5,6,7,8); cout << "C=" << endl << " " << C << endl << endl; return 0; }
左边是矩阵的一些操作输出结果,右边的图是通过IplImage *结构读入,转换为Mat后显示结果。
Part II
Mat的作用
The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ).
上面的一段话引用自官方的文档,Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵,灰度或彩色图像,立体元素,点云,张量以及直方图(高维的直方图使用SparseMat保存比较好)。简而言之,Mat就是用来保存多维的矩阵的。
Mat的常见属性
- data uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。
- dims 矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.
- rows 矩阵的行数
- cols 矩阵的列数
- size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
- channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
下面的几个属性是和Mat中元素的数据类型相关的。
- type
表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:
这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。CV_8UC1 CV_8UC2 CV_8UC3 CV_8UC4 CV_8SC1 CV_8SC2 CV_8SC3 CV_8SC4 CV_16UC1 CV_16UC2 CV_16UC3 CV_16UC4 CV_16SC1 CV_16SC2 CV_16SC3 CV_16SC4 CV_32SC1 CV_32SC2 CV_32SC3 CV_32SC4 CV_32FC1 CV_32FC2 CV_32FC3 CV_32FC4 CV_64FC1 CV_64FC2 CV_64FC3 CV_64FC4
例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2.
C1,C2,C3,C4则表示通道是1,2,3,4
type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth - depth
矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值,
将type的预定义值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F - elemSize
矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes - elemSize1
矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels
下面是一个示例程序,具体说明Mat的各个属性:
Mat img(3, 4, CV_16UC4, Scalar_<uchar>(1, 2, 3, 4)); cout << img << endl; cout << "dims:" << img.dims << endl; cout << "rows:" << img.rows << endl; cout << "cols:" << img.cols << endl; cout << "channels:" << img.channels() << endl; cout << "type:" << img.type() << endl; cout << "depth:" << img.depth() << endl; cout << "elemSize:" << img.elemSize() << endl; cout << "elemSize1:" << img.elemSize1() << endl;
首先创建了一个3*4的具有4个通道的矩阵,其元素类型是CV_16U。Scalar_是一个模板向量,用来初始化矩阵的每个像素,因为矩阵具有4个通道,Scalar_有四个值。其运行结果:
运行结果首先打印了Mat中的矩阵,接着是Mat的各个属性。注意其type = 26,而depth = 2。这是由于上面所说的各种预定义类型
例如,CV_16UC4,CV_8U是一些预定义的常量。
step
Mat中的step是一个MStep的一个实例。其声明如下:
struct CV_EXPORTS MStep { MStep(); MStep(size_t s); const size_t& operator[](int i) const; size_t& operator[](int i); operator size_t() const; MStep& operator = (size_t s); size_t* p; size_t buf[2]; protected: MStep& operator = (const MStep&); };
从其声明中可以看出,MStep和size_t有比较深的关系。用size_t作为参数的构造函数和重载的赋值运算符
MStep(size_t s); MStep& operator = (size_t s);
向size_t的类型转换以及重载的[ ]运算符返回size_t
const size_t& operator[](int i) const; size_t& operator[](int i);
size_t的数组以及指针
size_t* p; size_t buf[2];
那么size_t又是什么呢,看代码
typedef unsigned int size_t;
size_t就是无符号整数。
再看一下MStep的构造函数,就可以知道其究竟保存的是什么了。
inline Mat::MStep::MStep(size_t s) { p = buf; p[0] = s; p[1] = 0; }
从MStep的定义可以知道,buff是一个size_t[2],而p是size_t *,也就是可以把MStep看做一个size_t[2]。那么step中保存的这个size_t[2]和Mat中的数据有何种关系呢。
step[0]是矩阵中一行元素的字节数。
step[1]是矩阵中一个元素的自己数,也就是和上面所说的elemSize相等。
上面说到,Mat中一个uchar* data指向矩阵数据的首地址,而现在又知道了每一行和每一个元素的数据大小,就可以快速的访问Mat中的任意元素了。下面公式:
step1
规整化的step,值为step / elemSize1。 定义如下:
inline size_t Mat::step1(int i) const { return step.p[i]/elemSize1(); }
仍以上例代码中定义的img为例,来看下step,step1具体的值:
img(3*4)的type是CV_16UC4,step[0]是其一行所占的数据字节数4 *4 * 16 / 8 = 32.
step[1] 是一个元素所占的字节数,img的一个元素具有4个通道,故:4 * 16 / 8 = 2
step1 = step / elemSize1,elemSize1是元素的每个通道所占的字节数。
N维的step(N > 2)
上面分析step是一个size_t[2],实际不是很正确,正确的来说step应该是size_t[dims],dims是Mat的维度,所以对于上面的二维的Mat来说,step是size_t[2]也是正确的。
下面就对三维的Mat数据布局以及step(维度大于3的就算了吧)。
上图引用自http://ggicci.blog.163.com/blog/static/210364096201261052543349/ 搜集资料时发现了这幅图,一切就变的简单了 感谢作者Ggicci
三维的数据在Mat中是按面来存储的,上图描述的很清晰,这里不再多说。
上面言道,step是一个size_t[dims],dims是维度。so,三维的step就是size_t[3]。其余的不多说了,看图就有了。下面来创建一个三维的Mat,实际看看
int dims[3] = { 3, 3, 3 }; Mat src(3, dims, CV_16SC2, Scalar_<short>(1,2)); cout << "step[0]:" << src.step[0] << endl; cout << "step[1]:" << src.step[1] << endl; cout << "step[2]:" << src.step[2] << endl;
首先创建一个3*3*3,depth为CV_16S的两通道的Mat
step[0]是一个数据面的大小 3 * 3 * (16 / 8 ) * 2 = 36
step[1]是一行数据的大小 3 * (16 / 8 ) * 2 = 12
step[2]是一个元素的大小 2 * (16 / 8) = 4
PS: 三维的Mat 不能使用 <<运算符进行输出的。
Part III
- 综述
Mat类可以被看做是OpenCV中C++版本的矩阵类,替代原来C版本的矩阵结构体CvMat和图像结构体IplImage;
Mat最大的优势跟STL的兼容性很好,有很多类似于STL的操作。但是Mat远远强于后者;
Mat是一种高效的数据类型,它对内存进行动态的管理,不需要之前用户手动的管理内存。
- Mat类定义
Mat类定义于core.hpp中,主要包含有两部分数据:一部分是矩阵头(matrix header),这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等等;另一个部分是一个指向矩阵包含像素值的指针(data)。Mat类的定义如下:
- <span style="font-size:12px;">class CV_EXPORTS Mat
- {
- public:
- // ... a lot of methods ...
- ...
- /*! includes several bit-fields:
- - the magic signature
- - continuity flag
- - depth
- - number of channels
- */
- int flags;
- //! the array dimensionality, >= 2
- int dims;
- //! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions
- int rows, cols;
- //! pointer to the data
- uchar* data;
- //! pointer to the reference counter;
- // when array points to user-allocated data, the pointer is NULL
- int* refcount;
- // other members
- ...
- };</span>
- Mat数据类型
Mat的存储是逐行存储的,矩阵中的数据类型包括:Mat_<uchar>对应的是CV_8U,Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,Mat_<double>对应的是CV_64F,对应的数据深度如下:
• CV_8U - 8-bit unsigned integers ( 0..255 )
• CV_8S - 8-bit signed integers ( -128..127 )
• CV_16U - 16-bit unsigned integers ( 0..65535 )
• CV_16S - 16-bit signed integers ( -32768..32767 )
• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
- 创建Mat矩阵
- <span style="font-size:12px;">//1.使用构造函数,常见的几个如下:
- Mat::Mat(); //default
- Mat::Mat(int rows, int cols, int type);
- Mat::Mat(Size size, int type);
- Mat::Mat(int rows, int cols, int type, const Scalar& s);
- Mat::Mat(Size size, int type, const Scalar& s);
- Mat::Mat(const Mat& m);
- //参数说明:
- //int rows:高
- //int cols:宽
- //int type:参见"Mat类型定义"
- //Size size:矩阵尺寸,注意宽和高的顺序:Size(cols, rows)
- //const Scalar& s:用于初始化矩阵元素的数值
- //const Mat& m:拷贝m的矩阵头给新的Mat对象,但是不复制数据!相当于创建了m的一个引用对象
- //例子1:创建100*90的矩阵,矩阵元素为3通道32位浮点型
- cv::Mat M(100, 90, CV_32FC3);
- //例子2:使用一维或多维数组来初始化矩阵,
- double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
- cv::Mat M = cv::Mat(3, 3, CV_64F, m);
- //2.使用create函数:
- Mat a = create(10, 9, CV_16U); //创建10*9的矩阵,矩阵元素为16位无符号整型
- //create的一个特殊用法:如果初始化的时候没有传入size的参数,或者后面需要改变size的参数,可以使用create来调整
- // make 7x7 complex matrix filled with 1+3j.
- cv::Mat M(7,7,CV_32FC2,Scalar(1,3));
- // and now turn M to 100x60 15-channel 8-bit matrix.
- // The old content will be deallocated:隐式使用release()释放
- M.create(100,60,CV_8UC(15));</span>
- 复制矩阵
- 合并矩阵
- //假设现有需要将n个CvMat类型的矩阵(每个都是1*m的行向量)合并到一个n*m(n行m列)的矩阵中,即:矩阵合并问题。
- CvMat* vector; //行向量:1*m
- Mat dst; //目标矩阵:n*m
- Mat vectorMat = Mat(vector, true); //将CvMat转为Mat
- Mat tmpMat = M.row(i); //注意:浅拷贝(参见"复制矩阵")
- vectorMat.copyTo(tmpMat); //注意:深拷贝(此时tmpMat已改变为vectorMat,即:目标矩阵的第i+1行被赋值为vectorMat)
- //以上为改变目标矩阵的i+1行的值,以此类推即可将n个行向量合并到目标矩阵中。
- 访问矩阵元素
- //1.at函数
- M.at<double>(i,j) += 1.f; //set (i,j)-th element
- int a = M.at<int>(i,j); //get (i,j)-th element
- //2.矩阵元素指针(ptr)
- for(int i = 0; i < M.rows; i++)
- {
- const double* pData = M.ptr<double>(i); //第i+1行的所有元素
- for(int j = 0; j < M.cols; j++)
- cout<<pData[j]<<endl;
- }
- Mat类成员函数详解
Mat::eye
返回一个恒等指定大小和类型矩阵。
C++: static MatExpr Mat::eye(int rows, int cols, inttype)
C++: static MatExpr Mat::eye(Size size, int type)
参数
rows –的行数。
cols– 的列数。
size –替代矩阵大小规格Size(cols, rows)的方法。
type – 创建的矩阵的类型。
该方法返回 Matlab 式恒等矩阵初始值设定项,类似 Mat::zeros()和 Mat::ones(),你可以用缩放操作高效地创建缩放的恒等矩阵:
/ / 创建4 x 4 的对角矩阵并在对角线上以0.1的比率缩小。
Mat A = Mat::eye(4, 4, CV_32F)*0.1;
Mat::create
分配新的阵列数据 (如果需要)。
C++: void Mat::create(int rows, int cols, int type)
C++: void Mat::create(Size size, int type)
C++: void Mat::create(int ndims, const int* sizes, inttype)
参数
ndims – 新数组的维数。
rows –新的行数。
cols – 新的列数。
size – 替代新矩阵大小规格:Size(cols, rows)。
sizes – 指定一个新的阵列形状的整数数组。
type – 新矩阵的类型。
这是关键的Mat方法之一。大多数新样式 OpenCV 函数和产生阵列的方法每个输出数组都调用这个方法。此方法使用如下算法:
1.如果当前数组形状和类型匹配新的请立即返回。否则,通过调用 Mat::release()取消引用以前的数据。
2.初始化新矩阵头。
3.分配新的 total()*elemSize() 个字节的数据空间。
4.分配新的关联数据的引用计数并将其设置为 1。
这项计划使内存管理强大高效同时还为用户减少了额外输入。这意味着通常不需要显式分配输出数组。也就是说,可以不写成:
Mat color;
...
Mat gray(color.rows, color.cols,color.depth());
cvtColor(color, gray, CV_BGR2GRAY);
而写成:
Mat color;
...
Mat gray;
cvtColor(color, gray, CV_BGR2GRAY);
因为 cvtColor,与大多数 OpenCV 函数相同,在输出数组时内部调用Mat::create()。
Mat::addref
计数器参考。
C++: void Mat::addref()
该方法递增与矩阵数据关联的引用计数。如果矩阵头指向外部的数据集(见 Mat::Mat()),则引用计数为 NULL,并且该方法在这种情况下不起作用。通常情况下,为避免内存泄漏,不应显式调用该方法。它是由该矩阵赋值运算符隐式调用。在支持的它平台上,引用计数器递增是一个原子操作。因此,对相同的矩阵,在不同的线程异步操作是安全的。
Mat::release
在必要的情况下,递减引用计数并释放该矩阵。
C++: void Mat::release()
该方法递减与矩阵的数据关联的引用计数。当引用计数减为0时,矩阵的数据将被释放,数据和引用计数器指针设置为 NULL。如果矩阵头指向外部数据集 (见 Mat::Mat()), 引用计数为 NULL,并且该方法在这种情况下无效。
可以手动调用此方法强制矩阵数据释放。但由于这种方法在析构函数中是自动调用的,或以更改数据指针的其他方法,因此通常不需要调用这个函数。在支持它的平台上,引用计数器递减并检查是否为0 是一个原子操作。因此,在不同的线程异步调用相同的矩阵是安全的操作。
Mat::resize
更改矩阵的行数。
C++: void Mat::resize(size_t sz)
C++: void Mat::resize(size_t sz, const Scalar& s)
参数
sz –新的行数。
s –分配给新添加的元素的值。
该方法更改矩阵的行数。如果矩阵重新分配,第一最少(Mat::rows,sz) 行数要保留下来。该方法模拟相应的 STL 向量类的方法。
Mat::reserve
保留一定数量的行的空间。
C++: void Mat::reserve(size_t sz)
参数
sz –的行数。
该方法sz行存储空间。如果矩阵已经有足够的空间来存储sz行,没有任何异常发生。如果矩阵重新分配,保留前(Mat::rows) 行。该方法模拟了相应的STL 向量类的方法。
Mat::push_back
将元素添加到矩阵的底部。
C++: template<typename T> voidMat::push_back(const T& elem)
C++: void Mat::push_back(const Mat& elem)
参数
elem –增加的一个或多个元素。
该方法将一个或多个元素添加到矩阵的底部。他们是模拟相应的 STL 向量类的方法。元素为Mat时,其类型和列的数目必须和矩阵容器是相同的。
Mat::pop_back
从底部的列表中删除元素。
C++: template<typename T> voidMat::pop_back(size_t nelems=1)
参数
nelems –删除的行的数目。如果它大于总的行数,则会引发异常。
该方法从底部的列表中删除一行或多行。
Mat::locateROI
父矩阵内定位矩阵头。
C++: void Mat::locateROI(Size& wholeSize,Point& ofs) const
参数
wholeSize–输出参数,其中包含的整个矩阵包含大小 * 这是其中一部分。
ofs –输出参数包含*this在整个的矩阵里面的偏移量。你使用Mat::row()、 Mat::col()、 Mat::rowRange()、Mat::colRange()以及其他的方法从矩阵中提取子阵后该结果子阵只指向原始大矩阵的一部分。然而,每个子阵包含有助于重建的最初矩阵大小和提取子阵在原始矩阵中的位置信息(由 datastart 和 dataend fields表示)。locateROI方法正是这样做的。
Mat::adjustROI
调整子阵大小及其在父矩阵中的位置。
C++: Mat& Mat::adjustROI(int dtop, int dbottom,int dleft, int dright)
参数
dtop –顶部子阵边界向上的平移量。
dbottom –底部子阵边界向下的平移量。
dleft –左子阵边界向左的平移量。
dright –右子阵边界向右的平移量。
该方法是 Mat::locateROI() 的互补性方法。这些函数的典型应用是确定父矩阵中子阵的位置,然后以某种方式改变位置。尤其典型的是,当滤镜操作中要考虑ROI外的像素时就需要它。当方法的所有参数都是正的时候,ROI需要以指定量全方位增长,例如:
A.adjustROI(2, 2, 2,2);
在此示例中,每个方向 4 元素增加矩阵大小。矩阵向左侧和上侧分别平移2 个元素,这会产生5 x 5 内核的滤镜所需的所有像素。你的责任是确保 adjustROI 不超出父矩阵边界。如果超出,该函数发出错误提示。OpenCV的滤镜函数在内部调用该函数,像filter2D(),形态学的操作,等等。
另请参阅:
copyMakeBorder()
Mat::operator()
提取矩形子阵。
C++: Mat Mat::operator()(Range rowRange, RangecolRange) const
C++: Mat Mat::operator()(const Rect& roi) const
C++: Mat Mat::operator()(const Ranges* ranges) const
参数:
rowRange –提取的子阵的开始和结束的行。不包括的上限。若要选择的所有行,请使用 Range::all()。
colRange –提取的子阵的开始和结束的列。不包括的上限。若要选择的所有列,请使用 Range::all()。
roi – 抽出子阵 specified 作为一个矩形。
ranges – 选定范围沿每个数组维度的数组。
该运算符为*this的子数组创建新的头。他们是Mat::row()、 Mat::col()、 Mat::rowRange(),和Mat::colRange()最普遍的形式。例如,A(Range(0, 10),Range::all()) 是相当于A.rowRange(0, 10)。与上述所有操作相同,该操作运算符是复杂度为O(1)的操作,就是没有矩阵数据将被复制。
Mat::operator CvMat
创建矩阵 CvMat 头。
C++: Mat::operator CvMat() const
该运算符创建矩阵 CvMat 的头,而不复制的基础数据。引用计数未被考虑到此操作中。因此,您应该确保CvMat 头在使用的时候不释放原始矩阵。该运算符对于新旧OpenCV API混用是有用的,例如:
Mat img(Size(320, 240), CV_8UC3);
...
CvMat cvimg = img;
mycvOldFunc( &cvimg, ...);
其中 mycvOldFunc 是用于OpenCV 1.x 数据结构的函数。
Mat::operator IplImage
创建IplImage矩阵头。
C++: Mat::operator IplImage() const
运算符创建矩阵 IplImage 头,而不复制的基础数据。您应该确保使用IplImage头时不释放原矩阵。与Mat::operatorCvMat类似,该运算符在OpenCV新旧API混用中很有用。
Mat::total
返回数组元素的总数。
C++: size_t Mat::total() const
该方法返回数组元素(如果该数组表示图像的像素数)的数目。
Mat::isContinuous
返回矩阵是否连续。
C++: bool Mat::isContinuous() const
如果在每一行的结尾无间隙连续存储矩阵的元素,该方法返回 true。否则,它将返回 false。很明显,1 x 1 或 1xN 矩阵始终是连续的。使用 Mat::create() 创建矩阵是始终是连续的。但是,如果您提取的矩阵,使用 Mat::col()、 Mat::diag(),等等,或外部已分配的数据构造的矩阵头的一部分,那么这种矩阵可能不再具有此属性。连续性标记在Mat::flags域内用一个位存储,构造矩阵头时可以自动计算出来。因此,连续性检查是非常快速的操作,虽然理论上可以做,如下所示:
/ / 等价的Mat::isContinuous() 的执行情况
boolmyCheckMatContinuity(const Mat& m)
{
//返回 (m.flags & Mat::CONTINUOUS_FLAG) != 0;
return m.rows ==1 || m.step == m.cols*m.elemSize();
}
很多的OpenCV 函数中使用该方法。关键在于按元素操作(如算术和逻辑运算、 数学函数、 alpha 融合、颜色空间转换,以及其他) 不依赖于图像的几何形状。因此,如果所有的输入和输出的阵列是连续的,该函数可以它们作为很长的单行的向量处理。下面的示例阐释了如何实现 alpha 融合功能。
template<typename T>
void alphaBlendRGBA(const Mat& src1,const Mat& src2, Mat& dst)
{
const float alpha_scale =(float)std::numeric_limits<T>::max(),
inv_scale = 1.f/alpha_scale;
CV_Assert( src1.type() == src2.type()&&
src1.type() ==CV_MAKETYPE(DataType<T>::depth, 4) &&
src1.size() == src2.size());
Size size = src1.size();
dst.create(size, src1.type());
// 规定如下: 检查阵列的连续性
//如果的确如此,阵列连续
// 把阵列看做一维的向量。
if( src1.isContinuous() &&src2.isContinuous() && dst.isContinuous() )
{
size.width *= size. height;
size.height = 1;
}
size.width *= 4;
for( int i = 0; i < size.height; i++ )
{// 当阵列连续,
// 外循环只执行一次
const T* ptr1 = src1.ptr<T>(i);
const T* ptr2 = src2.ptr<T>(i);
T* dptr = dst.ptr<T>(i);
for( int j = 0; j < size.width; j += 4 )
{
float alpha = ptr1[j+3]*inv_scale, beta =ptr2[j+3]*inv_scale;
dptr[j] =saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
dptr[j+1] =saturate_cast<T>(ptr1[j+1]*alpha + ptr2[j+1]*beta);
dptr[j+2] =saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
dptr[j+3] = saturate_cast<T>((1 -(1-alpha)*(1-beta))*alpha_scale);
}
}
}
这种方法,不仅很简单,而且在简单的元素的操作中可以提高10-20%性能,尤其在图像很小且操作非常简单的时候。
在此函数中另一个 OpenCV 语法,目标数组中Mat::create() 的调用会对没有适当大小和类型的目标数组分配空间。虽然新分配的数组始终是连续的,但您仍需要检查目标数组,因为 Mat::create()不是总会分配一个新的矩阵。
Mat::elemSize
返回矩阵元素大小 (以字节为单位)。
C++: size_t Mat::elemSize() const
该方法返回以字节为单位的矩阵元素大小。例如,如果矩阵类型是 CV_16SC3,该方法返回3*sizeof(short)或 6。
Mat::elemSize1
以字节为单位返回每个矩阵元素通道的大小。
C++: size_t Mat::elemSize1() const
该方法返回以字节为单位的矩阵元素通道大小,也就是忽略通道的数量。例如,
如果矩阵类型是 CV_16SC3,该方法返回 sizeof(short) 或 2。
Mat::type
返回一个矩阵元素的类型。
C++: int Mat::type() const
该方法返回一个矩阵的元素类型。这是兼容CvMat 类型系统,像 CV_16SC3标识符
或 16 位有符号的3 通道阵列,等等。
Mat::depth
返回一个矩阵元素的深度。
C++: int Mat::depth() const
该方法返回矩阵元素深度(每个单独的通道类型)的标识符。例如,对于16位有符号的3通道数组,该方法返回CV_16S。矩阵类型的完整列表包含以下内容值:
• CV_8U-8 位无符号整数 (0…..255)
• CV_8S-8 位符号整数 (-128…..127)
• CV_16U-16 位无符号整数 (0……65535)
• CV_16S-16 位符号整数 (-32768…..32767)
• CV_32S-32 位符号整数 (-2147483648……2147483647)
• CV_32F-32 位浮点数 (-FLT_MAX ………FLT_MAX,INF,NAN)
• CV_64F-64 位浮点数(-DBL_MAX ……….DBL_MAX,INF,NAN)
Mat::channels
返回矩阵通道的数目。
C++: int Mat::channels() const
该方法返回矩阵通道的数目。
Mat::step1
返回矩阵归一化迈出的一步。
C + +: size_t const Mat::step1()
该方法返回以矩阵的step除以Mat::elemSize1()。它对快速访问任意矩阵元素很有用。
Mat::size
返回一个矩阵大小。
C++: Size Mat::size() const
该方法返回一个矩阵大小:Size(cols, rows)。矩阵超过 2 维时返回大小为(-1,-1)。
Mat::empty
如果数组有没有 elemens,则返回 true。
C++: bool Mat::empty() const
如果 Mat::total() 是 0 或 Mat::data 为 NULL,则方法返回 true。因为pop_back() 和 resize()方法M.total()= = 0,并不意味着M.data = =NULL。
Mat::ptr
返回指定矩阵行的指针。
C++: uchar* Mat::ptr(int i=0)
C++: const uchar* Mat::ptr(int i=0) const
C++: template<typename _Tp> _Tp* Mat::ptr(inti=0)
C++: template<typename _Tp> const _Tp*Mat::ptr(int i=0) const
参数:
i –一个基于0的行索引。
该方法返回uchar*,或指向由输入指定矩阵行的指针。参看Mat::isContinuous()的中示例了解如何使用这些方法。
Mat::at
返回对指定数组元素的引用。
C++: template<typename T> T& Mat::at(int i)const
C++: template<typename T> const T&Mat::at(int i) const
C++: template<typename T> T& Mat::at(int i,int j)
C++: template<typename T> const T&Mat::at(int i, int j) const
C++: template<typename T> T& Mat::at(Pointpt)
C++: template<typename T> const T&Mat::at(Point pt) const
C++: template<typename T> T& Mat::at(int i,int j, int k)
C++: template<typename T> const T&Mat::at(int i, int j, int k) const
C++: template<typename T> T& Mat::at(constint* idx)
C++: template<typename T> const T&Mat::at(const int* idx) const
参数
i –索引 0 维度
j – 1 维度的索引
k – 沿 2 维度的索引
pt – Point(j,i) 作为指定元素的位置。
idx – Mat::dims 数组的索引。
该模板方法返回指定数组元素的引用。为了具有更高的性能,索引范围检查只在调试配置下执行。请注意使用具有单个索引 (i) 的变量可以访问的单行或单列的2 维的数组元素。也就是比方说,如果A是1 x N 浮点矩阵和B是M x 1的整数矩阵,您只需编写A.at<float>(k+4) 和 B.at<int>(2*i+1) 分别代替A.at<float>(0,k+4)和
B.at<int>(2*i+1,0)。
下面的示例将初始化希尔伯特矩阵:
Mat H(100, 100, CV_64F);
for(inti= 0; i<H.rows; i++)
for(intj= 0; j<H.cols; j++)
H.at<double>(i,j)=1./(i+j+1);
Mat::begin
返回矩阵迭代器,并将其设置为第一矩阵元。
C++: template<typename _Tp>MatIterator_<_Tp> Mat::begin()
C++: template<typename _Tp>MatConstIterator_<_Tp> Mat::begin() const
该方法返回矩阵的只读或读写的迭代器。矩阵迭代器的使用和双向 STL 迭代器的使用是非常相似的。在下面的示例中,alpha融合函数是使用矩阵迭代器重写:
template<typename T>
void alphaBlendRGBA(const Mat& src1,const Mat& src2, Mat& dst)
{
typedef Vec<T, 4> VT;
const float alpha_scale =(float)std::numeric_limits<T>::max(),
inv_scale = 1.f/alpha_scale;
CV_Assert( src1.type() == src2.type()&&
src1.type() == DataType<VT>::type&&
src1.size() == src2.size());
Size size = src1.size();
dst.create(size, src1.type());
MatConstIterator_<VT> it1 =src1.begin<VT>(), it1_end = src1.end<VT>();
MatConstIterator_<VT> it2 =src2.begin<VT>();
MatIterator_<VT> dst_it =dst.begin<VT>();
for( ; it1 != it1_end; ++it1, ++it2,++dst_it )
{
VT pix1 = *it1, pix2 = *it2;
float alpha = pix1[3]*inv_scale, beta =pix2[3]*inv_scale;
*dst_it =VT(saturate_cast<T>(pix1[0]*alpha + pix2[0]*beta),
saturate_cast<T>(pix1[1]*alpha + pix2[1]*beta),
saturate_cast<T>(pix1[2]*alpha +pix2[2]*beta),
saturate_cast<T>((1 -(1-alpha)*(1-beta))*alpha_scale));
}
}
Mat::end
返回矩阵迭代器,并将其设置为 最后元素之后(after-last)的矩阵元。
C++: template<typename _Tp>MatIterator_<_Tp> Mat::end()
C++: template<typename _Tp>MatConstIterator_<_Tp> Mat::end() const
该方法返回矩阵只读或读写的迭代器,设置为紧随最后一个矩阵元素的点。
参考文档:
1) http://blog.csdn.net/xiahouzuoxin/article/details/38298165
2) http://www.cnblogs.com/wangguchangqing/p/4016179.html