Mat类详解(一)

译文参考The OpenCV Reference Manual (Release 2.3)August 17 2011

Mat类

OpenCV c + + n 维稠密数组类

class CV_EXPORTS Mat

{

public:

/ / … …很多的方法...

...

/*!包括几位字段:

-神奇的签名

-连续性标志

-深度(Note:应该是位深)

-通道数

*/

int flags;(Note :目前还不知道flags做什么用的)

//!数组的维数,> = 2

int dims ;

//!行和列的数量或 (-1,-1) 此时数组已超过 2 维

int rows,cols;

//!指向数据的指针

uchar *data ;

//!指针的引用计数器 ;

/ / 阵列指向用户分配的数据时,当指针为 NULL

int * refcount ;

/ / 其他成员

...

};

Mat类表示一个 n 维的稠密数值型的单通道或多通道数组。它可以用于存储实数或复数值的向量和矩阵、灰度或彩色图像、体素、向量场、点云、张量、直方图 (尽管较高维的直方图存储在SparseMat可能更好)。M 数组的数据布局是由阵列  M.step[]定义的,使元素的地址(i0,。。。。iM.dims-1),其中 0<= ik < M.size [k],可以计算为:

addr( Mi0 ;:::;iM.dims-1) = M.data+ M.step[ 0]*i0 + M.step[ 1] *i1 + .…+ M.step[ M:dims- 1] iM:dims- 1

2维的数组的情况下根据上述公式被减至:

addr( Mi,j)= M.data+ M.step[ 0]*i+ M.step[ 1] *j

请注意,M.step[i] > =M.step[i+1] (事实上,M.step[i] > =M.step[i+1]*M.size[i+1])。这意味着2维矩阵是按行存储的,3 维矩阵是由平面存储,以此类推。M.step[M.dims-1] 是最小的而且总是等于元素大小M.elemSize()。因此,Mat中的数据布局完全兼容OpenCV 1.x 中CvMat、 IplImage、 CvMatND类型。它也和标准工具包和SDK,如Numpy(ndarray),Win32(独立设备位图)等主流的密集数组类型相兼容,也就是说,与任何使用步进(或步长)来计算像素位置的阵列相兼容。由于这种兼容性,使用户分配的数据创建Mat头以及用OpenCV函数实时处理该头成为可能。有很多不同的方法,创建一个Mat的对象。下面列出了最常见的选项:

使用 create(nrows,ncols,type)方法或类似的Mat(nrows,ncols,type [,fillValue])构造函数。一个新的指定了大小和类型的数组被分配。type和cvCreateMat 方法中的type参数具有相同的含义。例如,CV_8UC1 是指一个 8 位单通道阵列,CV_32FC2 指 2 通道(复平面)浮点阵列,以此类推。

//创建一个用1+3j填充的 7 x 7 复矩阵。

Mat  M(7,7,CV_32FC2,Scalar(1,3)) ;

/ /现在将 M转换为100 x 60的CV_8UC(15)的矩阵。

/ / 旧内容将会被释放

M.create(100,60,CV_8UC(15)) ;

这一章导言中指出,当当前的数组与指定的数组的形状或类型create() 分配唯一的新数组时的形状或类型。

创建多维数组:

/ / 创建 100 x 100 x 100 8 位数组

int sz[] = {100, 100, 100};

Mat. bigCube (3,sz,CV_8U,Scalar::all(0)) ;它将维度数(= 1)传递给Mat的构造函数,但列数设置为 1时,创建数组将是 2 维的。因此,Mat::dims 始终是>=2的(该数组为空时,也可以是 0)。

使用的复制构造函数或赋值运算符可以是一个数组或右侧的表达式(请参阅下图)。正像在导言中指出的,数组赋值运算复杂度是O(1)因为当你需要它的时候,它仅复制头和增加引用计数。Mat::clone() 方法可用于获取全(深)的副本数组。

为另一个数组的一部分构建头。它可以是单个行、 单个列,几个行,几个列,矩形区域(代数中称为较小值) 的数组或对角线。这种操作也是复杂度为O(1),因为,新头引用相同的数据。实际上,您可以使用此特性修改该数组的一部分例如:

/ /第 5行,乘以 3,加到第 3 行,

M.row(3) = M.row(3) + M.row (5) * 3 ;

/ / 现在将第7列复制到第1列

