OpenCV_1

Mat

(1)为其开辟空间

(2)在不需要时立即将空间释放

 

Mat 是一个类  

  由两个部分组成:矩阵头(矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针。

 

同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝 的图像,因为这会降低程序速度。

每个Mat都有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。

而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。

1 Mat A, C;                                 // 只创建信息头部分
2 A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
3 
4 Mat B(A);                                 // 使用拷贝构造函数
5 
6 C = A;                                    // 赋值运算符

以上代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。

 

虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象

1 Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
2 Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

现在你也许会问,如果矩阵属于多个 Mat 对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人拷贝了一个 Mat 对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数值为零,矩阵会被清理。

 

但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。

 

1 Mat F = A.clone();
2 Mat G;
3 A.copyTo(G);

现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。

 

总结一下,你需要记住的是

  • OpenCV函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。
  • 使用OpenCV的C++接口时不需要考虑内存释放问题。
  • 赋值运算符和拷贝构造函数( ctor )只拷贝信息头。
  • 使用函数 clone() 或者 copyTo() 来拷贝一副图像的矩阵。

常用的构造函数有:
Mat::Mat() 无参数构造方法;


Mat::Mat(int rows, int cols, int type) 创建行数为 rows,列数为 col,类型为 type 的图像;


Mat::Mat(Size size, int type) 创建大小为 size,类型为 type 的图像;


Mat::Mat(int rows, int cols, int type, const Scalar& s)
创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始 化为值 s;


Mat::Mat(Size size, int type, const Scalar& s) 创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s;


Mat::Mat(const Mat& m) 将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m和新对象共用图像数据;


Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP) 创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建 图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。


Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP) 创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。


Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange) 创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据;


Mat::Mat(const Mat& m, const Rect& roi) 创建的新图像为m的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与m 共用图像数据。

这些构造函数中, 很多都涉及到类型 type。 type 可以是 CV_8UC1, CV_16SC1, …,CV_64FC4 等。里面的 8U 表示 8 位无符号整数, 16S 表示 16 位有符号整数, 64F 表示 64 位浮点数(即 double 类型); C 后面的数表示通道数,例如 C1 表示一个 通道的图像, C4 表示 4 个通道的图像,以此类推。

 

CvScalar定义可存放1—4个数值的数值,其结构体如下:

typedef struct CvScalar
{
    double val[4];
}CvScalar;

例如:CvScalar s;

如果使用的图像是1通道的,则s.val[0]中存储数据

如果使用的图像是3通道的,则s.val[0],s.val[1],s.val[2]中存储数据

Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。创建一个Mat对象有多种方法:

1 Mat M(2,2, CV_8UC3, Scalar(0,0,255)); 
2 cout << "M = " << endl << " " << M << endl << endl;  

  

Demo image of the matrix output

Scalar 是个short型vector,指定这个能够使用指定的定制化值来初始化矩阵。
在 C\C++ 中通过构造函数进行初始化
1 int sz[3] = {2,2,2}; 
2 Mat L(3,sz, CV_8UC(1), Scalar::all(0));

  

Mat的作用

The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ).

上面的一段话引用自官方的文档,Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵,灰度或彩色图像,立体元素,点云,张量以及直方图(高维的直方图使用SparseMat保存比较好)。简而言之,Mat就是用来保存多维的矩阵的。

 

Mat的常见属性

  • data  uchar型的指针。Mat类分为了两个部分:矩阵头和指向矩阵数据部分的指针,data就是指向矩阵数据的指针。
  • dims 矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3.
  • rows  矩阵的行数
  • cols   矩阵的列数
  • size 矩阵的大小,size(cols,rows),如果矩阵的维数大于2,则是size(-1,-1)
  • channels 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则channels = 3
  • type 
    表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值: 
    CV_8UC1 CV_8UC2 CV_8UC3 CV_8UC4
    CV_8SC1 CV_8SC2 CV_8SC3 CV_8SC4
    CV_16UC1 CV_16UC2 CV_16UC3 CV_16UC4
    CV_16SC1 CV_16SC2 CV_16SC3 CV_16SC4
    CV_32SC1 CV_32SC2 CV_32SC3 CV_32SC4
    CV_32FC1 CV_32FC2 CV_32FC3 CV_32FC4
    CV_64FC1 CV_64FC2 CV_64FC3 CV_64FC4
    这里U(unsigned integer)表示的是无符号整数,S(signed integer)是有符号整数,F(float)是浮点数。 
    例如:CV_16UC2,表示的是元素类型是一个16位的无符号整数,通道为2. 
    C1,C2,C3,C4则表示通道是1,2,3,4 
    type一般是在创建Mat对象时设定,如果要取得Mat的元素类型,则无需使用type,使用下面的depth
  • depth 
    矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S。depth也是一系列的预定义值, 
    将type的预定义值去掉通道信息就是depth值: 
    CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
  • elemSize 
    矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes
  • elemSize1 
    矩阵元素一个通道占用的字节数,例如:type是CV_16CS3,那么elemSize1 = 16  / 8 = 2 bytes = elemSize / channels

通过相关返回值的On-the-fly地址计算

事实上这个方法并不推荐被用来进行图像扫描,它本来是被用于获取或更改图像中的随机元素。它的基本用途是要确定你试图访问的元素的所在行数与列数。在前面的扫描方法中,我们观察到知道所查询的图像数据类型是很重要的。这里同样的你得手动指定好你要查找的数据类型。下面的代码中是一个关于灰度图像的示例(运用 + at() 函数):

该函数输入为数据类型及需求元素的坐标,返回的是一个对应的值。如果用 get ,则是constant,如果是用 set ,则为non-constant. 处于程序安全,当且仅当在 debug 模式下 它会检查你的输入坐标是否有效或者超出范围. 如果坐标有误,则会输出一个标准的错误信息. 和高性能法(the efficient way)相比, 在 release模式下,它们之间的区别仅仅是On-the-fly方法对于图像矩阵的每个元素,都会获取一个新的行指针,通过该指针和[]操作来获取列元素.

当你对一张图片进行多次查询操作时,为避免反复输入数据类型和at带来的麻烦和浪费的时间,OpenCV 提供了:basicstructures:Mat_ <id3> data type. 它同样可以被用于获知矩阵的数据类型,你可以简单利用()操作返回值来快速获取查询结果. 值得注意的是你可以利用at() 函数来用同样速度完成相同操作. 它仅仅是为了让懒惰的程序员少写点 >_< 

 

 

posted @ 2015-03-20 22:15  benbenTang  阅读(162)  评论(0编辑  收藏  举报