OpenCV: Mat存储方式全解析-单通道、多通道内存布局详解
在使用 OpenCV 的过程中,我们经常会接触 cv::Mat
这种图像矩阵类型。理解 Mat
在内存中的布局非常重要,尤其是做指针访问、性能优化、SIMD(如 AVX/NEON)加速时。本文将详细介绍:
Mat
的存储方式单通道 vs 多通道的内存布局
如何用指针访问某个像素
判断
Mat
是否连续实战图示与代码
1️⃣ Mat
的存储特性
cv::Mat
是一个行优先(Row-major)、通常连续(Continuous)的多维数组,数据区存储像素值。
行优先:按行存储,每一行像素紧挨着。
通道连续:多通道像素的各通道值依次存储。
可能有行间对齐填充:当
Mat
是 ROI(子图)或手动创建带 padding 的图像时,行与行之间可能有“间隙”,此时Mat::isContinuous()
会返回false
。
2️⃣ 单通道示例(CV_8UC1)
假设一张 3×3 的灰度图:
在内存中是这样的:
data →
[10][20][30][40][50][60][70][80][90]
每个像素占用 1 字节,行优先顺序排列。
3️⃣ 多通道示例(CV_8UC3)
假设一张 2×3 的彩色图像,每个像素是 (B,G,R)
:
内存布局:
Row 0: [10][20][30][40][50][60][70][80][90]
Row 1: [11][21][31][41][51][61][71][81][91]
✅ 注意:
并不是“打包成一个 24 位整数”,而是 3 个独立的字节 挨着存储。
每像素大小 =
elemSize()
= 3 字节。
4️⃣ 像素索引 → 内存地址
要访问 (row, col)
的像素,可以用
cv::Mat img = cv::imread("test.png"); // 假设是 CV_8UC3
int row = 1, col = 2;// 定位到(row,col)的像素指针
uchar* pixelPtr = img.data + row * img.step + col * img.elemSize();uchar B = pixelPtr[0];
uchar G = pixelPtr[1];
uchar R = pixelPtr[2];
img.data
:首地址img.step
:每行占用字节数img.elemSize()
:每个像素占用字节数(多通道时>1)
5️⃣ 判断 Mat 是否连续
有些操作(如 memcpy
、手动指针循环)需要连续内存
if (img.isContinuous()) {// 数据连续,可以直接用指针一次性遍历所有像素
} else {// 数据不连续,需要按行处理
}
6️⃣ 图示
下面是 2×3,CV_8UC3
的直观示意图:
内存布局 (2x3, CV_8UC3):
data →
┌───────────── Row 0 ─────────────┐┌───────────── Row 1 ─────────────┐
[10][20][30][40][50][60][70][80][90][11][21][31][41][51][61][71][81][91]
└───────────── 9 * 1 byte ───────┘└───────────── 9 * 1 byte ───────┘
7️⃣ 小结
cv::Mat
默认是行优先、连续存储。多通道像素值按通道顺序紧挨着。
使用
data + row*step + col*elemSize()
快速定位像素。用
isContinuous()
判断是否可以一次性处理所有数据。