1. Mat容器
数字图像在计算机中都是以矩阵的形式存储的,矩阵数据表示这图像的亮度,颜色等。OpenCV提供了Mat(矩阵matrix)来存储矩阵。Mat是一个类,也是1个容器。
1.1 Mat简介
由于图像对数据准确性要求较高,否则易失真。因此opencv的数据类型定义得比较详细,如下图所示。
仅有数据类型还不够,还得给图像来个通道。例如灰度图像处理是单通道数据,彩色图像数据是3或4通道数据。OpenCV给定义的通道标识为C1,C2,C3,C4,分别表示1通道,2通道,3通道,4通道。将数据类型和通道数结合得到OpenCV对图像的数据类型的完整定义:
例如CV_8UC1:表示8位无符号1通道数据,用于表示8位灰度图。CV_8UC3表示8位无符号整数3通道,用于表示8位彩色图。
Mat a(640,480,CV_8UC3) // 创建640*480的3通道矩阵存放彩色图像
Mat a(3,3,CV_8UC1)
1.2 Mat类构造与赋值
1.2.1 Mat构造
有以下3种方式:
// 1. 默认构造函数
Mat::Mat()
// 2. 根据输入图像矩阵和类型构造,type: CV_8UC1等
Mat::Mat(int rows, int cols, int type)
// 3. 根据已有矩阵构造
Mat::Mat(const Mat & m)
1.2.2 Mat赋值
5种赋值方式:
// 1. 构造时赋值
Mat::Mat(2, 2, CV_8UC3, Scalar(0,0,255)) // 创建3通道,每个像素都是0,0,255
// 2. 枚举法赋值:创建2 * 2的图像矩阵
Mat a = (Mat_<int>(2, 2) << 1, 2, 3, 4);
// 3. for循环赋值
Mat c = Mat_<int>(2, 2);
for (int i = 0; i < c.rows; i++)
{
for (int j = 0; j < c.rols; j++)
{
c.at<int>(i, j) = i+j; // Mat是个容器,at是容器常用赋值类函数
}
}
// 4. 类方法构造
Mat d = Mat::ones(3, 3, CV_8UC1); // 1矩阵,3*3矩阵,元素都为1
Mat e = Mat::zeros(4, 2, CV_8UC3); // 0矩阵,4*2矩阵,元素都为0
// 5. 数组赋值
float a[8] = {1, 2, 3, 4, 5, 6, 7, 8}
Mat b = Mat(2, 2, CV_32FC2, a)
Mat c = Mat(2, 4, CV_32FC1, a)
/*
此时会自由拆分赋值:
b 为 2*2 , 所以b =
[1,2 3,4
5,6 7,8]
c 为 2*4, 所以c =
[1 2 3 4
5 6 7 8]
*/
1.3 Mat支持的运算
处理数据时,需要做加减乘除的运算,例如对图像滤波,增强等都需要对像素数据操作。opencv提供的Mat类在操作加减乘除时只需要像矩阵一样直接使用加减乘除符号即可,类似于MATLAB。
示例:
e = a + b;
f = c - d;
g = 2 * a;
h = d / 2.0
i = a - 1 // 表示a矩阵每个元素减去1
j = c * d // 表示矩阵相乘,就是数学里的 m*n X n*l = m*l
k = a.dot(b) // 内积或叫做点乘, 得到double类型的数, a,b 只能是行或列向量,个数相等。
m = a.mul(b) // 乘积,是矩阵对应位置相乘,需要长一样的 m*n
1.4 Mat元素读取
Mat类矩阵在计算机中存储时是将三维数据变成二维数据,先存储第1个元素每个通道的数据,再存储第2个元素每个通道的数据。每一行都是这样存储。Mat 类自身有以下属性可以直接调用:
- cols:列数
- rows:行数
- step:矩阵宽度,单位为字节;
- elemSize():单个元素占的字节数;
- total:元素个数
- channels: 通道数
1.4.1 at读取
示例:
Mat b(3, 4, CV_8UC3, Scalar(0, 0, 1)); // 初始化时赋值3*4 矩阵,每个像素为(0,0,1)
Vec3b vc3 = b.at<Vec3b>(0, 0); // 使用at索引(0,0)数据,按照uchar读取
int num1 = (int)vc3.val[0]; // 依次取第1,2, 3个值
int num2 = (int)vc3.val[1];
int num3 = (int)vc3.val[2];
1.4.2 ptr指针读取
Mat类的数据存放是紧挨着,内存地址连续,索引可以通过指针依次索引。
int main()
{
Mat b(2, 3, CV_8UC3, Scalar(10, 20, 30));
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] << " ";
}
cout << endl;
}
}
1.4.3 迭代器iterator读取
示例:
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat a = (cv::Mat_<uchar>(2, 2) << 1, 2, 3, 4);
MatIterator_<uchar> it_begin = a.begin<uchar>();
MatIterator_<uchar> it_end = a.end<uchar>();
for (int i = 0; it_begin != it_end; it_begin++)
{
cout << (int)(*it_begin) << " ";
// 每行结束换新行
if ((++i % a.cols) == 0)
{
cout << endl;
}
}
}