学习OpenCV(2)--- 图像基础容器Mat

学习OpenCV(2)--- 图像基础容器Mat

一.Mat类基本概念

1.1 Mat类的组成

Mat是一个类,由两个部分组成:

①矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)。

②一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。

矩阵头的尺寸是常数值,但矩阵本身尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。

OpenCv使用了引用计数机制。其思路是让每一个Mat对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。

1.2 Mat 类核心特性

自动内存管理:基于引用计数的浅拷贝 + 显式深拷贝,避免内存泄漏(超出作用域自动释放)。

多维支持:支持 2D(图像)、3D(视频帧序列 / 立体图像)甚至更高维数组。

类型灵活:支持不同数据类型(uchar/int/float/double)和通道数(1/3/4 通道,对应灰度 / RGB / 透明图)。

与 Numpy 互操作 :OpenCV-Python 中cv2.Matnumpy.ndarray无缝转换(底层共享内存)。

1.3 内存管理与引用计数

cv::Mat 使用引用计数机制(类似智能指针)。

当多个 Mat 共享同一数据时,只有当所有引用都被销毁后,数据才会被释放。

若需深拷贝,使用:

c++ 复制代码
cv::Mat clone = original.clone();
// 或
cv::Mat copy;
original.copyTo(copy);

二、Mat 类的基本使用

2.1 Mat类的构造

2.1.1 使用Mat构造函数

c++ 复制代码
// 方式1:Mat 构造函数
// 构造一个2*2 的8位无符号char 类型的3通道的Mat 对象
Mat mat1(2,2,CV_8UC3,Scalar(0,0,255));
cout << "mat1 = " << endl << " " << mat1 << endl << endl;

2.1.2 在C/C++中通过构造函数进行初始化

c++ 复制代码
// 方式2:在C/C++中通过构造函数进行初始化
// 构造一个超过2维的的矩阵,指定维数,然后传递一个指向数组的指针
int sz[3] = { 2,2,2 };
Mat mat2(3,sz,CV_8UC(2), Scalar::all(0));

2.1.3 为已存在的IplImage指针创建信息头

c++ 复制代码
// 方式3:为已存在的IplImage指针创建信息头
IplImage* pImage = cvLoadImage("1.jpg",1);
Mat mat3(pImage);

说明:我用的是OpenCV 4.11.0版本,其中IplImage已经被移除,cvLoadImage 也没有,这种方式推荐在低版本中使用,比如2.4.x版本中。

2.1.4 方式4:利用Create()函数

c++ 复制代码
// 方式4:利用Create()函数
Mat mat4;
mat4.create(4,4,CV_8UC(2));
cout << "mat4 = " << endl << " " << mat4 << endl << endl;

2.1.5 方式5:采用Matlab式的初始化方式:zeros、ones()、eye()

C++ 复制代码
// 方式5:采用Matlab式的初始化方式:zeros、ones()、eye()
Mat matEye = Mat::eye(4,4,CV_64F);
cout << "matEye = " << endl << " " << matEye << endl << endl;

Mat matOne = Mat::ones(2,2,CV_32F);
cout << "matOne = " << endl << " " << matOne << endl << endl;

Mat matZero = Mat::zeros(3,3, CV_8UC1);
cout << "matZero = " << endl << " " << matZero << endl << endl;

2.1.6 对小矩阵使用逗号分隔式初始化函数

C++ 复制代码
// 方式6:对小矩阵使用逗号分隔式初始化函数
Mat mat6 = (Mat_<double>(3, 3) << 0,-1,0,-1,5,-1,0,-1,0);
cout << "mat6 = " << endl << " " << mat6 << endl << endl;

2.1.7 为已存在的对象创建信息头

c++ 复制代码
// 方式7:为已存在的对象创建信息头
Mat mat7 = mat6.row(1).clone();
cout << "mat7 = " << endl << " " << mat7 << endl << endl;

2.2 类型说明(Type)

OpenCV 通过宏定义统一数据类型,格式:CV_{位数}{类型}{通道数}

类型宏 说明 对应 C++ 类型 适用场景
CV_8UC1 8 位无符号单通道 unsigned char 灰度图像(0-255)
CV_8UC3 8 位无符号 3 通道 unsigned char[3] RGB 彩色图像
CV_32SC1 32 位有符号单通道 int 整数运算(如差值计算)
CV_32FC1 32 位浮点单通道 float 浮点运算(如滤波)
CV_64FC3 64 位浮点 3 通道 double[3] 高精度浮点计算

注:U=unsigned(无符号),S=signed(有符号),F=float(浮点);通道数可省略(默认 1)

可通过 mat.type() 获取类型,使用 CV_MAT_DEPTH(type)CV_MAT_CN(type) 分别提取深度和通道数。

C++ 复制代码
// 单通道
cv::Mat gray(100, 100, CV_8UC1);    // 灰度图像
cv::Mat floatMat(100, 100, CV_32FC1); // 浮点矩阵

// 多通道
cv::Mat color(100, 100, CV_8UC3);   // BGR彩色图像
cv::Mat rgba(100, 100, CV_8UC4);    // RGBA图像

2.3 拷贝与赋值

1.浅拷贝(仅拷贝头部,共享数据区)

c++ 复制代码
cv::Mat img1 = cv::imread("test.jpg");
cv::Mat img2 = img1;       // 浅拷贝
cv::Mat img3(img1);        // 浅拷贝
img2.at<cv::Vec3b>(0,0) = cv::Vec3b(255,0,0); // img1、img3的(0,0)像素也会变

2.深拷贝(独立数据区)