/ / M.col(1) = M.col(7) ;/ / 这个不能实现。

Mat  M1= M.col(1) ;

M.col(7).copyTo(M1) ;

/ / 创建一种新的 320 x 240 图像

Mat img(Size(320,240),CV_8UC3) ;

/ / 选择ROI(region of interest)

Mat roi(img,Rect(10,10,100,100)) ;

/ / 填充 (0,255,0) 的ROI (这是RGB 空间中的绿色);

/ / 320 x 240 原始图像将被修改。

roi = Scalar(0,255,0) ;

由于额外的 datastart 和 dataend 的成员,它们使得用locateROI() 计算子数组在主容器数组中的相对的位置成为可能:

Mat A = Mat::eye ( 10, 10, CV_32S);

/ / 提取 A 的1 (含)到 3 (不包含)列。

Mat B = A(Range::all(),Range(1,3)) ;

/ / 提取 B 的5 (含)到 9 (不包含)行。

/ /即 C ~ A(Range(5,9),Range (1,3))

Mat C = B(Range(5,9),Range::all()) ;

Size size;Point ofs;

C.locateROI (size,ofs);

/ / size将变为 (width= 10,height= 10),ofs会变为 (x = 1,y = 5)

考虑到整个矩阵,如果您需要深层副本,使用子矩阵的sclone() 方法的提取。

为用户分配数据创建矩阵头。有利于执行下列操作:

1. 使用 OpenCV处理"外来"的数据(例如,当您执行 DirectShow *?lter 或 gstreamer的pro-cessing 模块,等等)。例如:

void process_video_frame (const unsignedchar * pixels,

int width,int height,int step)

{

Mat img (width,height, CV_8UC3,pixels,step);

GaussianBlur (img,img ,Size(7,7),1.5,1.5) ;

}

2.快速初始化小矩阵和/或获取超快的元素的访问。

double m[3] [3] = {{a,b,c},{d,e,f} {g, h, i}}};

Mat M = Mat(3,3,CV_64F,m).inv() ;

本例中用户分配数据的一些很常见情况是从CvMat 和 IplImage 转换到Mat。为达到此目的,有些特殊的构造函数以指向CvMat 或 IplImage 和?ag可选参数指示是否数据复制。从Mat到 CvMat 或 IplImage 的后台转换是通过类型转换运算符 Mat::operator CvMat() const 和 Mat::operator IplImage()实现的。operators不要复制数据。

IplImage * img = cvLoadImage("greatwave.jpg",1) ;

Mat mtx(img) ;/ / IplImage *-> Mat

CvMat oldmat = mtx ;/ / Mat-> CvMat

CV_Assert (oldmat.cols = = img-> width&& oldmat.rows = = img-> height & &

oldmat.data.ptr = = (uchar *) img->imageData & & oldmat.step = = img-> widthStep);

使用 MATLAB 样式数组初始值设定项zeros()、 ones()、 eye(),例如:

/ / 创建具双精度标识矩阵并将其添加到M。

M + = Mat::eye (M.rows,M.cols,CV_64F);

使用逗号分隔的初始值设定项:

/ / 创建 3 x 3 双精度恒等矩阵

Mat M = (Mat_ <double> (3,3) <<1,0,0,0,1,0,0,0,1) ;

使用此方法,您首先调用具有适当的参数的 Mat_类构造函数,然后只要把 << 运算符后面的值用逗号分隔,这些值可以是常量、变量、 表达式,等等。此外请注意所需的额外的圆括号((Mat_<double> (3,3)<< 1,0,0,0,1,0,0,0,1))以免出现编译错误。

数组一旦创建起来,它可以自动通过引用计数的机制被管理。如果数组头是在用户分配的数据的基础上构建的,您应该自己处理这些数据。当没有指向它的引用时,数组中的数据将被释放。如果在数组的析构函被调用之前要释放一个由矩阵头指向的数据,请使用Mat::release()。

掌握Array类的另一个重要的环节是元素的访问。本手册已经描述了如何计算每个数组元素的地址。通常情况下,不需要在代码中直接使用的公式。如果你知道数组元素类型(它可以使用 Mat::type() 方法检索得到),您可以用以下方式访问二维数组的元素Mij

M.at <double>(i,j)  + = 1.f ;

假定 M 一个双精度浮点型数组。有几个变体的不同方法来针对不同的维度数进行处理。

