一、首先我们先看什么是Mat:
Mat类的对象用于表示一个多维度的单通道或者多通道稠密数组,它可以用来存储:实数值或复合值向量、矩阵、灰度图或者彩色图、立体元素、矢量场、点云、张量、直方图。可见Mat是一个强大的数据容器,是对数据进行操作的前提。opencv中比较旧的版本用的是cvMat和IplImage,这两个偏向于图像,内存的管理也相对麻烦。而opencv2.0后的Mat可高效地进行数学计算,且方便地管理内存,这也是为什么Mat渐渐取代cvMat和IplImage的原因。
二、Mat中重要的几个成员:
//! returns element size in bytes,
// similar to CV_ELEM_SIZE(cvmat->type)
size_t elemSize() const;
//! returns the size of element channel in bytes.
size_t elemSize1() const;
//! returns element type, similar to CV_MAT_TYPE(cvmat->type)
int type() const;
//! returns element type, similar to CV_MAT_DEPTH(cvmat->type)
int depth() const;
//! returns element type, similar to CV_MAT_CN(cvmat->type)
int channels() const;
//! returns step/elemSize1()
size_t step1(int i=0) const;
//! the matrix dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
也就是:
channels:我们熟悉的是彩色图像三通道RGB,也就是矩阵中每一个元素值的个数,就像一个彩色像素点它由R、G、B三个混合而成。处于某行某列的像素点有三个值分别为RGB。
depth:深度是用来度量每一个像素的精度,每个像素的位数(bit)也就是深度越深,精度越高。在opencv的Mat.depth()中得到的是一个 0 – 6 的数字,分别代表不同的位数:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3,CV_32S=4, CV_32F=5, CV_64F=6 }; 其中U表示unsigned的意思,S表示signed,也就是有无符号。我们可以看作以8U为基本单位~
data:显然,它是一个指向内存中存放矩阵数据的一块内存的指针,用这个指针来访问数据的速度是最快的,但是由于在数据存储过程中还会有一些其他规则,如为了访问效率,矩阵中的内存分配上,是以每四个字节做为最小单位的。因此如果一个矩阵的宽度是三个字节,那么就会在宽度上分配四个字节,而此时每行最后一个字节会被忽略掉。用这个指针来访问的话就会出错。
dim:维度,这个很容易理解。
rows/cols:行列数。
step:是一个比较好玩的东西,它是以一个数组的形式出现。它定义了矩阵的整体布局。另外注意 step1 (step / elemSize1),M.step[m-1] 总是等于 elemSize,M.step1(m-1)总是等于 channels;
elemSize : 矩阵中每一个元素的数据大小,如果Mat中的数据的数据类型是 CV_8U 那么 elemSize = 1,CV_8UC3 那么 elemSize = 3,CV_16UC2 那么 elemSize = 4;记住另外有个 elemSize1 表示的是矩阵中数据类型的大小,即 elemSize / channels 的大小
说了那么多好像听着都混乱了,来点图片形象点的吧:
我们想一想为什么要有step?就像上面说的,它定义了矩阵的整体布局。因为根据它,我们就能找到Mat中每一个数据元素。
我们假设这是一个5*3的二维数据,数据类型为CV_8CU3,上面的格式就是它在Mat中存储的样子,首先它是一个三通道的数据,用我们熟悉的彩色图片RGB来表示,图像在Mat的存储刚好为BGR,调过来。那我们看到,我们想访问最右下角的那个像素点其为M(4,2)【记得下标从0开始】,step是如何帮忙的呢?这时候step是二维矩阵,step[0]和step[1]~那么M点的地址是M.data + M.step[0] * row + M.step[1] * col,其中row为4,col为2。M.data是指向起始位置,也就是左上角第一个蓝色,我把它颜色加深了一点。那step[0]表示的是每一行元素的数据大小是多少,最确切的来说每一行由多少个8bit排列而成。这里因为数据类型为CV_8CU3,每个像素点有三个通道,所以step[0]=3*3=9。M.data + M.step[0] * row 这时候的指针就跑到了第五行第一个蓝色那里,step[1]表示的是一个元素的数据大小,也就是一个像素由多少个8bit排列而来,显然这里是3。最后M.data + M.step[0] * row + M.step[1] * col 这个指的就是最后一个像素点的B通道。也可以看出来,elemSize这里表示的是每个像素大小,其由三个通道组成,也就是3,elemSize1是1,也就是每个通道数据大小。我们如果想单独对R通道进行处理,那么elemSize1就可以派上用场啦,M.data + M.step[0] * row + M.step[1] * col + M.elemSize1() * 2就指向了红色通道。为方便理解,可以这么说,elemSize1是为通道服务的,它的大小就是通道的大小,以8为基本单位。
像下面的图:
数据类型变为CV_16U3,那么step[0],step[1],elemSize,elemSize1如何变化呢,显然,step[0]表示每一行数据大小,以8为单位,它就为16/8*3*2=12,step[1]为2*3=6,
elemSize这里是2*3,elemSize1每个通道数据大小是2。也始终有elemSize1=elemSize / channels。
可以看出来,这种通过step的寻址方式是以像素为单位去找的,它要操作的是像素,最小单位是像素,所以step[1]对应的是像素大小;而通过elemSize1的寻址方式是以通道为最小单位,它要操作的通道,所以elemSize对应的是每个通道的大小。
同理,如果是三维的话,step就是三维数组,step[0]、step[1]、step[2],step[0]表示面的大小,step[1]表示行的大小,step[2]表示像素的大小。
三、类型转换:由IpImage 转为Mat
/IplImage* img = cvLoadImage("lena.jpg", 1);
Mat mtx(img); // convert IplImage* -> Mat
四、关于深浅拷贝问题
我们对Mat的初始化有多种多样,这里涉及到几种如下:
Mat image = imread("lena.jpg");
//Mat result = image;
//result = image.clone();
//image.copyTo(result);
第一种是直接用=号,此时result和 image 共用的是同一个数据段,result中的data指针指向image中,它们只是拥用不同的段头,当两者中的其中一个数据发生变化时,就会影响另一个,这叫做
浅拷贝。但是后面的clone和copyTo,是真正的把数据拷贝过去,其中一个改变数据不会影响另一个,叫做
深拷贝。用的时候要特别注意~
五、对数据访问的方式效率:
常用到的方法是:M.at<float>(i, j)、M.ptr<float>( i )[ j ]、M.data指针,在debug模式下,由于at操作符或者ptr操作符,有内存检查以防止操作越界的,而直接使用data这个指针却没有,所以它的速度最快。速度M.data指》M.ptr<float>( i )[ j ]》M.at<float>(i, j),但在发行版的release下三都的速度都是差不多的~用getTickCount、getTickFrequency两个函数就可以对程序进行运行时计数,记得加头文件Windows.h:具体的测试方法参考:
http://blog.csdn.net/yang_xian521/article/details/7161335
六、常见类型转换:http://blog.csdn.net/songzitea/article/details/8459882
六Basic Structures
main data structures used in opencv.
OpenCV中的 C 结构 | OpenCV中的 C++ 封装 | EmguCV中的 C# 封装 |
CvPoint CvPoint2D32f CvPoint2D64f CvPoint3D32f CvPoint3D64f | Point_<typename _Tp> Point3_<typename _Tp> Point_<int>(Point2i, Point) Point_<float>(Point2f) Point_<double>(Point2d) Point3_<float>(Point3f) Point3_<double>(Point3d) Point3_<int>(Point3i) | System.Drawing.Point System.Drawing.PointF MCvPoint2D64f MCvPoint3D32f MCvPoint3D64f |
CvSize CvSize2D32f | Size_<typename _Tp> Size_<int>(Size, Size2i) Size_<float>(Size2f) | System.Drawing.Size System.Drawing.SizeF |
CvRect | Rect_<typename _Tp> Rect_<int>(Rect) | System.Drawing.Rectangle |
CvScalar (A container for 1-,2-,3-or4-tuples of doubles) | Scalar_<typename _Tp> Scalar_<double>(Scalar) (:public Vec<_Tp, 4>) (Scalar is widely used to pass pixel values) | MCvScalar |
CvBox2D | RotatedRect | MCvBox2D |
CvMat (A multi-channel dense matrix) | Mat | MCvMat MCvHistogram Matrix<TDepath> |
CvMatND (Multi-dimensional dense multi-channel array) | Mat | MCvMatND MatND<TDepth> |
IplImage | Mat | MIplImage Image<TColor, TDepth> |
CvSparseMat | SparseMat | SparseMatrix<TDepath> |
CvArr (“metatype”only used as function parameter) | InputArray OutputArray | CvArray<TDepth> |
CvTermCriteria (Termination criteria for iterative algorithms) | TermCriteria | MCvTermCriteria |
Dynamic Structures
for creating growable sequences and other dynamic data structures allocated in CvMemStorage. If you use the new C++, Python, Java etc interface, you will unlikely need this functionality. Use std::vector or other high-level data structures instead.
OpenCv中的 C 结构 | OpenCV中的 C++ 封装 | Emgu.CV中的 C# 封装 |
CvMemStorage | MemStorage | MemStorage |
CvMemBlock | ||
CvMemStoragePos | ||
CvSeq | Seq<typename _Tp> | MCvSeq Seq<T> |
CvSlice | Range | MCvSlice |
CvSet (derived from CvSeq) | MCvSet | |
CvGraph (derived from CvSet) | ||
CvGraphScanner (used for depth-first graph traversal) | ||
CvTreeNodeIterator (used to traverse trees of sequences) |
Extra C++ Basic Structures
some basic structures in C++ version.
Matx<typename _Tp, int m, int n>
- typedef Matx<float, 1, 2> Matx12f;
- typedef Matx<double, 6, 6> Matx66d;
Vec<typename _Tp, int n> (:public Matx<_Tp, n, 1>)
- typedef Vec<uchar, 2> Vec2b;
- typedef Vec<short, 4> Vec4s;
- typedef Vec<int, 3> Vec3i;
- ...float,double...
Ptr<typename _Tp> for smart reference-counting pointers
Matrix Expressions: (Mat A, B; Scalar s; double alpha)
- Addition, substraction, negation: A + B, A - B, A + s, A - s, s - A, -A
- Scaling: A * alpha
- Per-element multiplication and division: A.mul(B), A/B, alpha/A
- Matrix multiplication: A*B
- Transposition: A.t() (means AT)
- Matrix inversion and pseudo-inversion, solving linear systems and least-squares problems: A.inv([method]) (~ A-1) , A.inv([method])*B (~ X: AX=B)
- Comparison: A cmpop B, A cmpop alpha, alpha cmpop A, where cmpop is one of : >, >=, ==, !=, <=, <. The result of comparison is an 8-bit single channel mask whose elements are set to 255 (if the particular element or pair of elements satisfy the condition) or 0.
- Bitwise logical operations: A logicop B, A logicop s, s logicop A, ~A, where logicop is one of : &, |, ^.
- Element-wise minimum and maximum: min(A, B), min(A, alpha), max(A, B), max(A, alpha)
- Element-wise absolute value: abs(A)
- Cross-product, dot-product: A.cross(B) A.dot(B)
- Any function of matrix or matrices and scalars that returns a matrix or a scalar, such as norm, mean, sum, countNonZero, trace, determinant, repeat, and others.
- Matrix initializers ( Mat::eye(), Mat::zeros(), Mat::ones() ), matrix comma-separated initializers, matrix constructors and operators that extract sub-matrices (see Mat description).
- Mat_<destination_type>() constructors to cast the result to the proper type.
参考:
http://blog.csdn.net/yang_xian521/article/details/7161335
OpenCV中对Mat里面depth,dims,channels,step,data,elemSize和数据地址计算的理解