c++ 复制代码
cv::Mat img1 = cv::imread("test.jpg");
cv::Mat img2 = img1.clone(); // 深拷贝(推荐)
cv::Mat img3;
img1.copyTo(img3);          // 深拷贝(可指定掩码)
img2.at<cv::Vec3b>(0,0) = cv::Vec3b(255,0,0); // img1不受影响

要点:

  • 赋值/拷贝构造只复制头部,不复制像素;
  • 引用计数为 0 时自动 free()
  • 一旦某个实例调用 .clone().copyTo().create()reshape(),就会断开共享,自己管自己的内存。

三. 访问像素数据

3.1 at<T>(row, col)(直观但效率低,适合少量访问)

c++ 复制代码
Mat matGray(480,640, CV_8UC3,Scalar(0,0,0));
// 访问单通道(CV_8UC1)
uchar gray_pixel = matGray.at<uchar>(100,200);
// 访问三通道
Vec3b& rgb_pixel = matGray.at<Vec3b>(100, 200);
rgb_pixel[0] = 255; // B通道 (OpenCV默认BGR!)
rgb_pixel[1] = 0; // G通道
rgb_pixel[2] = 0; // R通道

3.2 指针访问(高效,适合遍历所有像素)

C++ 复制代码
// 使用指针访问
Mat mat(480, 640, CV_8UC3);
for (int i=0;i< mat.rows;i++)
{
	// 获取第i行的指针
	uchar* row_ptr = mat.ptr<uchar>(i);
	for (int j=0;i<mat.cols;j++)
	{
		row_ptr[j * 3] = 255;
		row_ptr[j * 3 + 1] = 0;
		row_ptr[j * 3 + 2] = 0;
	}
}

3.3 迭代器访问(安全,不关心尺寸)

c++ 复制代码
// 迭代器访问
Mat mat2(480, 640, CV_8UC3);
// 3通道迭代器
MatIterator_ <Vec3b> it = mat2.begin<Vec3b>();
MatIterator_ <Vec3b> it_end = mat2.end<Vec3b>();

for (; it != it_end;it++)
{
	(*it)[0] = 0;   
	(*it)[1] = 255;
	(*it)[2] = 0;
}

四.常用操作

4.0 读取图像

C++ 复制代码
Mat mat = cv::imread("test.jpg");

4.1 调整大小

c++ 复制代码
// 1.调整大小
Mat mat = cv::imread("test.jpg");
Mat mat_resize;
// 缩放(宽800,高600)
cv::resize(mat, mat_resize,cv::Size(800,600));
// 按比例缩放
cv::resize(mat, mat_resize, cv::Size(),0.5,0.5);

4.2 颜色空间转换

c++ 复制代码
// 颜色空间转换
Mat gray, hsv;
cv::cvtColor(mat, gray,cv::COLOR_BGR2GRAY);
cv::cvtColor(mat, hsv, cv::COLOR_BGR2HSV);

4.3 通道拆分与合并

c++ 复制代码
// 通道拆分与合并
vector<Mat> channels;
split(mat, channels); // 拆分:channels[0]=B, channels[1]=G, channels[2]=R

Mat bgr_r;
channels[2].copyTo(bgr_r);

Mat mat_Merge;
cv::merge(channels,mat_Merge);

4.4 ROI(Range Of Interest) 提取

c++ 复制代码
// ROI 提取
cv::Rect rect(200,100,200,200); // 提取ROI:行100-300,列200-400(Rect(x, y, width, height))
Mat img_roi = mat(rect);  // 浅拷贝!修改img_roi会影响原图

4.5 保存图像

c++ 复制代码
// 保存图像
cv::imwrite("output.jpg", mat);

4.6 阈值处理

c++ 复制代码
// 阈值处理
cv::Mat binary;
cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY);

4.7 滤波

c++ 复制代码
// 滤波
cv::Mat blurred;
cv::GaussianBlur(src, blurred, cv::Size(5, 5), 0);

4.8 形态学操作

C++ 复制代码
// 形态学操作
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::Mat eroded, dilated;
cv::erode(src, eroded, kernel);
cv::dilate(src, dilated, kernel);

五. 注意事项

  1. 内存管理:Mat使用引用计数,注意浅拷贝和深拷贝的区别
  2. 数据类型:确保操作时使用正确的数据类型
  3. 边界检查:访问像素时要确保不越界
  4. 连续存储:对于性能敏感的代码,注意矩阵的连续性
  5. 多通道访问:注意不同通道的存储顺序(通常是BGR)

参考文章

1.第 1.2 篇:Mat 结构完全讲透

2.cv::Mat Class Reference

相关推荐
Wild_Pointer.2 小时前
深入浅出OpenCV:查阅OpenCV的实现源码
人工智能·opencv·计算机视觉
dragoooon343 小时前
【OpenCV 图像处理 Python版】图像处理的基本操作
人工智能·opencv·计算机视觉
tangjunjun-owen3 小时前
OpenCV在Visual Studio中的完整配置教程
人工智能·opencv·visual studio
询问QQ:1808095117 小时前
基于出行链的电动汽车空间负荷预测,MATLAB,有注释,方便初学者理解上手,此程序用来计算节点...
opencv
南极星10051 天前
OPENCV(python)——初学之路(十三)分水岭算法的图像切割
人工智能·opencv·计算机视觉
0319zz2 天前
SFMFold
opencv
劈星斩月2 天前
OpenCV 学习2 -调整图像大小
opencv·调整图像大小
棒棒的皮皮2 天前
【OpenCV】Python图像处理之数字水印
图像处理·python·opencv·计算机视觉
劈星斩月2 天前
OpenCV 学习3 - 裁剪图像
opencv·裁剪图像