矩阵核心 — 元数据、构造与基础操作¶
矩阵元数据¶
矩阵结构
Mat类使用行主序存储布局,支持填充和step。stride在该类中是step的向后兼容别名。这种设计实现了高效的内存访问模式,并与DSP库兼容。
核心维度¶
-
int row: 矩阵的行数。 -
int col: 矩阵的列数。 -
int element: 元素总数 = 行数 × 列数。
内存布局¶
-
int step/int stride: 每行总元素数(逻辑列 + 填充)。在本类中二者等价,stride是step别名。 -
int pad: 两行之间的填充元素数。填充用于内存对齐和DSP优化。 -
int memory: 数据缓冲区大小 = 行数 × step(以float元素为单位)。
数据指针¶
-
float *data: 指向包含矩阵元素的数据缓冲区的指针。元素按行主序存储:位置(i, j)的元素位于data[i * step + j]。 -
float *temp: 指向临时数据缓冲区的指针(如果已分配)。某些操作内部使用。
内存管理标志¶
-
bool ext_buff: 标志矩阵是否使用外部缓冲区。当为true时,析构函数不会释放内存(调用者负责管理)。 -
bool sub_matrix: 标志矩阵是否为另一个矩阵的子集/视图。当为true时,矩阵与父矩阵共享数据。
内存布局示例
对于3×4矩阵,步幅=4(无填充):
[a b c d] 行 0: data[0*4+0] 到 data[0*4+3]
[e f g h] 行 1: data[1*4+0] 到 data[1*4+3]
[i j k l] 行 2: data[2*4+0] 到 data[2*4+3]
对于3×4矩阵,步幅=6(填充=2):
ROI 结构¶
感兴趣区域
ROI(感兴趣区域)结构表示矩阵的矩形子区域。它与view_roi()和copy_roi()函数一起使用,以高效地提取或引用子矩阵。
ROI 元数据¶
-
int pos_x: 起始列索引(左上角的x坐标)。 -
int pos_y: 起始行索引(左上角的y坐标)。 -
int width: ROI的宽度(列数)。 -
int height: ROI的高度(行数)。
ROI 构造函数¶
描述:
构造一个 ROI 对象,默认值为 (0, 0, 0, 0)。
参数:
-
int pos_x: 起始列索引 -
int pos_y: 起始行索引 -
int width: ROI 的宽度(列数) -
int height: ROI 的高度(行数)
ROI 重置函数¶
描述:
重置 ROI 的位置和大小。
参数:
-
int pos_x: 起始列索引 -
int pos_y: 起始行索引 -
int width: ROI 的宽度(列数) -
int height: ROI 的高度(行数)
返回值:
void
ROI 面积函数¶
描述:
计算 ROI 的面积。
参数:
void
返回值:
整数类型 ROI 的面积
打印函数¶
调试工具
这些函数对于调试和理解矩阵状态至关重要。使用它们来验证矩阵维度、内存布局和数据值。
打印矩阵信息¶
描述:
打印全面的矩阵信息,包括:
-
维度:行数、列数、元素数
-
内存布局:填充数、步幅、内存大小
-
指针:数据缓冲区地址、临时缓冲区地址
-
标志:外部缓冲区使用情况、子矩阵状态
-
警告:维度不匹配、无效状态
参数:
void
返回值:
void
使用建议:
-
调试: 对于验证矩阵状态和检测内存问题至关重要。
-
内存分析: 显示实际内存使用情况与逻辑大小的对比,帮助识别内存效率问题。
-
子矩阵检测: 清楚地指示矩阵是否为视图,这会影响内存管理。
打印矩阵元素¶
描述:
以格式化表格形式打印矩阵元素。可选择显示由视觉分隔符分隔的填充元素。
参数:
bool show_padding: 如果为true,显示填充值并用分隔符|分隔。如果为false,仅显示实际矩阵元素。
返回值:
void
使用建议:
-
格式化: 元素以固定宽度(12个字符)格式化以保持对齐。
-
填充可视化:
show_padding选项有助于理解内存布局和验证填充值。 -
大矩阵: 对于非常大的矩阵,考虑使用
view_roi()打印特定区域。
构造与析构函数¶
内存管理
构造函数自动处理内存分配。析构函数仅在内存是内部分配的情况下安全释放内存(不包括外部缓冲区或视图)。构造后始终检查data指针以确保分配成功。
内存分配¶
描述:
根据计算的内存需求为矩阵分配内存的内部函数。设置ext_buff = false并分配row * step个float元素。
参数:
void
返回值:
void
使用建议:
-
自动调用: 由构造函数自动调用。很少需要手动调用。
-
内存计算: 分配
row * step个元素,可能包括填充。 -
错误处理: 如果分配失败,
data保持为nullptr。构造后始终检查data。
默认构造函数¶
描述:
默认构造函数创建一个1×1的零矩阵。这对于初始化和作为错误情况的返回值很有用。
数学原理:
在某些上下文中创建矩阵运算的恒等元素,尽管通常您会希望指定维度。
参数:
void
返回值:
Mat - 一个元素为0的1×1矩阵。
构造函数 - Mat(int rows, int cols)¶
描述:
构造函数创建具有指定维度的矩阵。所有元素初始化为零。这是最常用的构造函数。
参数:
-
int rows: 行数(必须 > 0)。 -
int cols: 列数(必须 > 0)。
返回值:
Mat - 一个rows×cols的矩阵,所有元素初始化为0。
使用建议:
-
零初始化: 所有元素使用
memset设置为零,确保干净的状态。 -
内存布局: 创建连续的内存布局,无填充(stride = cols)。
-
错误处理: 如果内存分配失败,
data将为nullptr。始终验证分配成功。
构造函数 - Mat(int rows, int cols, int step)¶
描述:
构造函数创建具有指定维度、列数和步幅的矩阵。当需要填充以进行内存对齐或DSP优化时很有用。
参数:
-
int rows: 行数。 -
int cols: 列数。 -
int step: 每行总元素数(必须 ≥ cols)。填充 = step - cols。
返回值:
Mat - 一个具有步幅的rows×cols矩阵,所有元素初始化为0。
使用建议:
-
DSP优化: 某些DSP库需要对齐的内存。使用步幅确保适当的对齐。
-
内存效率: 填充允许在对齐边界上进行高效的向量化操作。
-
兼容性: 实现与使用步幅内存布局的外部库的兼容性。
构造函数 - Mat(float *data, int rows, int cols)¶
描述:
构造函数在外部数据缓冲区上创建矩阵视图。矩阵不拥有内存;调用者负责管理它。对于与现有数据数组接口很有用。
参数:
-
float *data: 指向外部数据缓冲区的指针(在矩阵生命周期内必须保持有效)。 -
int rows: 行数。 -
int cols: 列数。
返回值:
Mat - 一个ext_buff = true的矩阵视图。
使用建议:
-
零拷贝: 不发生内存拷贝;矩阵直接引用外部数据。
-
生命周期管理: 外部缓冲区在矩阵存在期间必须保持有效。析构函数不会释放此内存。
-
数据布局: 假设行主序布局,无填充(stride = cols)。
-
使用场景:
-
包装C数组
-
与其他库接口
-
避免不必要的拷贝
构造函数 - Mat(float *data, int rows, int cols, int step)¶
描述:
构造函数在具有指定step的外部数据缓冲区上创建矩阵视图。支持带填充的行主序内存布局以实现DSP兼容性。
参数:
-
float *data: 指向外部数据缓冲区的指针(在矩阵生命周期内必须保持有效)。 -
int rows: 行数。 -
int cols: 列数。 -
int step: 每行总元素数(必须 ≥ cols)。
返回值:
Mat - 一个ext_buff = true并具有指定step的矩阵视图。
使用建议:
-
步幅布局: 对于使用步幅内存布局的DSP库至关重要。
-
内存安全: 与上一个构造函数相同的生命周期要求 - 外部缓冲区必须保持有效。
-
填充支持: 可以处理行间有填充的缓冲区。
拷贝构造函数 - Mat(const Mat &src)¶
描述:
拷贝构造函数从源矩阵创建新矩阵。使用智能拷贝策略:常规矩阵进行深拷贝,子矩阵视图进行浅拷贝。
拷贝策略:
-
常规矩阵: 深拷贝 - 分配新内存并拷贝所有数据
-
子矩阵视图: 浅拷贝 - 与源共享数据(创建另一个视图)
参数:
const Mat &src: 源矩阵。
返回值:
Mat - 根据源类型具有拷贝或共享数据的新矩阵。
使用建议:
-
自动选择: 根据源矩阵类型自动选择深拷贝或浅拷贝。
-
内存效率: 子矩阵视图进行浅拷贝以避免不必要的内存分配。
-
独立性: 深拷贝是独立的;修改不会影响源。
析构函数¶
描述:
析构函数安全地释放分配的内存。仅释放内部分配的内存(ext_buff = false)。外部缓冲区和视图不会被释放。
内存管理:
-
如果
ext_buff = false,释放data缓冲区 -
如果已分配,释放
temp缓冲区 -
对外部缓冲区或视图不执行任何操作
参数:
void
返回值:
void
构造函数和析构函数规则
- 构造函数函数必须与类名相同且无返回类型
- C++允许通过更改参数数量/顺序进行函数重载
- 当对象超出作用域时,析构函数会自动调用
- 构造后始终检查
data != nullptr以验证分配成功
元素访问¶
矩阵索引
Mat类使用运算符重载提供直观的矩阵元素访问。operator()允许使用A(i, j)这样的自然语法,而不是A.data[i * step + j]。实现自动处理步幅和填充。
访问矩阵元素(非常量)¶
描述:
以读写方式访问矩阵元素。返回元素的引用,允许读取和修改。
数学原理:
位置(row, col)的元素访问为data[row * step + col],其中step考虑了填充。
参数:
-
int row: 行索引(基于0,必须在范围[0, row-1]内)。 -
int col: 列索引(基于0,必须在范围[0, col-1]内)。
返回值:
float& - 矩阵元素的引用,允许修改。
使用建议:
-
边界检查: 为了性能,不进行自动边界检查。确保索引有效。
-
步幅处理: 自动考虑步幅,因此可以正确处理带填充的矩阵。
-
性能: 内联函数,开销最小,适用于紧密循环。
-
示例:
A(2, 3) = 5.0f;将第2行第3列的元素设置为5.0。
访问矩阵元素(常量)¶
描述:
以只读方式访问矩阵元素。返回常量引用,防止修改。当矩阵为const时使用。
参数:
-
int row: 行索引(基于0)。 -
int col: 列索引(基于0)。
返回值:
const float& - 矩阵元素的常量引用(只读)。
使用建议:
-
常量正确性: 实现正确的const-correct代码。在const成员函数中使用此版本。
-
安全性: 防止意外修改const矩阵。
运算符重载
这些函数重载了()运算符,实现了自然的矩阵索引语法:
数据操作¶
复制其他矩阵到当前矩阵¶
描述:
将源矩阵的元素复制到当前矩阵的指定位置。
参数:
-
const Mat &src: 源矩阵对象 -
int row_pos: 目标矩阵的起始行索引 -
int col_pos: 目标矩阵的起始列索引
返回值:
错误代码
复制矩阵头部¶
描述:
将源矩阵的头部信息复制到当前矩阵。
参数:
const Mat &src: 源矩阵对象
返回值:
错误代码
获取子矩阵视图¶
描述:
获取当前矩阵的子矩阵视图。
参数:
-
int start_row: 起始行索引 -
int start_col: 起始列索引 -
int roi_rows: 子矩阵的行数 -
int roi_cols: 子矩阵的列数
返回值:
子矩阵对象
获取子矩阵视图 - 使用 ROI 结构¶
描述:
获取当前矩阵的子矩阵视图,使用 ROI 结构。
参数:
const Mat::ROI &roi: ROI 结构对象
返回值:
子矩阵对象
警告
与 ESP-DSP 不同,view_roi 不允许设置步长,因为它会根据列数和填充数自动计算步长。该函数还会拒绝非法请求,即超出范围的请求。
获取子矩阵副本¶
描述:
获取当前矩阵的子矩阵副本。
参数:
-
int start_row: 起始行索引 -
int start_col: 起始列索引 -
int height: 子矩阵的行数 -
int width: 子矩阵的列数
返回值:
子矩阵对象
获取子矩阵副本 - 使用 ROI 结构¶
描述:
获取当前矩阵的子矩阵副本,使用 ROI 结构。
参数:
const Mat::ROI &roi: ROI 结构对象
返回值:
子矩阵对象
获取矩阵块¶
描述:
获取当前矩阵的块。
参数:
-
int start_row: 起始行索引 -
int start_col: 起始列索引 -
int block_rows: 块的行数 -
int block_cols: 块的列数
返回值:
块对象
viewroi | copyroi | block 之间的区别
-
view_roi: 从该矩阵浅拷贝子矩阵 (ROI)。 -
copy_roi: 从该矩阵深拷贝子矩阵 (ROI)。复制内存拷贝,速度更快。 -
block: 从该矩阵深拷贝块。逐个元素拷贝,速度更慢。
交换行¶
描述:
交换当前矩阵的两行。
参数:
-
int row1: 第一行索引 -
int row2: 第二行索引
返回值:
void
交换列¶
描述:
交换当前矩阵的两列。
参数:
-
int col1: 第一列索引 -
int col2: 第二列索引
返回值:
void
清除矩阵¶
描述:
通过将所有元素设置为零来清除矩阵。
参数:
void
返回值:
void