如果您要处理整行的二维数组,最有效的方式是获取该行的头指针然后只需使用普通的 C运算符[]:

/ / 正矩阵元素之和计算

/ / (假定M 是一个双精度矩阵)

double sum = 0;

for (int i = 0 ;i < M.rows ; i + +)

{

const double *Mi = M.ptr <double> (i) ;

for (int j = 0; j < M.cols ; j + +)

sum + = std::max(Mi [j],0.) ;

}

以上的操作中,某些操作实际上不依赖该数组的形状。他们只是一个接一个(或多个具有相同的坐标的多个数组中的元素,例如,数组相加)地处理数组元素。这种操作称为 元素指向(element-wise)。检查是否所有的输入/输出阵列是连续的,即有没有间断在每行的结尾,是有意义的。如果是的话,将它们(这些数组)作为单独的一个长行来处理:

/ / 计算正矩阵元素,优化的变量的总和

double sum = 0;

int cols =M.cols,rows = M.rows ;

if(M.isContinuous())

{

cols * = rows ;

rows = 1 ;

}

for (int i = 0 ;i < rows; i + +)

{

const double * Mi = M.ptr <double>(i) ;

for (int j = 0; j < cols ; j ++)

sum + = std::max (Mi [j],0.) ;

}

对于连续的矩阵来说,外部循环体只需一次执行。所以,开销是规模较小,

小型矩阵的情况下尤其明显。

最后,还有足以成功跳过连续的行之间的间隔智能的STL 样式迭代器:

/ / 计算正矩阵元素和基于迭代器类型的变量之和

double sum = 0;

Mat Const Iterator_ <double> it =M.begin <double> (),it_end = M.end <double> () ;

for(; it! = it_end ; ++it)

sum+ = std::max (*it,0.);

矩阵迭代器是随机存取的迭代器,所以他们可以被传递给任何 STL 算法,包括 std::sort()。

矩阵表达式

这是已经实现的可以组合在任意复杂的表达式中的矩阵运算操作, (此处 A 、B 的表示矩阵 (Mat)、 s表示标量(Scalar),alpha为实数标量 (双精度型):

加法、减法、求反: A + B + A-B、 A + s、 A-s、 s + A、 s-A、-A;

缩放: A * alpha

每个元素乘法和除法: A.mul (B)、 A / B,alpha/A

矩阵相乘: A * B

大动脉转位: A.t() (指在)

矩阵反演和伪反演,求解线性系统和最小二乘问题:

A.inv([method]) (~ A-1) , A.inv([method])*B (~ X: AX=B)

比较: cmpop B、 cmpop alpha、 alpha cmpop A,其中 cmpop 是以下几种运算符之一: >,> =,= =,! =,< =,<。比较的结果是其元素设置为 255的 8 位单通道掩码(如果特殊元素对满足条件) 或 0。

按位逻辑运算: logicop B、 logicop s slogicop A、 ~ A,其中 logicop 是以下运算符之一: &,|, ^.

元素的最小值和最大值:分 (A、 B)、 民 (,alpha),最大值 (A,B),最大 (,alpha)

元素的绝对价值: abs(A)

叉乘,点乘: A.cross(B) A.dot(B)

任何标量与矩阵或矩阵的函数,返回一个矩阵或标量(scalar),如norm、, mean、 sum、countNonZero、trace、determinant、repeat和其他。

矩阵初始值设定项(Mat::eye(),Mat::zeros(),Mat::ones())、矩阵以逗号分隔的初始值设定项、可提取sub-matrices的m atrix构造函数和运算符,(请参见Mat的说明)。

Mat_ <destination_type> () 构造函数将结果强制转换为适当的类型。

Note:有些逗号分隔初始值设定项和一些其他的运算符可能需要显示调用Mat();或Mat_<T>();的构造函数来解决可能产生的歧义。

以下是一些矩阵表达式的例子:

//计算矩阵A的伪反演等价于A.inv(DECOMP_SVD)

SVD svd(A);

Mat pinvA =svd.vt.t()*Mat::diag(1./svd.w)*svd.u.t();

//计算莱文伯格-马夸特算法中的参数的新向量

x -= (A.t()*A +lambda*Mat::eye(A.cols,A.cols,A.type())).inv(DECOMP_CHOLESKY)*(A.t()*err);

//用“Unsharp Mask”算法锐化图像

Mat blurred; double sigma = 1, threshold =5, amount = 1;

GaussianBlur(img, blurred, Size(), sigma,sigma);

Mat lowConstrastMask = abs(img - blurred)< threshold;

Mat sharpened = img*(1+amount) +blurred*(-amount);

img.copyTo(sharpened, lowContrastMask);

下面正式讲解Mat的各种方法。

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, constScalar& 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, constRange& rowRange, const Range& colRange)

