一 图像存储容器

        学习图像处理首先要学会如何操作矩阵信息,在OpenCV中提供了一个Mat类用于存储矩阵数据。

       Mat类用来保存矩阵类型的数据信息,包括向量,矩阵,灰度或彩色图像等数据。Mat类分为矩阵头和指向存储数据的矩阵指针两部分。其中矩阵头中包含矩阵的尺寸、存储方法、地址和引用次数。矩阵头的大小是一个常数,不会随着矩阵尺寸的大小二改变。在绝大多数情况下,矩阵头大小远小于矩阵中数据量的大小,因此图像复制和传递过程中的主要开销是存放矩阵数据。

1.创建Mat类

cv::Mat a;          //创建一个名为a的矩阵头
a = cv::imread("test.jpg")   //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b = a   //复制矩阵头

        上述代码,首先创建了名为a的矩阵头,之后读入一张图像并将中的矩阵指针指向该图的像素数据,最后将a矩阵头中的内容复制到b矩阵头中。虽然a、b有各自的矩阵头,但是矩阵指针所指向的是同一矩阵数据,所以通过任意矩阵头修改矩阵中的数据时,另一个矩阵头指向的数据也会发生改变。但是当删除变量a时,b变量并不会指向一个空数据,只用当两个变量同时删除时,才会释放矩阵数据。因为矩阵头中引用次数标记了引用某个矩阵的次数,只有当矩阵的引用次数为0的时候才会释放矩阵数据。

1.1声明一个指定类型的Mat类

cv::Mat A = Mat_<double>(3,3);   //创建一个3*3的矩阵用于存放double数据类型

        Mat可以存储的数据类型包括double,float,uchar,unsigned char,以及自定义模板。

1.2通过OpenCV数据类型创建Mat类

OpenCV中的数据类型
数据类型具体类型取值范围
CV_8U8位无符号整数0~255
CV_8S8位符号整数-128~127
CV_16U16位无符号整数0~65535
CV_16S16位符号整数-32768~32767
CV_32S32位符号整数-2147483~2147483647
CV_32F32位浮点小数

CV_64F

64位浮点小数
cv::Mat a(640,480,CV_8UC3)    //创建一个640*480 的三通道矩阵用于存放彩色图像
cv::Mat a(3,3,CV_8UC1)    //创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a(3,3,CV_8U)   //创建单通道矩阵,c1标识可以省略

        注意:虽然在64位编译器里,uchar和CV_8U都表示8位无符号整数,但是两者有着严格的定义,CV_8U只能用于Mat类内部构建方法,如果用Mat_<CV_8U>(3,3)和Mat a(3,3,CV_8U),就会提示创建错误。

2.Mat类构造与赋值

2.1 Mat类的构造

(1)利用默认构造函数

cv::Mat::Mat();

        这种构造方式不需要输入任何的参数,在后续给变量赋值的时候会自动判断矩阵的类型大小,实现灵活的存储,常用于存储读取的图像数据和某个函数的运算输出结果。

(2)根据输入矩阵尺寸和类型构造

cv::Mat::Mat( int rows,
            int cols,
            int type,
            )
//rows:构造矩阵的行数
//cols:构造矩阵的列数
//type:矩阵中存储的数据类型

(3)用Size()结构构造Mat类

cv::Mat::Mat(Size size(),
            int type
            )
//size: 二维数组变量尺寸,通过Size(cols,rows)来进行赋值,注意列在前,行在后
//type:略

(4)利用已有矩阵构造Mat实例

cv::Mat()::Mat(const Mat &m)
//m:以构建完成的Mat类矩阵变量

        希望复制两个一模一样的Mat类而批次之间不会影响,那么可以使用m=a.clone()

(5)创建已有Mat类的子类

cv::Mat::Mat(const Mat &m,
            const Range &rowRange,
            const Range &colRange = Range::all()
            )
//m: 已经构建完成的Mat类矩阵数据
//rowRange:在以有矩阵中需要截取的行数范围,是一个Range变量,例如Range(2,5)表示从第二行,    //到第五行
//colRange:截取列的范围

//这种方式主要用于在原图中截图使用。不过需要注意的是,通过这种方式构造的Mat类与已有Mat类享有共同的数据。

2.Mat类的赋值

(1)在构造时赋值

cv::Mat::Mat(int rows,
            int cols,
            int type,
            const Scalar &s
            )
//rows:略
//cols:略
//type:略
//s: 给矩阵中没个像素赋值的参数变量

//实例: 
cv::Mat::Mat(2, 2, CV_8UC3, CV::Scalar(0,0,255))

(2)枚举法赋值

cv::Mat::Mat(cv::Mat_<int>(3,3)<<1, 2, 3, 4, 5, 6, 7, 8, 9);

(3)利用循环赋值

