类 Mat导言
OpenCV c + + n 维稠密数组类
类 CV_EXPORTS Mat
{
public:
/ / … …很多的方法...
...
int flags;(Note :目前还不知道flags做什么用的)
//!数组的维数,> = 2
int dims ;
//!行和列的数量或 (-1,-1) 此时数组已超过 2 维
int rows,cols;
//!指向数据的指针
uchar *data ;
//!指针的引用计数器 ;
/ / 阵列指向用户分配的数据时,当指针为 NULL
int * refcount ;
/ / 其他成员
...
};
Mat类表示一个 n维的密集数值单通道或多通道数组。它可以用于存储实数或复数值的向量和矩阵、灰度或彩色图像、体素、向量场、点云、张量、直方图(尽管较高维的直方图存储在SparseMat可能更好)。M 数组的数据布局是由阵列
addr( Mi0
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转换为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
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 *filter 或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 和flag可选参数指示是否数据复制。从Mat到 CvMat 或 IplImage 的后台转换是通过类型转换运算符Mat::operator CvMat() const 和 Mat::operatorIplImage()实现的。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)
假定 M 一个双精度浮点型数组。有几个变体的不同方法来针对不同的维度数进行处理。
如果您要处理整行的二维数组,最有效的方式是获取该行的头指针然后只需使用普通的 C运算符[]:
/ / 正矩阵元素之和计算
/ / (假定M 是一个双精度矩阵)
double sum = 0;
for (int i = 0 ;i < M.rows ; i + +)
{
const double *Mi = M.ptr <double> (i);
sum + = std::max(Mi [j],0.) ;
}
以上的操作中,某些操作实际上不依赖该数组的形状。他们只是一个接一个(或多个具有相同的坐标的多个数组中的元素,例如,数组相加)地处理数组元素。这种操作称为元素指向(element-wise)。检查是否所有的输入/输出阵列是连续的,即有没有间断在每行的结尾,是有意义的。如果是的话,将它们(这些数组)作为单独的一个长行来处理:
/ / 计算正矩阵元素,优化的变量的总和
double sum = 0;
int cols =M.cols,rows = M.rows ;
if(M.isContinuous())
{
}
for (int i = 0 ;i < rows; i + +)
{
const double * Mi = M.ptr <double>(i);
for (int j = 0; j < cols ; j ++)
}
对于连续的矩阵来说,外部循环体只需一次执行。所以,开销是规模较小,
小型矩阵的情况下尤其明显。
最后,还有足以成功跳过连续的行之间的间隔智能的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.inv([method]) (~ A-1) , A.inv([method])*B (~ X: AX=B)
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,constScalar& 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_tstep=AUTO_STEP)
C++: Mat::Mat(Size size, int type, void*data, size_tstep=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, intn>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(constvector<T>& vec, boolcopyData=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, constScalar& 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)
rows
cols
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
vec–矩阵的元素构成的STL向量。矩阵可以取出单独一列并且该列上的行数和矢量元素的数目相同。矩阵的类型匹配的向量元素的类型。构造函数可以处理任意的有正确声明的DataType类型。这意味着矢量元素不支持的混合型结构,它们必须是数据(numbers)原始数字或单型数值元组。对应的构造函数是显式的。由于STL 向量不会自动转换为Mat实例,您应显式编写 Mat(vec)。除非您将数据复制到矩阵 (copyData =true),没有新的元素被添加到向量中,因为这样可能会造成矢量数据重新分配,并且因此使得矩阵的数据指针无效。
copyData
rowRange
colRange
ranges
expr
以上这些都是Mat形成一个矩阵的各类构造函数。如输出数据的自动分配中所提到的,往往默认构造函数就足够了,不同的矩阵可以由OpenCV函数来分配数据空间。构造的矩阵可以进一步分配给另一个矩阵或矩阵表达或通过Mat::create()获配。在前一种情况,旧的内容是间接引用的。
原文地址http://blog.csdn.net/giantchen547792075/article/details/7061391