OpenCV入门教程(1)-创建Mate类

来源:互联网 发布:网络的好处例子 编辑:程序博客网 时间:2024/06/05 20:15

1、图像的基本表示

一副尺寸为 M × N 的图像可以用一个 M × N 的矩阵来表示,矩阵元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该点越亮。
一般来说,灰度图用 2 维矩阵表示,彩色(多通道)图像用 3 维矩阵(M × N × 3)表示。对于图像显示来说,目前大部分设备都是用无符号 8 位整数(类型为 CV_8U)表示像素亮度。

2、Mate类

早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。IplImage 和 CvMat 都是 C 语言的结构。使用这两个结构的问题是内存需要手动管理,开发者必须清楚的知道何时需要申请内存,何时需要释放内存。这个开发者带来了一定的负担,开发者应该将更多精力用于算法设计,因此在新版本的 OpenCV 中引入了 Mat 类。
新加入的 Mat 类能够自动管理内存。使用 Mat 类,你不再需要花费大量精力在内存管理上。而且你的代码会变得很简洁,代码行数会变少。但 C++接口唯一的不足是当前一些嵌入式开发系统可能只支持 C 语言,如果你的开发平台支持C++,完全没有必要再用 IplImage 和 CvMat。在新版本的 OpenCV 中,开发者依然可以使用 IplImage 和 CvMat,但是一些新增加的函数只提供了 Mat 接口。
Mat 类的定义如下所示,关键的属性如下方代码所示:

class CV_EXPORTS Mat{public:    //一系列函数    ...    /* flag 参数中包含许多关于矩阵的信息,如:    -Mat 的标识    -数据是否连续    -深度    -通道数目    */    int flags;    //矩阵的维数,取值应该大于或等于 2    int dims;    //矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1    int rows, cols;    //指向数据的指针    uchar* data;    //指向引用计数的指针    //如果数据是由用户分配的,则为 NULL    int* refcount;    //其他成员变量和成员函数    ...};

2.1、创建 Mat 对象

Mat 是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创建和操作多维矩阵。有多种方法创建一个 Mat 对象。

2.1.1构造函数方法

Mat 类提供了一系列构造函数,可以方便的根据需要创建 Mat 对象。下面是一个使用构造函数创建对象的例子。
开发环境是基于 Qt 4.8.3 (64 bit)搭建的Qt Creator 2.6.2 IDE。后面的例子都是使用该开发环境。
首先创建一个空的Qt Console Application工程。要想使用OpenCV库,必须在工程文件中加入相应的头文件和库文件的路径,如下所示:

INCLUDEPATH += /usr/local/include/opencv\                /usr/local/include/opencv2LIBS += -L/usr/local/lib/ -lopencv_core -lopencv_highgui -lopencv_imgproc

下面是main.cpp的内容:

#include <QCoreApplication>#include <iostream>#include <opencv2/core/core.hpp>using namespace std;using namespace cv;int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    Mat M(3,2, CV_8UC3, Scalar(0,0,255));    cout << "M = " << endl << " " << M << endl;    return a.exec();}

执行结果如下:
这里写图片描述
第一行代码创建一个行数(高度)为 3,列数(宽度)为 2 的图像,图像元素是 8 位无符号整数类型,且有三个通道。图像的所有像素值被初始化为(0, 0,255)。由于 OpenCV 中默认的颜色顺序为 BGR,因此这是一个全红色的图像。
第二行代码是输出 Mat 类的实例 M 的所有像素值。 重定义了<<操作符,Mat使用这个操作符,可以方便地输出所有像素值,而不需要使用 for 循环逐个像素输出。
Mat类中属性有:
(1)data:
uchar类型的指针,指向Mat数据矩阵的首地址。可以理解为标示一个房屋的门牌号;
(2)dims:
Mat矩阵的维度,若Mat是一个二维矩阵,则dims=2,三维则dims=3,大多数情况下处理的都是二维矩阵,是一个平面上的矩阵。可以理解为房屋是一个一层的平房,三维或更多维的则是多层楼房;
(3)rows:
Mat矩阵的行数。可理解为房屋内房间行数;
(4)cols:
Mat矩阵的列数。可理解为房屋内房间列数;
(5)size():
首先size是一个结构体,定义了Mat矩阵内数据的分布形式,数值上有关系式:

