OPENCV的MAT类详细讲解Word下载.docx
《OPENCV的MAT类详细讲解Word下载.docx》由会员分享,可在线阅读,更多相关《OPENCV的MAT类详细讲解Word下载.docx(17页珍藏版)》请在冰豆网上搜索。
;
:
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中的数据布局完全兼容OpenCV1.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填充的7x7复矩阵。
Mat
M(7,7,CV_32FC2,Scalar(1,3));
//现在将M转换为100x60的CV_8UC(15)的矩阵。
//旧内容将会被释放
M.create(100,60,CV_8UC(15));
这一章导言中指出,当当前的数组与指定的数组的形状或类型create()分配唯一的新数组时的形状或类型。
创建多维数组:
//创建100x100x1008位数组
intsz[]={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);
//创建一种新的320x240图像
Matimg(Size(320,240),CV_8UC3);
//选择ROI(regionofinterest)
Matroi(img,Rect(10,10,100,100));
//填充(0,255,0)的ROI(这是RGB空间中的绿色);
//320x240原始图像将被修改。
roi=Scalar(0,255,0);
由于额外的datastart和dataend的成员,它们使得用locateROI()计算子数组在主容器数组中的相对的位置成为可能:
MatA=Mat:
eye(10,10,CV_32S);
//提取A的1(含)到3(不包含)列。
MatB=A(Range:
all(),Range(1,3));
//提取B的5(含)到9(不包含)行。
//即C~A(Range(5,9),Range(1,3))
MatC=B(Range(5,9),Range:
all());
Sizesize;
Pointofs;
C.locateROI(size,ofs);
//size将变为(width=10,height=10),ofs会变为(x=1,y=5)
考虑到整个矩阵,如果您需要深层副本,使用子矩阵的sclone()方法的提取。
为用户分配数据创建矩阵头。
有利于执行下列操作:
1.使用OpenCV处理"
外来"
的数据(例如,当您执行DirectShow*filter或gstreamer的pro-cessing模块,等等)。
例如:
voidprocess_video_frame(constunsignedchar*pixels,
intwidth,intheight,intstep)
Matimg(width,height,CV_8UC3,pixels,step);
GaussianBlur(img,img,Size(7,7),1.5,1.5);
}
2.快速初始化小矩阵和/或获取超快的元素的访问。
doublem[3][3]={{a,b,c},{d,e,f}{g,h,i}}};
MatM=Mat(3,3,CV_64F,m).inv();
本例中用户分配数据的一些很常见情况是从CvMat和IplImage转换到Mat。
为达到此目的,有些特殊的构造函数以指向CvMat或IplImage和flag可选参数指示是否数据复制。
从Mat到CvMat或IplImage的后台转换是通过类型转换运算符Mat:
operatorCvMat()const和Mat:
operatorIplImage()实现的。
operators不要复制数据。
IplImage*img=cvLoadImage("
greatwave.jpg"
1);
Matmtx(img);
//IplImage*->
Mat
CvMatoldmat=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);
使用逗号分隔的初始值设定项:
//创建3x3双精度恒等矩阵
MatM=(Mat_<
double>
(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()方法检索得到),您可以用以下方式访问二维数组的元素Mij:
M.at<
(i,j)
+=1.f;
假定M一个双精度浮点型数组。
有几个变体的不同方法来针对不同的维度数进行处理。
如果您要处理整行的二维数组,最有效的方式是获取该行的头指针然后只需使用普通的C运算符[]:
//正矩阵元素之和计算
//(假定M是一个双精度矩阵)
doublesum=0;
for(inti=0;
i<
M.rows;
i++)
constdouble*Mi=M.ptr<
(i);
for(intj=0;
j<
M.cols;
j++)
sum+=std:
max(Mi[j],0.);
以上的操作中,某些操作实际上不依赖该数组的形状。
他们只是一个接一个(或多个具有相同的坐标的多个数组中的元素,例如,数组相加)地处理数组元素。
这种操作称为元素指向(element-wise)。
检查是否所有的输入/输出阵列是连续的,即有没有间断在每行的结尾,是有意义的。
如果是的话,将它们(这些数组)作为单独的一个长行来处理:
//计算正矩阵元素,优化的变量的总和
intcols=M.cols,rows=M.rows;
if(M.isContinuous())
cols*=rows;
rows=1;
rows;
constdouble*Mi=M.ptr<
(i);
for(intj=0;
cols;
j++)
max(Mi[j],0.);
对于连续的矩阵来说,外部循环体只需一次执行。
所以,开销是规模较小,
小型矩阵的情况下尤其明显。
最后,还有足以成功跳过连续的行之间的间隔智能的STL样式迭代器:
//计算正矩阵元素和基于迭代器类型的变量之和
MatConstIterator_<
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)
比较:
cmpopB、cmpopalpha、alphacmpopA,其中cmpop是以下几种运算符之一:
>
,>
=,==,!
=,<
比较的结果是其元素设置为255的8位单通道掩码(如果特殊元素对满足条件)或0。
按位逻辑运算:
logicopB、logicopsslogicopA、~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的matrix构造函数和运算符,(请参见Mat的说明)。
Mat_<
destination_type>
()构造函数将结果强制转换为适当的类型。
Note:
有些逗号分隔初始值设定项和一些其他的运算符可能需要显示调用Mat();
或Mat_<
T>
();
的构造函数来解决可能产生的歧义。
以下是一些矩阵表达式的例子:
//计算矩阵A的伪反演等价于A.inv(DECOMP_SVD)
SVDsvd(A);
MatpinvA=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);
//用“UnsharpMask”算法锐化图像
Matblurred;
doublesigma=1,threshold=5,amount=1;
GaussianBlur(img,blurred,Size(),sigma,sigma);
MatlowConstrastMask=abs(img-blurred)<
threshold;
Matsharpened=img*(1+amount)+blurred*(-amount);
img.copyTo(sharpened,lowContrastMask);
下面正式讲解Mat的各种方法。
Mat
各种Mat构造函数。
C++:
Mat:
Mat()
Mat(introws,intcols,inttype)
Mat(Sizesize,inttype)
Mat(introws,intcols,inttype,constScalar&
s)
Mat(Sizesize,inttype,constScalar&
Mat(constMat&
m)
Mat(introws,intcols,inttype,void*data,size_tstep=AUTO_STEP)
Mat(Sizesize,inttype,void*data,size_tstep=AUTO_STEP)
m,constRange&
rowRange,constRange&
colRange)
m,constRect&
roi)
Mat(constCvMat*m,boolcopyData=false)
Mat(constIplImage*img,boolcopyData=false)
template<
typenameT,intn>
explicitMat:
Mat(constVec<
T,n>
vec,boolcopyData=true)
typenameT,intm,intn>
explicitMat:
Mat(constMatx<
T,m,n>
typenameT>
explicitMat:
Mat(constvector<
vec,boolcopyData=false)
Mat(constMatExpr&
expr)
Mat(intndims,constint*sizes,inttype)
Mat(intndims,constint*sizes,inttype,constScalar&
Mat(intndims,constint*sizes,inttype,void*data,constsize_t*steps=0)
m,constRange*ranges)
参数
ndims–数组的维数.
rows
–2维数组中行行数
cols
–Numberofcolumnsina2Darray.
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列数的取值范围。
all()来取所有的列。
ranges
–表示M沿每个维度选定的区域的数组。
expr
–矩阵表达式。
请参见矩阵表达式。
以上这些都是Mat形成一个矩阵的各类构造函数。
如输出数据的自动分配中所提到的,往往默认构造函数就足够了,不同的矩阵可以由OpenCV函数来分配数据空间。
构造的矩阵可以进一步分配给另一个矩阵或矩阵表达或通过Mat:
create()获配。
在前一种情况,旧的内容是间接引用的。
~Mat
Mat的析构函数。
~Mat()
析构函数调用Mat:
operator=
提供矩阵赋值操作。
Mat&
operator=(constMat&
operator=(constMatExpr_Base&
operator=(constScalar&
参数:
m
–被赋值的右侧的矩阵。
矩阵的赋值是一个复杂度为O
(1)的操作。
这就意味着没有数据段复制并且有数量的递增两矩阵将使用同一引用计数器。
在给矩阵赋新数据之前先由M