四、cv::Mat的介绍和使用

发布于:2025-07-24 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、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 BGR
    printMat(mat3, "Constructor with init value (color)");

    // 4. 多维数组
    int sizes[] = {2, 2, 2};  // 3D array
    cv::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)); // 中间2x2
    printMat(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 -> Mat
    std::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);

网站公告

今日签到

点亮在社区的每一天
去签到