OpenCV中矩阵类详解之一:Mat

  • 综述

Mat类可以被看做是opencv中C++版本的矩阵类,替代原来C版本的矩阵结构体CvMat和图像结构体IplImage;

Mat最大的优势跟STL的兼容性很好,有很多类似于STL的操作。但是Mat远远强于后者;

Mat是一种高效的数据类型,它对内存进行动态的管理,不需要之前用户手动的管理内存。

  • Mat类定义

Mat类定义于core.hpp中,主要包含有两部分数据:一部分是矩阵头(matrix header),这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等等;另一个部分是一个指向矩阵包含像素值的指针(data)。Mat类的定义如下:

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
    ...
};

  • Mat数据类型

Mat的存储是逐行存储的,矩阵中的数据类型包括:Mat_对应的是CV_8U,Mat_对应的是CV_8U,Mat_对应的是CV_8S,Mat_对应的是CV_32S,Mat_对应的是CV_32F,Mat_对应的是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矩阵
//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));
  • 复制矩阵
参见另一篇博文:http://blog.csdn.net/holybin/article/details/17711013
  • 合并矩阵
//假设现有需要将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(i,j) += 1.f;	//set (i,j)-th element
int a = M.at(i,j);		//get (i,j)-th element
//2.矩阵元素指针(ptr)
for(int i = 0; i < M.rows; i++)
{
    const double* pData = M.ptr(i);	//第i+1行的所有元素
    for(int j = 0; j < M.cols; j++)
		cout<
  • 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 voidMat::push_back(const T& elem)

C++: void Mat::push_back(const Mat& elem)

参数

elem –增加的一个或多个元素。

该方法将一个或多个元素添加到矩阵的底部。他们是模拟相应的 STL 向量类的方法。元素为Mat时,其类型和列的数目必须和矩阵容器是相同的。

Mat::pop_back

从底部的列表中删除元素。

C++: template 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

void alphaBlendRGBA(const Mat& src1,const Mat& src2, Mat& dst)

{

const float alpha_scale =(float)std::numeric_limits::max(),

inv_scale = 1.f/alpha_scale;

CV_Assert( src1.type() == src2.type()&&

src1.type() ==CV_MAKETYPE(DataType::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(i);

const T* ptr2 = src2.ptr(i);

T* dptr = dst.ptr(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(ptr1[j]*alpha + ptr2[j]*beta);

dptr[j+1] =saturate_cast(ptr1[j+1]*alpha + ptr2[j+1]*beta);

dptr[j+2] =saturate_cast(ptr1[j+2]*alpha + ptr2[j+2]*beta);

dptr[j+3] = saturate_cast((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 _Tp* Mat::ptr(inti=0)

C++: template const _Tp*Mat::ptr(int i=0) const

参数:

i –一个基于0的行索引。

该方法返回uchar*,或指向由输入指定矩阵行的指针。参看Mat::isContinuous()的中示例了解如何使用这些方法。

Mat::at

返回对指定数组元素的引用。

C++: template T& 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(Pointpt)

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(constint* idx)

C++: template 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(k+4) 和 B.at(2*i+1) 分别代替A.at(0,k+4)和

B.at(2*i+1,0)。

下面的示例将初始化希尔伯特矩阵:

Mat H(100100, CV_64F);

for(inti0; i<H.rows; i++)

for(intj0; j<H.cols; j++)

H.at<double>(i,j)=1./(i+j+1);

Mat::begin

返回矩阵迭代器,并将其设置为第一矩阵元。

C++: templateMatIterator_<_Tp> Mat::begin()

C++: templateMatConstIterator_<_Tp> Mat::begin() const

该方法返回矩阵的只读或读写的迭代器。矩阵迭代器的使用和双向 STL 迭代器的使用是非常相似的。在下面的示例中,alpha融合函数是使用矩阵迭代器重写:

template

void alphaBlendRGBA(const Mat& src1,const Mat& src2, Mat& dst)

{

typedef Vec VT;

const float alpha_scale =(float)std::numeric_limits::max(),

inv_scale = 1.f/alpha_scale;

CV_Assert( src1.type() == src2.type()&&

src1.type() == DataType::type&&

src1.size() == src2.size());

Size size = src1.size();

dst.create(size, src1.type());

MatConstIterator_ it1 =src1.begin(), it1_end = src1.end();

MatConstIterator_ it2 =src2.begin();

MatIterator_ dst_it =dst.begin();

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(pix1[0]*alpha + pix2[0]*beta),

saturate_cast(pix1[1]*alpha + pix2[1]*beta),

saturate_cast(pix1[2]*alpha +pix2[2]*beta),

saturate_cast((1 -(1-alpha)*(1-beta))*alpha_scale));

}

}

Mat::end

返回矩阵迭代器,并将其设置为 最后元素之后(after-last)的矩阵元。

C++: templateMatIterator_<_Tp> Mat::end()

C++: templateMatConstIterator_<_Tp> Mat::end() const

该方法返回矩阵只读或读写的迭代器,设置为紧随最后一个矩阵元素的点。


REF:

http://blog.csdn.net/yang_xian521/article/details/7107786

http://blog.csdn.net/giantchen547792075/article/details/9107877


你可能感兴趣的:(OpenCV,OpenCV,Mat)