Mat 的介绍
cv::Mat
是 OpenCv 中非常重要的一个类,它提供了一种自动化的方式来管理图片的加载、操作和销毁等。加载图片时,它会自动申请内存,根据实际情况自动释放该内存。
cv::Mat
有两个必不可少的组成部分:信息头和数据块:
- 信息头:包含了矩阵的属性信息:矩阵尺寸、通道数量、数据类型等;
- 数据块:包含了图像中所有像素的值。信息头有一个指向数据块的指针;
cv::Mat
有一个很重要的属性:即只有在明确要求的情况下,内存块才会被复制。拷贝构造函数只是复制了cv::Mat
的信息头,因此会多个对象指向同一个数据块,这种内存管理模式可以提高程序的运行效率,避免内存泄漏 。以下操作只复制了信息头:
Mat A,C;// 仅仅创建信息头
A = imread("bird.jpg", CV_LOAD_IMAGE_COLOR); // 为数据块开辟内存
C = A; // 赋值运算
Mat D(A, Rect(10, 10, 100, 100)); // 使用矩形界定 ROI 区域
Mat E = A(Range::all(), Range(1, 3)); // 使用行和列来界定 ROI 区域
以上代码中所有的 Mat 对象拥有独自的 信息头,但都指向相同的 数据块。不同的对象只是以不同的方式访问同一块 数据块。
可能会存在一个疑问?如果矩阵属于多个 Mat 对象,那么当不再需要它时,谁来负责清理呢?–最后一个使用它的对象负责清理。OpenCv 使用了引用计数机制。当我们复制一个 Mat 对象的信息头时,都会增加该矩阵的引用次数;反之,当一个头被释放时,这个计数就会减一;当这一数据块计数数值为零时,数据块内存会被自动清除。
但是,有的时候我们也想复制 数据块,该怎么做呢?可以使用 clone()
和 copyTo()
函数,如下所示:
Mat F = A.clone();
Mat G;
A.copyTo(G);
此时改变 F 和 G 都不会影响 A 的 数据块。
创建 Mat 对象
Mat 不但是一个非常有用的容器类,同时也是一个通用的矩阵类,我们可以用它来创建和操作多维矩阵
使用 Mat() 构造函数
使用 Mat 构造函数,如下所示:
cv::Mat M(2, 2, CV_8UC3,Scalar(0, 0, 255))
std::cout << "M = " << endl << " " << M << endl;
结构如图所示:
上述构造函数的原型如下:
cv::Mat::Mat ( int rows,
int cols,
int type,
const Scalar & s
)
具体参数含义如下:
- rows:二维数组的行数;
- cols:二维数组的列数;
- type:二维数组的数据类型,其格式为:CV_[位数][带符号与否][类型前缀]C[通道数],常见的形式为:
- CV_8UC3:表示 8 位无符号类型,每个元素由三个通道组成;
- CV_8UC1:表示 8 位无符号类型,每个元素由三个通道组成;
- CV_32FC2:表示 32 位单精度浮点类型,每个元素由两个通道组成;
- CV_64FC4:表示 64 位双精度浮点类型,每个元素由四个通道组成;
- s:初始化矩阵每一个元素的值。Scalar 是 Short 类型的向量,用来表示颜色;
创建多维度矩阵
样例如下:
/* create a 10 x 10 8-bit array*/
/*
int sz[2] = {10, 10};
cv::Mat M(2, sz, CV_8U, cv::Scalar::all(1.0));
std::cout << "M = " << std::endl << " " << M << std::endl << std::endl;
结果如下图所示:
利用 Create 函数进行初始化
使用样例如下:
/* 2. 创建多维度矩阵*/
/* create a 10 x 10 8-bit array*/
int sz[2] = {10, 10};
cv::Mat M(2, sz, CV_8U, cv::Scalar::all(1.0));
std::cout << "M = " << std::endl << " " << M << std::endl << std::endl;
/* 3. 利用Create()函数进行初始化*/
// and now turn M to a 3x4 5-channel 8-bit matrix.
// The old content will be deallocated
M.create(3, 4, CV_8UC(5));
std::cout << "M = " << std::endl << " " << M << std::endl << std::endl;
结果如下图所示:
采用 MatIab 式的初始化方式
采用Matlab形式的初始化方式:zeros(),ones(),eyes()。使用以下方式指定尺寸和数据类型:
cv::Mat E = cv::Mat::eye(4, 4, CV_64F);
std::cout << "E = " << std::endl << " " << E << std::endl << std::endl;
cv::Mat O = cv::Mat::ones(2, 2, CV_32F);
std::cout << "O = " << std::endl << " " << O << std::endl << std::endl;
cv::Mat Z = cv::Mat::zeros(3, 3, CV_8UC1);
std::cout << "Z = " << std::endl << " " << Z << std::endl << std::endl;
结果如下所示:
小矩阵初始化
当要创建小矩阵时,可以采用如下形式:
/* 5. 小矩阵初始化*/
cv::Mat C = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
std::cout << "C = " << std::endl << " " << C << std::endl << std::endl;
double m[3][3] = { {1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0} };
cv::Mat M = cv::Mat(3, 3, CV_64F, m);
std::cout << "M = " << std::endl << " " << M << std::endl << std::endl;
结果如下:
用已存在的对象创建一个新的信息头
主要利用成员函数 clone()
和 copyTo()
为一个已存在的 Mat 对象创建一个新的信息头,示例代码如下:
/* 6. 用已存在的对象创建一个新的信息头*/
cv::Mat C = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cv::Mat RowClone1 = C.row(1).clone();
std::cout << "RowClone1 = " << std::endl << " " << RowClone1 << std::endl << std::endl;
cv::Mat RowClone2;
C.row(1).copyTo(RowClone2);
std::cout << "RowClone2 = " << std::endl << " " << RowClone2 << std::endl << std::endl;
上述代码的运行结果如下所示: