- rows:行数,或者高度;cols:列数,或者宽度
实现原理
cv::Mat 有两个必不可少的组成部分,一个头部,一个数据块。
class CV_EXPORTS Mat
{
public:
int flags;
//! the matrix dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
}
cv::Mat 头部属性主要有:cols、rows 或 channels,而数据块(uchar* data)包含了图像中所有像素的值。
cv::Mat 有一个很重要的特性,就是只有在明确要求时((copyTo、clone)),内存块才会被复制。实际上,大多数操作都只仅仅复制了 cv::Mat 的头部信息,因此多个对象会同时指向同一个数据块。这种内存管理模式可以提高应用程序的运行效率,避免内存泄露。
cv::Mat image1(240, 320, CV_8U, 100);
我们需要指定每个矩阵元素的类型,CV_8U 表示每个像素对应一个字节,U表示无符号,字母S则是有符号。对于彩色图像,可用 CV_8UC3(C表示channel)
我们可以随时用 create 方法分配或重新分配图像的数据块,如果图像已经分配,首先其原来的内容会被释放。出于对性能的考虑,如果新的大小和类型与原来的相同,就不会重新分配内存(可以想象,会直接覆盖)。
// 重新分配一个新图像
// (仅在大小或类型不同时)
image1.create(200, 200, CV_8U);
一旦没有了指向 cv::Mat 对象的引用,分配的内存就会被自动释放。这一点可避免 C++ 动态内存分配(new)中常常发生的内存泄露问题。这是 OpenCV2 中的一个关键机制,通过 cv::Mat 实现引用计数(reference count)和浅复制。当在两个图像之间赋值时,图像数据(也即像素)并不会被复制,此时两个图像指向同一个内存块。
一些细节
cv::Mat image = cv::imread("...");
(1)Mat 类型
if (image.type() == CV_8U) image.at<uchar>(i, j) = ...; if (image.type() == CV_8UC3) image.at<cv::Vec3b>(i, j)[0] = ...; image.at<cv::Vec3b>(i, j)[1] = ...; image.at<cv::Vec3b>(i, j)[2] = ...;