cv::Mat c = cv::Mat_(3, 3); //定义一个3*3的矩阵
for (int i = 0; i < c.rows; i++)
{
    for (int j = 0; j < c.cols; j++)
    {
        c.at<int>(i, j) = i + j;
    {
}

(4)利用类方法赋值

cv::Mat a = cv::Mat::eye(3, 3, CV_8UC1);
cv::Mat b = (cv::Mat_<int>(1, 3) <<1, 2, 3);
cv::Mat c = cv::Mat::diag(b);
cv::Mat d = cv::Mat::ones(3, 3, CV_8UC1);
cv::Mat e = cv::Mat::zeros(4, 2, CV_8UC3);


//eye:创建一个单位矩阵,如果行和列不相等,在(1,1),(2,2),等主对角线出为1
//diag:创建对角矩阵,其参数必须是Mat类型的一维变量,用来存放对角元素的数值
//ones:创建一个全为1的矩阵
//zeros:创建一个全为0的矩阵

(5)利用数组进行赋值

float a[8] = {5, 6, 7, 8, 1, 2, 3, 4, };
cv::Mat b = cv::Mat(2, 2, CV_32FC2, a);
CV::Mat c = cv::Mat(2, 4, CV_32FC1, a);

//当矩阵中的元素数目大于数组中的数据时,将用-1.0737418e+08填充给矩阵;当矩阵中元素的数目小于数组中的数据时,将矩阵赋值完成后,数组中剩余数据将不再赋值。

3.Mat类支持的运算

        Mat支持加减乘除的运算,在对图像进行卷积运算时,需要两个矩阵进行乘法运算,OpenCV提供了两个Mat类矩阵的乘法运算,而且定义了两个矩阵的内积和对应位的乘法运算。

cv::Mat() j,m;
double k;
j = c*d;
k = a.dot(b);
m = a.mul(b);

 4.Mat类元素的读取

Mat类矩阵常用的属性
属性作用
cols矩阵的列数
rows矩阵的行数
step以字节为单位的矩阵有效宽度
elemSize()每个元素的字节数
total()矩阵中元素的个数
channels()矩阵的通道数

(1)通过at方法读取Mat类矩阵中的元素


//读取单通道矩阵元素
cv::Mat a = (cv::Mat_<uchar>(3, 3) <<1, 2, 3, 4, 5, 6, 7, 8, 9);
int value = (int)a.at<uchar>(0, 0);

//读取多通道矩阵元素
cv::Mat b(3, 4, CV_8UC3, CV::Scalar(0, 0, 1));
cv::Vec3b vc3 = b.at<cv::Vec3b>(0, 0);
int first = (int)vc3.val[0];
int second = (int)vc3.val[1];
int third = (int)vc3.val[2];

//在使用多通道变量类型是,同样需要注意at方法中数据变量类型与矩阵的变量类型相对应,
//并且cv::Vec3b类型在输入每个通道数据是需要将其变量类型强制转化成int类型。

        针对三通道矩阵,定义了cv::Vec3b,cv::Vec3s,cv::Vec3w,cv::Vec3d,cv::Vec3f,cv::Vec3i。b是uchar类型的缩写,s是short类型的缩写,w是ushort类型的缩写,d是double类型的缩写,f是float类型的缩写, i是int类型的缩写。

(2)通过指针ptr读取Mat类矩阵中的元素

        Mat类矩阵中每一行中的每个元素都是挨着存放的,所以给出了通道指针ptr读取Mat类矩阵元素。

cv::Mat b(3, 4, CV_8UC3, cv::Scalar(0, 0, 1));
for (int i = 0; i < b.rows; i++)
{
    uchar* ptr = b.ptr<uchar>(i);
    for(int j = 0; j < b.cols*b.channels(); j++)
    {
        cout << (int)ptr[j] <<endl;
    }
}

(3)通过迭代器访问Mat类矩阵中的元素

cv::MatIterator_<uchar> it = a.begin<uchar>();
cv::MatIterator_<uchar> it_end = a.end<uchar>();
for(int i = 0; it ! = it_end; it++)
{
    cout << (int)(*it) <<"";
    if((++i%a.cols) == 0)
    {
        cout << endl;
    }

} 

        Mat类的迭代器变量类型是cv::MatIterator_<>,在定义时同样需要在尖括号中声明数据的变量类型。Mat类迭代器的起始是Mat.begin<>(),借宿是Mat.end<>(),与其他迭代器用法相同,通过“++”运算实现指针位置向下迭代,数据的读取方式是先读取第一个元素的每一个通道,之后再读取第二个元素的每一个通道,直到最后一个元素的最后一个通道。

(4)通过矩阵元素的地址定位方式访问元素

(int)(*(b.data + b.step[0] * row + b.step[1] * col + channel));

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