C++: Mat::Mat(const Mat& m, constRect& roi)

C++: Mat::Mat(const CvMat* m, boolcopyData=false)

C++: Mat::Mat(const IplImage* img, boolcopyData=false)

C++: template<typename T, int n>explicit Mat::Mat(const Vec<T, n>& vec, bool copyData=true)

C++: template<typename T, int m, intn> explicit Mat::Mat(const Matx<T, m, n>& vec, bool copyData=true)

C++: template<typename T> explicitMat::Mat(const vector<T>& vec, bool copyData=false)

C++: Mat::Mat(const MatExpr& expr)

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, constRange* ranges)

参数

ndims– 数组的维数.

rows – 2维数组中行行数

cols – Number of columnsin a 2D array.

size – 2维数组的尺寸Size(cols, rows) .在Size()构造函数中行数和列数在次序上刚好反转过来了。

sizes–指定 n 维数组形状的整数数组。

type–数组的类型。使用 CV_8UC1,… …,创建 1-4 通道的矩阵,CV_64FC4 或CV_8UC(n),… …,CV_64FC(n)可以创建多通道 (高达 CV_MAX_CN 通道)矩阵。

s–一个可选的初始化每个矩阵元素的参数。要在矩阵建成后将所有元素设置为特定值可以用Mat的赋值运算符Mat:operator=(constScala& value)。

data–指向用户数据的指针。矩阵构造函数传入data和step参数不分配矩阵数据。相反,它们只是初始化矩阵头指向指定的数据,这意味着没有数据的复制。此操作是很高效的,可以用来处理使用 OpenCV 函数的外部数据。外部数据不会自动释放,所以你应该小心处理它。

step–每个矩阵行占用的字节数。如果任何值应包括每行末尾的填充字节。如果缺少此参数(设置为 AUTO_STEP),假定没有填充和实际的步长用cols*elemSize()计算。请参阅Mat::elemSize()。

steps–多维数组(最后一步始终设置为元素大小) 的情况下的 ndims-1个步长的数组。如果没有指定的话,该矩阵假定为连续。

m–分配给构造出来的矩阵的阵列(作为一个整体或部分)。这些构造函数没有复制数据。相反,指向 m 的数据或它的子数组的头被构造并被关联到m上。引用计数器中无论如何都将递增。所以,当您修改矩阵的时候,自然而然就使用了这种构造函数,您还修改 m 中的对应元素。如果你想要独立的子数组的副本,请使用 Mat::clone()。

img –指向老版本的 IplImage图像结构的指针。默认情况下,原始图像和新矩阵之间共享数据。但当 copyData 被设置时,完整的图像数据副本就创建起来了。

vec–矩阵的元素构成的STL 向量。矩阵可以取出单独一列并且该列上的行数和矢量元素的数目相同。矩阵的类型匹配的向量元素的类型。构造函数可以处理任意的有正确声明的DataType类型。这意味着矢量元素不支持的混合型结构,它们必须是数据(numbers)原始数字或单型数值元组。对应的构造函数是显式的。由于 STL 向量不会自动转换为Mat实例,您应显式编写 Mat(vec)。除非您将数据复制到矩阵 (copyData = true),没有新的元素被添加到向量中,因为这样可能会造成矢量数据重新分配,并且因此使得矩阵的数据指针无效。

copyData –指定STL 向量或旧型 CvMat 或 IplImage是应复制到 (true)新构造的矩阵中 还是 (false) 与之共享基础数据的标志,复制数据时,使用Mat引用计数机制管理所分配的缓冲区。虽然数据共享的引用计数为 NULL,但是分配数据必须在矩阵被析构之后才可以释放。

rowRange – m 的行数的取值范围。正常情况下,范围开始端具有包容性和范围结束端是独占的。使用 Range::all() 来取所有的行。

colRange –m 列数的取值范围。使用 Range::all() 来取所有的列。

ranges –表示M沿每个维度选定的区域的数组。

