10、OpenCV中图像和Mat类型(一)

Mat类型可以被认为是OpenCV库的核心 。 OpenCV库中绝大多数的函数都是Mat类的成员 , 以Mat作为参数 , 或者Mat作为返回值 。
如果你熟悉OpenCV库的C接口(2.1之前的版本) , 您将会记住IplImage和CvMat的数据类型 。 你也可能记得CvArr 。 在C++实现中 , 所有这些都消失了 , 用Mat代替 。 Mat类可以用于任何维数的数组 。 数据被存储在阵列中 , 被认为是"光栅扫描顺序"的n维数字 。 这意味着在一维数组中 , 元素是顺序的 。 在二维数组中 , 数据按行组织 , 每行依次出现 。 对于三维阵列 , 每个平面都是逐行填充的 。
【10、OpenCV中图像和Mat类型(一)】每个Mat都包含一个标志元素 , 指示数组内容 , 一个dims元素指示维数 , rows和cols元素指示行数和列数 , 指向数据指针的位置数组数据被存储 , 一个类似于Ptr<>的的引用计数器 。 数据数组被布置成使得其索引由(i0 , ii , ... , iNd-1)给出的元素的地址是:
在二维数组的简单情况下 , 这可以简化为:
&(mtxi,j)=mtx.data+mtx.step0*i+mtx.step1*j
Mat中每个数据元素本身可以是单个数字 , 也可以是多个数字 。 在多个数字的情况下 , 这就是多通道数组 。 一个数组可能被认为是一个32位浮点数的二维三通道数组;在这种情况下 , 数组的元素是三个32位浮点数 , 大小为12个字节 。 在内存中布局时 , 数组的行可能不是绝对顺序的;在下一个之前可能会有小的间隙缓冲每一行 。 一个n维单通道阵列和一个(n-1)维多通道阵列之间的区别在于 , 这个填充将始终发生在整行的末尾,即元素中的通道将始终是连续的) 。
可以简单地通过实例化一个类型为Mat的变量来创建一个数组 。 以这种方式创建的数组没有大小和数据类型 。 但是 , 可以使用create()等成员函数分配数据 。 create()的一个变体将多个行 , 多个列和一个类型作为参数 , 并将该数组表示为一个二维对象 。 数组的类型决定了它具有哪种元素以及通道的数量 。 所有这些类型都在库中定义 , 并具有CV_{8U , 16S , 16U , 32S , 32F , 64F}C{1,2,3}的格式 。 例如 , CV_32FC3意味着一个32位浮点三通道阵列 。
也可以在首次分配矩阵时指定这些内容 。 Mat有许多构造函数 , 其中一个与create()具有相同的参数 。 例如:
cv::Matm;
//创建3行10列3通道32位浮点型数据
m.create(3,10,CV_32FC3);
//设置第一个通道为1.0 , 第二个通道为0.0 , 第三个通道为1.0
m.setTo(cv::Scalar(1.0f,0.0f,1.0f));
//上面的定义与下面的语句等价
Matm(3,10,CV_32FC3,cv::Scalar(1.0f,0.0f,1.0f));
Mat对象实际上是数据区域的头 , 原则上它是一个完全独立的东西 。 例如 , 可以将一个矩阵n分配给另一个矩阵m(即 , m=n) 。 在这种情况下 , m中的数据指针将被改变为指向与n相同的数据 。 先前由m的数据元素指向的数据将被释放 。 同时 , 它们现在共享的数据区域的引用计数器将递增 。 同时将更新m成员的数据(如行 , 列和标志) , 以准确描述m中数据指向的数据 。
表1是Mat的构造函数的完整列表 。 但事实上 , 大多数时候可能使用其中的一小部分 。
表2的复制构造函数显示了如何从另一个数组创建一个数组 。 除了基本的复制构造函数外 , 还有三种方法用于从现有数组的子区域构建数组 , 以及使用某个矩阵表达式的结果初始化新矩阵的构造函数 。
有时候我们需要访问数组中的某一块元素 , 可能是选择一行或一列 , 或原始数组的任何子区域 。 有很多方法可以做到这一点 , 表6是Mat类的成员函数 , 并返回调用它们的数组的子部分 。
除了从m.diag()返回的数组引用矩阵的对角线元素之外 , 成员函数diag()与row()或col()的作用相同 。 m.diag()需要一个整数参数 , 用于指示要提取哪个对角线 。 如果该参数为零 , 那么它将是主对角线 。 如果是正数 , 则它将从阵列上半部分的主对角线偏移该距离 。 如果它是负数 , 那么它将来自阵列的下半部分 。
提取子矩阵的最后一种方法是使用operator() 。 使用这个运算符 , 你可以传递一对范围(行的Range和列的Range)或者从Rect指定你想要的区域 。


    推荐阅读