0%

Mat

摘要

  • cv::Mat 类相关学习笔记

OpenCV cv::Mat 详解

在 OpenCV 中,cv::Mat 是一个用于表示图像的核心类。它是 OpenCV 中所有图像操作的基础,可以用于存储图像数据以及执行各种图像处理操作。cv::Mat 既可以表示二维图像(如灰度图像、RGB图像),也可以表示多维数据(如视频帧、深度图等)。

1. cv::Mat 基本概念

cv::Mat 是 OpenCV 的主要数据结构之一,代表一个多维矩阵。它能够存储不同类型的数据(如像素值、浮动数据、整数数据等)。图像通常作为矩阵存储,因此 cv::Mat 是处理图像和其他类型矩阵数据的基础。

2. cv::Mat 构造与初始化

cv::Mat 可以通过多种方式进行初始化。

构造函数

  1. 空矩阵构造
1
cv::Mat mat;

这会创建一个空的 cv::Mat 对象,矩阵大小为 0,数据为空。

  1. 指定大小的矩阵
1
cv::Mat mat(rows, cols, type);
  • rows:行数(矩阵的高度)。
  • cols:列数(矩阵的宽度)。
  • type:数据类型,通常由 CV_ 开头的常量指定(如 CV_8UC3 表示 8 位无符号 3 通道图像)。
  1. 初始化矩阵
1
cv::Mat mat(rows, cols, CV_8UC3, cv::Scalar(255, 0, 0));  // 创建一个 RGB 图像,填充为红色
  • 这里 cv::Scalar(255, 0, 0) 初始化矩阵中的所有像素值为红色(在 RGB 中,255 代表红色,0 代表绿色和蓝色)。
  1. 从文件加载图像
1
cv::Mat img = cv::imread("image.jpg");
  • 使用 cv::imread() 函数加载图像文件,并将其存储为 cv::Mat
  1. 深拷贝与浅拷贝
1
2
cv::Mat mat2 = mat;  // 浅拷贝
cv::Mat mat3 = mat.clone(); // 深拷贝
  • 浅拷贝:mat2 会与 mat 共享相同的数据,修改其中一个也会影响另一个。
  • 深拷贝:mat3 会复制 mat 的数据,修改 mat3 不会影响 mat

3. cv::Mat 数据结构

