OPENCVMAT类详解

OPENCVMAT类详解
OPENCVMAT类详解

类Mat导言

OpenCV c + + n 维稠密数组类

类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,。。。。i M.dims-1),其中0<= i k< M.size [k],可以计算为:

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

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

addr( M i,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_ (3,3) <<1,0,0,0,1,0,0,0,1);

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

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

Mat::release()。

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

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

假定M 一个双精度浮点型数组。有几个变体的不同方法来针对不同的维度数进行处理。如果您要处理整行的二维数组,最有效的方式是获取该行的头指针然后只需使用普通的C 运算符[]:

/ / 正矩阵元素之和计算

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

double sum = 0;

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

{

const double *Mi = M.ptr (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 (i) ;

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

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

}

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

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

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

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

double sum = 0;

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

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 * 阿尔法

每个元素乘法和除法: 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_ () 构造函数将结果强制转换为适当的类型。

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

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

//计算矩阵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)

相关主题
相关文档
最新文档