Mat的内容非常多,大致可以分类 构造函数、成员函数2个大板块。
1、Mat构造函数
Mat::Mat
C++: Mat::Mat()
C++: Mat::Mat(int rows, int cols, int type)
C++: Mat::Mat(Size size, int type)
C++: Mat::Mat(int rows, int cols, int type, const Scalar& s)
C++: Mat::Mat(Size size, int type, const Scalar& s)
C++: Mat::Mat(const Mat& m)
C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
C++: Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all() )
C++: Mat::Mat(const Mat& m, const Rect& roi)
C++: Mat::Mat(const CvMat* m, bool copyData=false)
C++: Mat::Mat(const IplImage* img, bool copyData=false)
C++: template explicit Mat::Mat(const Vec& vec, bool copyData=true)
C++: template explicit Mat::Mat(const Matx& vec, bool copyData=true)
C++: template explicit Mat::Mat(const vector& vec, bool copyData=false)
C++: Mat::Mat(int ndims, const int* sizes, int type)
C++: Mat::Mat(int ndims, const int* sizes, int type, const Scalar& s)
C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
C++: Mat::Mat(const Mat& m, const Range* ranges)
部分参数:
2、成员函数
1) Mat & Mat::operator =
Mat A = (Mat_(2, 2) << 1, 2, 3, 4);
Mat B = (Mat_(1, 4) << 3, 2, 1, 4);
Mat C = A;
C = B;
C = A + B;
2) Mat Mat::row(int y) const Mat::col(int x) const
返回基于0的某一列或某一行为新的矩阵,深复制
Mat A;
...
A.row(i) = A.row(j); // not works
A.row(i) = A.row(j) + 0; // works, but looks a bit obscure.
A.row(j).copyTo(A.row(i));
3) Mat Mat::rowRange(int startrow, int endrow) const
Mat Mat::rowRange(const Range& r) const
子矩阵,连续某些行或者连续某些列构成的矩阵, 深复制。(类似还有colRange()方法)
Mat A = (Mat_(3, 3) << 0, 1, 2, 3,4,5,6,7,8);
Mat B = (Mat_(3,3) << 1, 2, 3, 4,5,6,7,8,9);
Mat c = A(Range(0, 2), Range::all());
// 行中使用Range(n1,n2),就是选择n1到n2-1行,同理
// 列中使用Range(n1,n2),就是选择n1到n2-1列;
// all()是选择对为位置所有的行或者列
4) Mat Mat::diag(int d=0 ) const
取矩阵对角线元素为列向量
Matx44f m( 1, 2, 3, 4,
5, 6, 7, 8,
9,10,11,12,
13,14,15,16);
Mat dia = Mat(m).diag(); //[1,6,11,16]'
dia = Mat(m).diag(1); //[2,7,12]'
dia = Mat(m).diag(1); //[3,8]'
dia = Mat(m).diag(-1); //[5,10,15]'
5) Mat::clone , Mat::copyTo
深复制; copyTo第二个参数可选mask
6) void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const
两种方式, (1) 改变数据类型 (2)数据类型和数值两个变换
m(x; y) = saturate_cast < rType > (α(*this)(x; y) + β) ; saturate_cast
例子和结果如下
int data[] = { 1, 2, 3, 4, 5, 6 };
Mat A(2, 3, CV_32SC1, data); //整型
Mat B1,B2;
A.convertTo(B1, CV_8UC1);
A.convertTo(B2, CV_32FC1, 0.1, 1);
coutMat("A = ", A);
coutMat("convertTo(B, CV_8UC1) = ", B1); //uchar
coutMat("convertTo(B, CV_32FC1, 0.1, 1) = ", B2); //float
7) void Mat::resize(size_t sz), void Mat::resize(size_t sz, const Scalar& s)
改变矩阵行数,当新的行数小于原矩阵行数,那么会释放新的行数后面的元素;大于时,可以指定后面新增的行数的元素初值为s。下面给出一个例子,和对应两次resize的结果图
Mat A = (Mat_(3, 3) << 0, 1, 2, 3, 4, 5, 6, 7, 8);
A.resize(4, 9); //改变行数为4行,新增1行,并指定新的一行元素全为9
A.resize(2); //由原来4行,变为2行
(第一二张放反了,囧... 调试工具查看Mat数据参考OpenCv2 学习笔记(2) Mat图像显示)
8) Mat& Mat::setTo(InputArray value, InputArray mask=noArray() )
矩阵(可选mask区域)赋值,如
int data[] = { 1, 2, 3, 4, 5, 6 };
Mat A(2, 3, CV_32SC1, data);
Mat mask = (Mat_(2, 3) << 0, 0, 1, 1, 0, 0);
coutMat("A =", A);
Mat B = A.setTo(Scalar(2), mask);
coutMat("A' =", A);
coutMat("B =", B);
Mat C = A.setTo(Scalar(2), Matx(0, 0, 0, 0, 1, 1)) + 0;
coutMat("A'' =", A);
coutMat("B' =", B);
coutMat("C =", C);
A.setTo(Scalar(0), Matx(0, 0, 0, 0, 1, 1));
coutMat("C' =", C);
9) Mat Mat::reshape(int cn, int rows=0) const
改变二维矩阵的size或/和通道数,浅复制(但是返回值不是引用)。变换后,必须保证 rows*cols*channels()保持相等。例子和结果
vector vec; // vec包含3个浮点二维点。(可以理解为2通道的3个元素)
int i = 0;
while (i++ < 3)
vec.push_back(Point2f(i, i+1)); // 压入的元素和vec类型一致
//vec.pop_back(); // 移除最后一个元素
Mat matpoint = Mat(vec); // 操作符 vec转成Mat 结构, 二维矩阵,3行1列
//Mat res = matpoint.reshape(1,3); // *浅复制* 和matpoint一样。
Mat res = matpoint.reshape(1,3) + 0; // 通过运算,改变通道数。二维矩阵,3行1列 =》 一维矩阵,3行2列
通过上图,发现2通道的matpoint矩阵的3个元素,都是按照列向量摆放,正好反映了matx、vet、scalar是以列向量访问和复制矩阵。
10) Mat::t , Mat::inv, Mat::mul, Mat::cross, Mat::dot
分别为:转置,矩阵的逆(LU、CHOLESKY、SVD三种解法),数乘(2个矩阵对应位置元素相乘或一个矩阵和一个标量相乘),叉乘(含有三个元素的2个向量运算),点乘(内积,2个向量(一维矩阵)运算)。
11) Mat::zeros,Mat::ones,Mat::eye 0矩阵,1矩阵, 单位(主对角线为1)矩阵
12) 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, int type)
13) Mat::addref, Mat::release
前者用于给矩阵的内部成员变量refcount计数,后者用于释放矩阵。在Mat里面通常不用关心矩阵的内存分配和释放,矩阵会在有需要的时候自行分配,同样由于矩阵的引用计数机制使得在refcount为0时会自动释放矩阵内存。
14) void Mat::locateROI(Size& wholeSize, Point& ofs) const
确定子矩阵在被引用矩阵的顶点位置和被引用矩阵的size。
Mat a = Mat(100, 100, CV_32F);
Size sz; Point ofs;
Mat b = a(Range(20, 51), Range(50, 92)); //31行42列 b.size = [42,31] -> a ~ [20,51; 50,92]
b.locateROI(sz, ofs); // b/ [100x100], [50,20]
Mat c = b(Range(5, 17), Range(10, 21)); //12行11列 b.size = [11,12] -> a ~ [25,42; 60,81]
c.locateROI(sz, ofs); // c/ [100x100], [60,25]
5) size_t Mat::total() const
矩阵元素个数 total = cols*rows
16) bool Mat::isContinuous() const
确定矩阵元素在内存中是否为连续存储。显然,只有一个元素的矩阵、只有一行的矩阵的所有元素在内存中都是连续存储的。通过create()创建的矩阵的元素也总是连续的。但是由矩阵部分元素得到的子矩阵,如某一列col()、diag()等肯定是不连续的。通常矩阵为连续时,可以将这个矩阵的数据存储区域看成一个具有大量元素的行向量(访问时可以变得简单)。
17) size_t Mat::elemSize() const , size_t Mat::elemSize1()
elemSize() 矩阵一个元素占用字节数,elemSize1() 矩阵元素的一个通道占用的字节数。如矩阵数据类型为CV_8UC3时,elemSize()=1byte*3,elemSize1() =8bit = 1byte;矩阵数据类型为CV_16SC2时,elemSize()=2byte*2,elemSize1() =16bit = 8 byte.
18) int Mat::channels() const
矩阵通道数。如类型CV16SC2,通道数2(即类型中字母C后的数值)。
19) int Mat::depth() const
矩阵数据的类型,宏定义,其取值范围为0~7
// Mat::depth()
#define CV_8U 0 // uchar
#define CV_8S 1 // char
#define CV_16U 2 // ushort
#define CV_16S 3 // short
#define CV_32S 4 // int
#define CV_32F 5 // float
#define CV_64F 6 // double
#define CV_USRTYPE1 7
20) int Mat::type() const
矩阵的类型,描述矩阵的数据类型和通道数目。例如矩阵类型为CV_32FC2时,表示该矩阵的数据类型为32位浮点数、通道数为2。其返回值为下述宏定义
#define CV_CN_MAX 512
#define CV_CN_SHIFT 3
#define CV_DEPTH_MAX (1 << CV_CN_SHIFT)
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
其中CV_MAT_DEPTH(depth)是另外一个宏定义,由于depth() 取值为[0,7], 那么 depth() & (8-1) = depth() , 因此直接可以当做depth(),cn的通道数为channels(),CV_CN_SHIFT = 3。因此,type() = depth() + (channels() - 1) << 3。
通过代码打印出下面常用的几种类型
21) size_t Mat::step1(int i=0 ) const,Size Mat::size() const
对于二维矩阵,size()返回Size(cols, rows),step1(0)为一行元素的字节数,step1(1)为一个元素的字节数elemSize()。
对于多维,还有一个step的成员变量,是一个MStep结构体,step[i]是第i维占用的字节数。对于二维矩阵,step[0]是一行占用的字节数,step[1]是一个元素占用的字节数。
有关step,size,step1,elemSize,elemSize1的详细介绍,可以点击参考网站。
在OpenCv1.0中对CvMat和IplImage的元素遍历,会用到一行的字节数找到某个元素的指针。然而,这些个参数其实在OpenCv中使用的不多,用成员函数Mat::at可以方便的遍历,在debug下效率和指针有区别,但是在release下效率相同,再后续学习笔记中会详细介绍。
22) Mat::ptr, Mat::data, Mat::empty
两个数据区指针,data和ptr<_Tp>(0)都指向数据区的首地址,默认都是uchar *类型。ptr<_Tp>(i)指向第i行元素的首地址,那么第i行第j列元素的地址为addrptr<_Tp>(i)[j]。若知道每行的字节数和单个元素字节数,可以通过data得到第i行第j列元素的地址addr(Mi0,…,iM.dims-1) = M.data + M:step[0] * i0 + M.step[1] * i1 + … + M.step[M.dims - 1] * iM.dims-1, 对于二维矩阵 addr(Mi,j) = M.data + M.step[0] * i + M.step[1] * j。
bool Mat::empty()当矩阵没有元素即Mat::total() ==0时为true。当矩阵仅创建信息头,没有分配内存时,Mat::data == NULL,那么Mat::total() ==0,因而 empty() == true。但是Mat::total() ==0 并不能说明Mat::data == NULL,是因为矩阵分配内存后使用函数pop_back和resize后元素没有了,但是数据指针还在但不为NULL。
23) Mat::push_back, Mat::pop_back
这两个方法使得Mat有了STL容器的功能,可以在矩阵最后一行压入或者弹出一行元素。当矩阵为空,可以压入任意一个矩阵,当矩阵不为空时只能压入行元素个数相同的矩阵,并且type相同。
那么,当要在矩阵最右侧加入一列,要怎么做呢?既然只能按照行添加,那么仅需将转置,加入某一列矩阵的转置或者行矩阵,最后将结果矩阵再转置一次,就得到结果了(可以用函数hconcat、vconcat拼接函数实现)。例子和结果如下
Mat A = (Mat_(3, 2) << 0, 1, 2, 3, 4, 5); //3行2列
Mat B = (Mat_(3, 1) << 7, 8, 9); //3行1列 列向量
coutMat("A = ", A); coutMat("B = ", B);
Mat C = A.t();
Mat D = B.t();
C.push_back(D);
Mat E = C.t();
coutMat("E = ", E);
24) Mat::at
返回指定元素的一个引用,有以下几种方法:
C++: template
对于size为1*n或者n*1的矩阵,也就是单行或者单列矩阵,可以直接用一个(index)取代(i,j)去引用某个指定元素,如用A.atT& Mat::at(int i) const C++: template const T& Mat::at(int i) const C++: template T& Mat::at(int i, int j) C++: template const T& Mat::at(int i, int j) const C++: template T& Mat::at(Point pt) C++: template const T& Mat::at(Point pt) const C++: template T& Mat::at(int i, int j, int k) C++: template const T& Mat::at(int i, int j, int k) const C++: template T& Mat::at(const int* idx) C++: template const T& Mat::at(const int* idx) cons (k+4)代替A.at (0,k+4) ,B.at (2*i+1)代替B.at (2*i+1,0)等。
下面给出一个Hilbert矩阵初始化的例子
Mat H(100, 100, CV_64F);
for(int i = 0; i < H.rows; i++)
for(int j = 0; j < H.cols; j++)
H.at(i,j)=1./(i+j+1);
附:类Mat_
由Mmat派生的一个模板类,在定义时给出数据类型,用Mat::at访问时可以不用给出类型直接通过index访问。下例可以和上面的24)Mat::at进行比较:
Mat_ M(20,20);
for(int i = 0; i < M.rows; i++)
for(int j = 0; j < M.cols; j++)
M(i,j) = 1./(i+j+1);
对于多维Mat_矩阵,可以用Vec传递参数(同样适用于Mat),如
Mat_ img(240, 320, Vec3b(0,255,0));
for(int i = 0; i < 100; i++)
img(i,i)=Vec3b(255,255,255);
for(int i = 0; i < img.rows; i++)
for(int j = 0; j < img.cols; j++)
img(i,j)[2] ^= (uchar)(i ^ j);