cv::Mat 是一个复杂的数据结构,包含了以下几个重要部分:

  1. 数据指针(data

    • cv::Mat 内部包含指向矩阵数据的指针。通过该指针,可以直接访问图像的像素值。
  2. 矩阵的尺寸(rowscols

    • rows:矩阵的行数。
    • cols:矩阵的列数。
  3. 矩阵的数据类型(depth()channels()

    • depth():返回矩阵的数据类型,表示每个元素的位深。例如,CV_8U 表示每个元素是 8 位无符号整数。
    • channels():返回矩阵的通道数。对于彩色图像,通常是 3(RGB),而对于灰度图像,是 1。
  4. 矩阵的步长(step

    • 步长指的是矩阵每一行的字节数(也就是每行的内存偏移量)。通过步长可以实现对矩阵不同区域的访问。
  5. 矩阵引用计数

    • cv::Mat 采用引用计数机制。当多个 cv::Mat 对象指向同一块数据时,引用计数会增加。数据会在引用计数为 0 时自动释放。

4. cv::Mat 常用函数

获取和设置像素值

  1. 访问像素值
1
2
cv::Mat image = cv::imread("image.jpg");
cv::Vec3b pixel = image.at<cv::Vec3b>(row, col);
  • cv::Vec3b 是一个包含 3 个元素的向量(用于表示 RGB 图像的三个颜色通道)。
  1. 设置像素值
1
image.at<cv::Vec3b>(row, col) = cv::Vec3b(255, 0, 0);  // 设置像素为红色

矩阵的形状和大小

1
2
3
cv::Size size = image.size();  // 获取图像的大小
int rows = image.rows; // 获取行数(高度)
int cols = image.cols; // 获取列数(宽度)

矩阵类型和通道数

1
2
int type = image.type();         // 获取矩阵的数据类型
int channels = image.channels(); // 获取矩阵的通道数

矩阵的复制

1
cv::Mat mat_copy = image.clone(); // 创建一个深拷贝

矩阵的拆分与合并

  1. 拆分图像通道
1
2
std::vector<cv::Mat> channels;
cv::split(image, channels); // 将 BGR 图像分割为 3 个单通道图像
  1. 合并多个图像通道
1
2
cv::Mat merged;
cv::merge(channels, merged); // 合并多个单通道图像为 BGR 图像

5. cv::Mat 的数据类型

OpenCV 使用 CV_ 作为前缀来表示矩阵的数据类型。以下是一些常见的类型:

  • **CV_8U**:8 位无符号整数,常用于表示灰度图像或每通道数据为 0-255 的图像。
  • **CV_32F**:32 位浮动数,常用于表示浮动图像或特征数据。
  • **CV_64F**:64 位浮动数,常用于精度较高的图像数据或数学运算。
  • **CV_8SC3**:表示每个元素为 8 位带符号整数且有 3 个通道(例如,RGB 图像)。

通过 type() 方法可以获取图像的类型。

6. cv::Mat 的运算与操作

cv::Mat 还提供了许多常见的数学运算和图像处理方法:

  • 加法、减法、乘法、除法
1
2
3
4
cv::Mat result = image + 50;  // 图像加上常数
cv::Mat result = image - 50; // 图像减去常数
cv::Mat result = image * 2; // 图像乘以常数
cv::Mat result = image / 2; // 图像除以常数
  • 图像变换
1
2
cv::Mat rotated;
cv::warpAffine(image, rotated, rotationMatrix, image.size()); // 仿射变换
  • 图像滤波
1
2
cv::Mat blurred;
cv::GaussianBlur(image, blurred, cv::Size(5, 5), 0); // 高斯模糊
  • 图像矩阵与其它矩阵的运算
1
2
3
cv::Mat matA = cv::Mat::ones(3, 3, CV_32F);
cv::Mat matB = cv::Mat::eye(3, 3, CV_32F);
cv::Mat result = matA + matB; // 矩阵相加

7. 总结

  • cv::Mat 是 OpenCV 中的核心数据结构,用于表示图像和多维矩阵数据。
  • 它支持丰富的图像处理操作,包括像素级操作、矩阵运算、形态学变换、滤波、几何变换等。
  • cv::Mat 提供了数据类型、形状、通道数等信息,以及内存管理和引用计数的功能,简化了图像数据的处理。

通过理解和熟练使用 cv::Mat,可以方便地实现图像处理任务,如图像读写、转换、滤波、矩阵运算等。

cv::Mat 常用成员变量和成员方法

cv::Mat 是 OpenCV 中的一个核心数据结构,广泛用于表示图像、矩阵以及其他多维数据。它提供了许多成员变量和成员方法,便于对矩阵进行各种操作。以下是 cv::Mat 常用的成员变量和成员方法的详细介绍。

常用成员变量

  1. rows

    • 类型int
    • 描述:表示矩阵的行数,即图像的高度。
    1
    int rows = mat.rows;
  2. cols

    • 类型int
    • 描述:表示矩阵的列数,即图像的宽度。
    1
    int cols = mat.cols;
  3. depth()

    • 类型int
    • 描述:返回矩阵数据的深度,表示每个元素的位深。常见的值有:
      • CV_8U(8 位无符号整数)
      • CV_8S(8 位有符号整数)
      • CV_16U(16 位无符号整数)
      • CV_16S(16 位有符号整数)
      • CV_32S(32 位有符号整数)
      • CV_32F(32 位浮动数)
      • CV_64F(64 位浮动数)
    1
    int depth = mat.depth();
  4. channels()

    • 类型int
    • 描述:返回矩阵的通道数。对于单通道图像(如灰度图像)为 1,对于多通道图像(如 RGB 图像)为 3。
    1
    int channels = mat.channels();
  5. type()

    • 类型int
    • 描述:返回矩阵的数据类型(结合 depth()channels())。常见的返回值包括:
      • CV_8UC3:8 位无符号整数 3 通道(RGB 图像)
      • CV_32F:32 位浮动数单通道
    1
    int type = mat.type();
  6. data

    • 类型uchar*
    • 描述:返回指向矩阵数据的指针。通过 data 指针,可以直接访问矩阵的元素。
    1
    uchar* ptr = mat.data;
  7. step

    • 类型size_t
    • 描述:每行的字节数。step 是矩阵中每行的内存偏移量,也就是说它告诉你一行数据的大小。通常用于在内存中遍历矩阵时,计算每行的起始地址。
    1
    size_t step = mat.step;
  8. empty()

    • 类型bool
    • 描述:判断矩阵是否为空(即是否有有效数据)。如果矩阵没有分配内存,或者矩阵的尺寸为 0,则返回 true
    1
    bool isEmpty = mat.empty();

常用成员方法

  1. at<T>(row, col)

    • 描述:返回指定位置 (row, col) 的元素值。T 是矩阵元素的数据类型,可以是 uchar, int, float 等。
    1
    2
    cv::Mat mat = cv::Mat::ones(3, 3, CV_8UC1);  // 创建一个 3x3 的 8 位单通道矩阵
    uchar value = mat.at<uchar>(1, 1); // 获取 (1, 1) 位置的像素值

    这种方法非常方便,但它有性能开销,特别是在处理大型矩阵时。对于大量的像素访问,可以考虑使用 ptr() 方法。

  2. ptr<T>(row)

    • 描述:返回指向指定行的指针,T 是矩阵的元素类型。可以通过该指针访问该行的元素,通常用于高效的矩阵操作。
    1
    uchar* rowPtr = mat.ptr<uchar>(1);  // 获取第 1 行的指针

    ptr() 方法返回的是 uchar* 类型的指针,因此可以直接访问和修改该行的数据。

  3. clone()

    • 描述:返回矩阵的深拷贝,创建一个新的矩阵并复制数据。原矩阵的修改不会影响拷贝。
    1
    cv::Mat matCopy = mat.clone();  // 创建一个矩阵的深拷贝
  4. copyTo()

    • 描述:将当前矩阵的数据复制到另一个矩阵。
    1
    2
    cv::Mat mat2;
    mat.copyTo(mat2); // 将 mat 的数据复制到 mat2
  5. reshape()

    • 描述:将矩阵的尺寸更改为新形状,但不改变其数据。可以用来改变矩阵的行列数目,但总的元素数量不变。
    1
    cv::Mat matReshaped = mat.reshape(0, newRows);  // 改变矩阵形状
  6. resize()

    • 描述:调整矩阵的大小。与 reshape() 不同,resize() 会根据新的大小修改矩阵的内容。
    1
    cv::resize(mat, matResized, cv::Size(newWidth, newHeight));  // 调整矩阵的大小
  7. convertTo()

    • 描述:将矩阵的数据类型转换为其他类型。
    1
    2
    cv::Mat matConverted;
    mat.convertTo(matConverted, CV_32F); // 将矩阵转换为 32 位浮动数类型
  8. split()

    • 描述:将一个多通道图像分割为多个单通道图像。
    1
    2
    std::vector<cv::Mat> channels;
    cv::split(mat, channels); // 分割为多个单通道图像
  9. merge()

    • 描述:将多个单通道图像合并为一个多通道图像。
    1
    2
    cv::Mat matMerged;
    cv::merge(channels, matMerged); // 合并多个通道为一个图像
  10. setTo()

    • 描述:将矩阵的所有元素设置为一个特定的值,或根据条件设置为特定的值。
    1
    mat.setTo(cv::Scalar(0, 0, 255));  // 将矩阵的所有像素设置为红色(BGR)
  11. row()col()

    • 描述:返回矩阵的某一行或某一列作为一个新的矩阵。
    1
    2
    cv::Mat rowMat = mat.row(1);  // 获取第 1 行
    cv::Mat colMat = mat.col(0); // 获取第 0 列
  12. transpose()

    • 描述:返回矩阵的转置。
    1
    cv::Mat matTransposed = mat.t();  // 获取矩阵的转置
  13. inv()

    • 描述:返回矩阵的逆。
    1
    cv::Mat matInv = mat.inv();  // 获取矩阵的逆
  14. norm()

    • 描述:计算矩阵的范数(例如,L2 范数)。
    1
    double normVal = cv::norm(mat);  // 计算矩阵的 L2 范数
  15. empty()

    • 描述:判断矩阵是否为空(即是否没有分配内存或矩阵的尺寸为 0)。
    1
    bool isEmpty = mat.empty();  // 判断矩阵是否为空

总结

cv::Mat 是 OpenCV 中非常核心的类,提供了丰富的成员变量和方法,用于图像的存储、操作和转换。理解这些成员方法和变量可以帮助你更高效地进行图像处理和矩阵运算。

cv::Mat::total() 函数 详解

cv::Mat::total() 是 OpenCV 中 cv::Mat 类的一个成员函数,主要用于计算矩阵的总元素数量。它返回矩阵中所有元素的总数,等于矩阵的行数 (rows) 和列数 (cols) 的乘积。

函数定义

1
size_t total() const;

返回值

  • 类型size_t
  • 描述:返回矩阵中所有元素的总数。如果矩阵是二维的,则返回行数与列数的乘积;如果矩阵是多维的,返回每个维度大小的乘积。

作用

total() 计算矩阵所有元素的数量,而不考虑矩阵的深度(即每个元素的字节数)。如果你知道矩阵的形状,可以用它来确定矩阵总共有多少个元素。

例如:

  • 对于一个 3x3 的矩阵,total() 将返回 9
  • 对于一个 4x5 的矩阵,total() 将返回 20

例子

1. 使用 total() 计算二维矩阵的总元素数量

1
2
3
4
5
6
7
8
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
cv::Mat mat = cv::Mat::ones(4, 5, CV_32F); // 创建一个 4x5 的矩阵,元素值为 1
std::cout << "Total elements: " << mat.total() << std::endl;
return 0;
}

输出:

1
Total elements: 20

这里,mat 是一个 4x5 的矩阵,因此 mat.total() 返回 20,即矩阵中有 20 个元素。

2. 使用 total() 计算多维矩阵的总元素数量

对于多维矩阵,total() 会计算所有维度的元素数量。例如,如果矩阵是三维的,那么它将返回三个维度的大小的乘积。

1
2
3
4
5
6
7
8
9
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
cv::Mat mat = cv::Mat::ones(3, 4, CV_32F); // 创建一个 3x4 的二维矩阵
mat = mat.reshape(1, 2); // 将矩阵重塑为 2x12 的二维矩阵
std::cout << "Total elements after reshape: " << mat.total() << std::endl;
return 0;
}

输出:

1
Total elements after reshape: 24

在这个例子中,矩阵最初是 3x4,后来被重塑为 2x12 的形状。因此,mat.total() 返回 24,即矩阵中有 24 个元素。

为什么使用 total()

  • 简洁性:使用 total() 可以快速获取矩阵的总元素数量,而不需要手动计算行数和列数的乘积。
  • 多维支持:对于多维矩阵(例如,n 维矩阵),total() 可以自动处理所有维度的计算,简化代码。
  • 性能total() 是一个常量时间操作,其计算时间为 O(1),它直接返回矩阵的元素总数。

应用场景

  1. 矩阵元素遍历:在遍历矩阵时,可以使用 total() 来确定矩阵中元素的总数,从而控制循环次数。
  2. 内存占用估算:如果你知道矩阵的元素类型和总元素数,可以估算矩阵占用的内存大小。例如,通过 total() 结合 elemSize()(每个元素的字节数),可以计算矩阵的总内存占用。

示例代码:计算内存占用

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
cv::Mat mat = cv::Mat::ones(1000, 1000, CV_8UC3); // 创建一个 1000x1000 的彩色图像
size_t totalElements = mat.total(); // 获取元素总数
size_t totalMemory = totalElements * mat.elemSize(); // 计算矩阵占用的内存

std::cout << "Total elements: " << totalElements << std::endl;
std::cout << "Total memory: " << totalMemory << " bytes" << std::endl;

return 0;
}

输出:

1
2
Total elements: 3000000
Total memory: 9000000 bytes

在这个例子中,mat 是一个 1000x1000 的彩色图像(每个像素 3 个通道),所以总共有 3000000 个元素,每个元素占用 3 字节,因此矩阵的总内存占用为 9000000 字节。

总结

  • cv::Mat::total() 用于获取矩阵中所有元素的总数,返回一个 size_t 类型的值。
  • 它是一个简洁且高效的函数,适用于计算任何维度矩阵的元素数量。
  • 在图像处理和矩阵操作中,total() 可以方便地帮助你了解矩阵的规模,从而优化内存管理和性能。
感谢老板支持!敬礼(^^ゞ