四、cv::Mat的介绍和使用
文章目录
- 一、cv::Mat类N维稠密数组
- 1.1 什么是稠密数组
- 二、使用cv::Mat类创建数组
- 2.1 创建空矩阵
- 2.2 创建指定大小、类型的矩阵
- 2.3 创建并初始化为常量值
- 2.4 使用 cv::Mat::zeros / ones / eye
- 三、cv::Mat的常用构造函数
- 3.1 默认构造函数(创建空矩阵)
- 3.2 构造指定大小、类型的矩阵
- 3.3 同时初始化数据值
- 3.4 创建多维矩阵
- 3.5 使用已有数据(不复制)
- 3.7 拷贝构造函数(浅拷贝)
- 3.8 子矩阵(ROI)构造
- 3.9 多维子矩阵(高级用法)
- 3.10 从数组构造(C++11 支持)
- 3.11 从 cv::MatExpr 表达式构造(用于矩阵运算)
- 三、cv::Mat的常用静态函数
- 四、cv::Mat 如何获取独立元素
- 4.1 使用 at<>() 获取矩阵中的单个元素
- 4.2 二维矩阵中通过“块”访问元素(提取子区域)
- 4.3 指定 Rect 块区域访问(更常用于图像 ROI)
- 4.4 块访问在多维矩阵中(>=3维)
一、cv::Mat类N维稠密数组
1.1 什么是稠密数组
稠密数组(Dense Array) 是指大多数元素都有明确值并被实际存储的数组。它和稀疏数组(Sparse Array) 相对,后者则是大多数元素为 0 或无效值,不被存储。
通俗理解:
举例说明:
稠密数组(Dense)示例:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
- 所有元素都有值,适合使用 cv::Mat 表示;
- 存储结构是连续内存。
稀疏数组(Sparse)示例:
[0, 0, 5]
[0, 0, 0]
[0, 0, 8]
- 大部分是 0;
- 使用稀疏结构(如 cv::SparseMat)节省内存,只记录非零元素及其索引。
为什么叫 “稠密”?
“稠密”即“密集”,指的是:
- 数据元素紧密存储;
- 适合用连续内存块保存(效率高);
- 可用于图像、矩阵运算、卷积等对内存访问要求高的场景。
OpenCV 中的稠密数组:cv::Mat
- 使用连续内存保存像素或矩阵数据;
- 支持 n 维、多个通道;
- 更适合图像、视频帧等数据处理。
二、使用cv::Mat类创建数组
在 OpenCV 中,使用 cv::Mat 类可以轻松创建各种类型的稠密数组,包括单通道、三通道图像,多维矩阵,初始化为常量等。一个有效的数据类型需要同时指明数据的类型和通道,所有这些数据类型都在库的头文件中声明,包括CV_{8U,16S,16U,32S,32F,64F}的多种组合。比如说,CVC_32FC3表示一个三通道的32位浮点数据。
创建二维数组(图像)
2.1 创建空矩阵
cv::Mat mat; // 空矩阵,尚未分配内存
2.2 创建指定大小、类型的矩阵
cv::Mat mat(rows, cols, type);
示例:
cv::Mat mat1(3, 3, CV_8UC1); // 3x3,单通道,8位无符号整数
cv::Mat mat2(3, 3, CV_8UC3); // 3x3,3通道彩色图像(BGR)
cv::Mat mat3(3, 3, CV_32FC1); // 3x3,单通道浮点矩阵
2.3 创建并初始化为常量值
cv::Mat mat(rows, cols, type, cv::Scalar(val1, val2, val3, ...));
示例:
cv::Mat gray(3, 3, CV_8UC1, cv::Scalar(100)); // 灰度图,全为100
cv::Mat color(2, 2, CV_8UC3, cv::Scalar(0, 255, 0)); // 彩色图,全为绿色(BGR)
2.4 使用 cv::Mat::zeros / ones / eye
cv::Mat m1 = cv::Mat::zeros(3, 3, CV_8UC1); // 全为0
cv::Mat m2 = cv::Mat::ones(3, 3, CV_32F); // 全为1
cv::Mat m3 = cv::Mat::eye(3, 3, CV_64F); // 单位矩阵
创建多维数组(n-D)
int sizes[] = {2, 3, 4}; // 三维数组:2x3x4
cv::Mat ndMat(3, sizes, CV_32F, cv::Scalar::all(0)); // 所有元素初始化为0
从已有数据创建(不拷贝)
float data[] = {1, 2, 3, 4, 5, 6};
cv::Mat mat(2, 3, CV_32F, data); // 使用已有数组,2x3
示例:3x3 单通道矩阵赋值
cv::Mat mat(3, 3, CV_8UC1);
mat.at<uchar>(0, 0) = 255;
mat.at<uchar>(1, 1) = 128;
mat.at<uchar>(2, 2) = 64;
常用类型宏:
三、cv::Mat的常用构造函数
3.1 默认构造函数(创建空矩阵)
cv::Mat::Mat();
示例:
cv::Mat mat; // 不分配内存,仅创建一个空对象
3.2 构造指定大小、类型的矩阵
cv::Mat::Mat(int rows, int cols, int type);
cv::Mat::Mat(Size size, int type);
示例:
cv::Mat mat1(3, 4, CV_8UC1); // 3行4列,8位单通道
cv::Mat mat2(cv::Size(640, 480), CV_8UC3); // 640x480 彩色图像
3.3 同时初始化数据值
cv::Mat::Mat(int rows, int cols, int type, const Scalar& s);
cv::Mat::Mat(Size size, int type, const Scalar& s);
示例:
cv::Mat gray(3, 3, CV_8UC1, cv::Scalar(128)); // 所有像素初始化为128
cv::Mat color(2, 2, CV_8UC3, cv::Scalar(0, 255, 0)); // 全绿图像(BGR)
3.4 创建多维矩阵
cv::Mat::Mat(int ndims, const int* sizes, int type);
cv::Mat::Mat(int ndims, const int* sizes, int type, const Scalar& s);
示例:
int sizes[] = {3, 4, 5}; // 三维
cv::Mat ndMat(3, sizes, CV_32F, cv::Scalar(1.0f));
3.5 使用已有数据(不复制)
cv::Mat::Mat(int rows, int cols, int type, void* data, size_t step = AUTO_STEP);
cv::Mat::Mat(Size size, int type, void* data, size_t step = AUTO_STEP);
示例:
uchar buffer[100];
cv::Mat mat(10, 10, CV_8UC1, buffer); // 使用外部数据,不复制
3.7 拷贝构造函数(浅拷贝)
cv::Mat::Mat(const Mat& m);
示例:
cv::Mat a(3, 3, CV_8UC1, cv::Scalar(5));
cv::Mat b = a; // 浅拷贝,共享数据
3.8 子矩阵(ROI)构造
cv::Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange);
cv::Mat::Mat(const Mat& m, const Rect& roi);
示例:
cv::Mat roi = mat(cv::Rect(10, 10, 100, 100)); // 截取图像区域
3.9 多维子矩阵(高级用法)
cv::Mat::Mat(const Mat& m, const Range* ranges);
3.10 从数组构造(C++11 支持)
cv::Mat::Mat(std::initializer_list<_Tp>);
示例:
cv::Mat mat = (cv::Mat_<uchar>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
3.11 从 cv::MatExpr 表达式构造(用于矩阵运算)
cv::Mat::Mat(const MatExpr& expr);
示例:
cv::Mat a = cv::Mat::ones(3, 3, CV_32F);
cv::Mat b = cv::Mat(a + 5); // a + 5 是一个 MatExpr,强制转换为 Mat
cv::Mat 构造函数用法汇总示例:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <iomanip>void printMat(const cv::Mat& mat, const std::string& title = "") {if (!title.empty()) {std::cout << "=== " << title << " ===\n";}if (mat.empty()) {std::cout << "Matrix is empty.\n";return;}std::cout << "Size: " << mat.rows << "x" << mat.cols << ", Channels: " << mat.channels() << ", Type: " << mat.type() << "\n";if (mat.channels() == 1) {for (int i = 0; i < mat.rows; ++i) {const uchar* rowPtr = mat.ptr<uchar>(i);for (int j = 0; j < mat.cols; ++j) {std::cout << std::setw(4) << static_cast<int>(rowPtr[j]) << " ";}std::cout << "\n";}} else if (mat.channels() == 3) {for (int i = 0; i < mat.rows; ++i) {const cv::Vec3b* rowPtr = mat.ptr<cv::Vec3b>(i);for (int j = 0; j < mat.cols; ++j) {const auto& pix = rowPtr[j];std::cout << "[" << (int)pix[0] << "," << (int)pix[1] << "," << (int)pix[2] << "] ";}std::cout << "\n";}}std::cout << std::endl;
}int main() {// 1. 默认构造函数cv::Mat mat1;printMat(mat1, "Default constructor");// 2. 指定大小和类型cv::Mat mat2(2, 3, CV_8UC1);mat2.setTo(10);printMat(mat2, "Constructor with rows, cols, type");// 3. 初始化为常量值cv::Mat mat3(2, 2, CV_8UC3, cv::Scalar(0, 255, 0)); // Green BGRprintMat(mat3, "Constructor with init value (color)");// 4. 多维数组int sizes[] = {2, 2, 2}; // 3D arraycv::Mat mat4(3, sizes, CV_32F, cv::Scalar(1.0f));std::cout << "=== 3D Matrix (2x2x2) ===\n";std::cout << "Dims: " << mat4.dims << ", Size: (" << mat4.size[0] << "," << mat4.size[1] << "," << mat4.size[2] << ")\n\n";// 5. 从已有数据构造(不复制)uchar buffer[6] = {1, 2, 3, 4, 5, 6};cv::Mat mat5(2, 3, CV_8UC1, buffer);printMat(mat5, "Constructor with external data");// 6. 拷贝构造函数(浅拷贝)cv::Mat mat6 = mat2;mat6.at<uchar>(0, 0) = 99;printMat(mat2, "Original after shallow copy modified");printMat(mat6, "Shallow copy");// 7. ROI 构造(子矩阵)cv::Mat big = (cv::Mat_<uchar>(4, 4) << 1, 2, 3, 4,5, 6, 7, 8,9,10,11,12,13,14,15,16);cv::Mat roi = big(cv::Rect(1, 1, 2, 2)); // 中间2x2printMat(big, "Original Matrix");printMat(roi, "ROI from (1,1) size 2x2");// 8. 初始化列表构造cv::Mat mat8 = (cv::Mat_<uchar>(2, 2) << 10, 20, 30, 40);printMat(mat8, "Constructor with initializer list");// 9. MatExpr 构造(矩阵运算)cv::Mat a = cv::Mat::ones(2, 2, CV_32F);cv::Mat b = cv::Mat(a + 5); // MatExpr -> Matstd::cout << "=== Matrix expression result (a + 5) ===\n" << b << "\n";return 0;
}
三、cv::Mat的常用静态函数
3.1 Mat cv::Mat::zeros(int rows, int cols, int type)
功能:创建一个所有像素值为 0 的新矩阵。
参数:
- rows:行数(高度)
- cols:列数(宽度)
- type:图像类型(如 CV_8UC3)
示例:
cv::Mat m = cv::Mat::zeros(3, 4, CV_8UC1); // 创建 3x4 单通道 uchar 矩阵,全为0
3.2 Mat cv::Mat::zeros(Size size, int type)
功能:创建指定尺寸的零矩阵(cv::Size(width, height))
示例:
cv::Mat m = cv::Mat::zeros(cv::Size(640, 480), CV_8UC3);
3.3 Mat cv::Mat::ones(int rows, int cols, int type)
功能:创建所有像素值为 1 的矩阵。
示例:
cv::Mat m = cv::Mat::ones(2, 2, CV_32F); // 所有元素为 1.0f
3.4 Mat cv::Mat::ones(Size size, int type)
cv::Mat m = cv::Mat::ones(cv::Size(100, 50), CV_64F);
3.5 Mat cv::Mat::eye(int rows, int cols, int type)
功能:创建单位矩阵(主对角线为1,其余为0)
示例:
cv::Mat m = cv::Mat::eye(4, 4, CV_32F);
3.6 Mat cv::Mat::eye(Size size, int type)
cv::Mat m = cv::Mat::eye(cv::Size(3, 3), CV_8UC1);
3.7 Mat cv::Mat::diag(const Mat& d)
功能:创建一个对角矩阵,其主对角线元素来自参数 d。
参数:d:一维向量(Mat),作为对角线元素。
示例:
cv::Mat v = (cv::Mat_<float>(3, 1) << 1, 2, 3);
cv::Mat diagMat = cv::Mat::diag(v); // 生成一个3x3的对角矩阵
3.8 Mat cv::Mat::diag(int d) const
功能:从现有矩阵提取对角线,构成一个列向量。
示例:
cv::Mat A = (cv::Mat_<int>(3, 3) << 1,2,3,4,5,6,7,8,9);
cv::Mat diagVec = A.diag(0); // 提取主对角线:1,5,9
四、cv::Mat 如何获取独立元素
4.1 使用 at<>() 获取矩阵中的单个元素
示例:访问单位矩阵中的某个元素
cv::Mat identity = cv::Mat::eye(3, 3, CV_32F);// 访问第1行第1列的元素(索引从0开始)
float val = identity.at<float>(1, 1); // 等于 1.0// 访问第0行第2列的元素
float val2 = identity.at<float>(0, 2); // 等于 0.0
注意:
- at(row, col) 中的 T 必须匹配 Mat 的数据类型(如 CV_32F 对应 float,CV_8U 对应 uchar)。
- 如果类型不匹配,可能会崩溃或读出错。
遍历所有元素并判断“是否为独立元素”
以单位矩阵为例,主对角线上的元素为 1,其他为 0。如果你想找出这些“独立元素”,可以如下处理:
方式1:遍历所有元素
for (int i = 0; i < identity.rows; ++i) {for (int j = 0; j < identity.cols; ++j) {float val = identity.at<float>(i, j);std::cout << "Element(" << i << "," << j << ") = " << val << std::endl;}
}
方式2:只获取主对角线(主对角元素是单位矩阵中的“独立 1 元素”)
cv::Mat diagVec = identity.diag(); // 提取主对角线
for (int i = 0; i < diagVec.rows; ++i) {float val = diagVec.at<float>(i, 0);std::cout << "Diagonal[" << i << "] = " << val << std::endl;
}
简化获取所有“独立元素”的用途举例
假设你有如下矩阵:
cv::Mat m = (cv::Mat_<int>(3, 3) << 1, 0, 0,0, 2, 0,0, 0, 3);
你要提取 1、2、3(主对角线上的“独立”元素):
cv::Mat diagVec = cv::Mat::diag(m);
for (int i = 0; i < diagVec.rows; ++i) {int val = diagVec.at<int>(i, 0);std::cout << val << " ";
}
// 输出:1 2 3
4.2 二维矩阵中通过“块”访问元素(提取子区域)
语法 1:使用 cv::Range
cv::Mat submat = mat(cv::Range(row_start, row_end), cv::Range(col_start, col_end));
注意:区间是左闭右开,即 row ∈ [row_start, row_end),col ∈ [col_start, col_end)
示例:
cv::Mat mat = cv::Mat::eye(5, 5, CV_32F);// 提取第1~3行,第2~4列的子矩阵(3行3列)
cv::Mat block = mat(cv::Range(1, 4), cv::Range(2, 5));std::cout << "Block:\n" << block << std::endl;
4.3 指定 Rect 块区域访问(更常用于图像 ROI)
语法 2:使用 cv::Rect(x, y, width, height)
cv::Mat roi = mat(cv::Rect(x, y, width, height));
- (x, y) 是左上角坐标
- width, height 是子区域的宽度和高度
示例:
cv::Mat img = cv::imread("test.jpg");// 获取图像中间 100x100 区域
int cx = img.cols / 2, cy = img.rows / 2;
cv::Mat centerBlock = img(cv::Rect(cx - 50, cy - 50, 100, 100));
4.4 块访问在多维矩阵中(>=3维)
对多维 cv::Mat,不能直接使用 Rect,只能用 Range[]:
int sz[] = { 4, 4, 4 };
cv::Mat mat(3, sz, CV_8U);// 访问:dim0 ∈ [1,3), dim1 ∈ [0,2), dim2 ∈ [2,4)
cv::Range ranges[] = { cv::Range(1, 3), cv::Range(0, 2), cv::Range(2, 4) };
cv::Mat sub = mat(ranges);