opencv学习笔记(1)--深度解析Mat

 

 

一、首先我们先看什么是Mat:

Mat类的对象用于表示一个多维度的单通道或者多通道稠密数组,它可以用来存储:实数值或复合值向量、矩阵、灰度图或者彩色图、立体元素、矢量场、点云、张量、直方图。可见Mat是一个强大的数据容器,是对数据进行操作的前提。opencv中比较旧的版本用的是cvMatIplImage,这两个偏向于图像,内存的管理也相对麻烦。而opencv2.0后的Mat可高效地进行数学计算,且方便地管理内存,这也是为什么Mat渐渐取代cvMatIplImage的原因。

二、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
CvBox2DRotatedRectMCvBox2D
CvMat (A multi-channel dense matrix)MatMCvMat
MCvHistogram
Matrix<TDepath>
CvMatND (Multi-dimensional dense multi-channel array)MatMCvMatND
MatND<TDepth>
IplImageMatMIplImage
Image<TColor, TDepth>
CvSparseMatSparseMatSparseMatrix<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# 封装
CvMemStorageMemStorageMemStorage
CvMemBlock  
CvMemStoragePos  
CvSeqSeq<typename _Tp>MCvSeq
Seq<T>
CvSliceRangeMCvSlice
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和数据地址计算的理解



  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值