    image.size().width==image.cols;          image.size().height==image.rows    

可以理解为房屋内房间的整体布局,这其中包括了房间分别在行列上分布的数量信息;
(6)channels():
Mat矩阵元素拥有的通道数。例如常见的RGB彩色图像,channels==3;而灰度图像只有一个灰度分量信息,channels==1。
可以理解为每个房间内放有多少床位,3通道的放了3张床,单通道的放了1张床;
(7)depth:
用来度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在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,也就是有符号和无符号数。可以理解为房间内每张床可以睡多少人,这个跟房间内有多少床并无关系;
(8)elemSize:
elem是element(元素)的缩写,表示矩阵中每一个元素的数据大小,如果Mat中的数据类型是CV_8UC1,那么 elemSize==1;如果是CV_8UC3或CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么elemSize==6;即elemSize是以8位(一个字节)为一个单位,乘以通道数和8位的整数倍;
可以理解为整个房间可以睡多少人,这个时候就得累计上房间内所有床位数(通道)和每张床的容纳量了;
(9)elemSize1:
elemSize加上一个“1”构成了elemSize1这个属性,1可以认为是元素内1个通道的意思,这样从命名上拆分后就很容易解释这个属性了:表示Mat矩阵中每一个元素单个通道的数据大小,以字节为一个单位,所以有:
eleSize1==elemSize/channels;
(10)step:
可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所有元素、所有通道、所有通道的elemSize1之后的值;
(11)step1():
以字节为基本单位,Mat矩阵中每一个像素的大小,累计了所有通道、所有通道的elemSize1之后的值,所以有:
step1==step/elemSize1;
(12)type:
Mat矩阵的类型,包含有矩阵中元素的类型以及通道数信息,type的命名格式为CV_(位数)+(数据类型)+(通道 数),所有取值如下:
这里写图片描述
常用的构造函数有:
(1)Mat::Mat()
无参数构造方法,输出空的数组M=[];
(2)Mat::Mat(int rows, int cols, int type)
创建行数为 rows,列数为 col,类型为 type 的图像;
(type is CV_8UC1, CV_64FC3, CV_32SC(12) 等)
前面的数字是保存数据的位数,后面的是通道数;UC表示无符号数,FC表示浮点数,64FC就是double类型的64位,SC表示有符号数。
(3)Mat::Mat(Size size, int type)
创建大小为 size,类型为 type 的图像;
(4)Mat::Mat(int rows, int cols, int type, const Scalar& s)
创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始化为值 s;
(5)Mat::Mat(Size size, int type, const Scalar& s)
创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s;
(6)Mat::Mat(const Mat& m)
将 m 赋值给新创建的对象, 此处不会对图像数据进行复制, 和新对象 m共用图像数据;
(7)Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step指定。
(8)Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。
(9)Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据;
(10)Mat::Mat(const Mat& m, const Rect& roi)
创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据。
如果你需要更多的通道数,需要用宏 CV_8UC(n),例如:
Mat M(3,2, CV_8UC(5));//创建行数为 3,列数为 2,通道数为 5 的图像

2.1.2 create()函数创建对象

除了在构造函数中可以创建图像,也可以使用 Mat 类的 create()函数创建图像。如果 create()函数指定的参数与图像之前的参数相同,则不进行实质的内存申请操作;如果参数不同,则减少原始数据内存的索引,并重新申请内存。使用方法如下面例程所示:

Mat M(2,2, CV_8UC3);//构造函数创建图像M.create(3,2, CV_8UC2);//释放内存重新创建图像

需要注意的时,使用 create()函数无法设置图像像素的初始值。
2.1.3 Matlab 风格的创建对象方法
OpenCV 2 中提供了 Matlab 风格的函数,如 zeros(),ones()和 eyes()。这种方
法使得代码非常简洁,
使用起来也非常方便。使用这些函数需要指定图像的大小
和类型,使用方法如下:

Mat Z = Mat::zeros(2,3, CV_8UC1);cout << "Z = " << endl << " " << Z << endl;Mat O = Mat::ones(2, 3, CV_32F);cout << "O = " << endl << " " << O << endl;Mat E = Mat::eye(2, 3, CV_64F);cout << "E = " << endl << " " << E << endl;

该代码中,有些 type 参数如 CV_32F 未注明通道数目,这种情况下它表示单
通道。上面代码的输出结果如图 3.4 所示。
这里写图片描述

0 0
原创粉丝点击