expr – 矩阵表达式。请参见矩阵表达式。

以上这些都是Mat生成一个矩阵的各类构造函数。如 输出数据的自动分配 一节(该节内容在第一章 Introduction)中所提到的,往往默认构造函数就足够了,不同的矩阵可以由 OpenCV 函数来分配数据空间。构造的矩阵可以进一步分配给另一个矩阵或矩阵表达或通过Mat::create()获配。在前一种情况,旧的内容是间接引用的。

时间: 2024-11-09 02:16:33

Mat类详解(一)的相关文章

OpenCV参考手册之Mat类详解

目标 我们有多种方法可以获得从现实世界的数字图像:数码相机.扫描仪.计算机体层摄影或磁共振成像就是其中的几种.在每种情况下我们(人类)看到了什么是图像.但是,转换图像到我们的数字设备时我们的记录是图像的每个点的数值. 例如在上图中你可以看到车的镜子只是一个包含所有强度值的像素点矩阵.现在,我们如何获取和存储像素值可能根据最适合我们的需要而变化,最终可能减少计算机世界内的所有图像数值矩阵和一些其他的信息的描述基质本身.OpenCV 是一个计算机视觉库,其主要的工作是处理和操作,进一步了解这些信息.

OpenCV参考手册之Mat类详解(二)

转自 http://blog.csdn.net/giantchen547792075/article/details/7169255 Mat::~Mat Mat的析构函数. C++: Mat::~Mat() 析构函数调用Mat::release(). Mat::operator = 提供矩阵赋值操作. C++: Mat& Mat::operator=(const Mat& m) C++: Mat& Mat::operator=(const MatExpr_Base& exp

Mat类详解(二)

Mat::~Mat Mat的析构函数. C++: Mat::~Mat() 析构函数调用Mat::release(). Mat::operator = 提供矩阵赋值操作. C++: Mat& Mat::operator=(const Mat& m) C++: Mat& Mat::operator=(const MatExpr_Base& expr) C++: Mat& Mat::operator=(const Scalar& s) 参数: m – 被赋值的右侧

QAction类详解:

先贴一段描述:Qt文档原文: Detailed Description The QAction class provides an abstract user interface action that can be inserted into widgets. In applications many common commands can be invoked via menus, toolbar buttons, and keyboard shortcuts. Since the user

Android技术18:Android中Adapter类详解

1.Adapter设计模式 Android中adapter接口有很多种实现,例如,ArrayAdapter,BaseAdapter,CursorAdapter,SimpleAdapter,SimpleCursorAdapter等,他们分别对应不同的数据源.例如,ArrayAdater对应List和数组数据源,而CursorAdapter对应Cursor对象(一般从数据库中获取的记录集).这些Adapter都需要getView方法返回当前列表项显示的View对象.当Model发生改变时,会调用Ba

C++虚基类详解

1.虚基类的作用从上面的介绍可知:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员.在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如    c1.A::display( ).在一个类中保留间接共同基类的多份同名成员,这种现象是人们不希望出现的.C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员.现在,将类A声明为

URLConnection类详解

为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3753224.html URLConnection概述 URLConnection是一个抽象类,表示指向URL指定资源的活动连接. URLConnection类本身依赖于Socket类实现网络连接.一般认为,URLConnection类提供了比Socket类更易于使用.更高级的网络连接抽象.但实际上,大多数程序员都会忽略它

ThreadLocal类详解

众所周知,ThreadLocal对象可以每一个线程保存一份值,可以避免因线程间共享数据带来的问题. 其实现的原理,大致如下,具体的可以参考JDK里的源码. Thread类中,有一个threadLocals字段,它是ThreadLocalMap类型(ThreadLocal里的一个静态内部类).该字段存放当前线程下,所有与ThreadLocal相关的值.该对象是一个Map,key为ThreadLocal对象,value为所存放的值. 在ThreadLocal类里,有两个重要的方法:set()和get

Cocos2d之Node类详解之节点树(二)

一.声明 本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可. 笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析.这篇文章承接上篇<Cocos2d之Node类详解之节点树(一)>. 二.简介 节点 一个Node对象. 节点树 上篇文章介绍到,Node类有一个成员变量 Vector<Node*> _children,这是一个保存所有子节点的数组,因为Node类采用遍历树的方式获取子节点进行渲染,所以我管这两个东西的结合叫节点树. 三.源码详解 &