Mat中的step[i],step1(i),elemsize,elemsize1

Mat中的step[i], step1(i), elemsize, elemsize1在读取数据和数据转换的时候经常用到,分析了目前比较流行的几种说法,加入了一些自己的见解。

概念

elemsize指的是每个像素的字节数
elemsize1指的是每个像素值的字节数(比如三通道的图像每个像素有三个像素值)
step是一个数组,用 step 数据属性可得到单位是字节的图像的有效宽度,即使图像的类型不是 uchar,step 仍然能提供行的字节数。

举例说明

  1. opencv中Mat类的elemsize是每个像素的字节数,如果一个图像是3通道,每个通道是16位,那么其elemsize就是3*(16/8)=6,即每个元素占6个字节

  2. opencv中Mat类的elemsize1是指每个像素每个通道的像素值所占的字节数,对应于上面就是2个字节

  3. opencv中Mat类的step,step是一个数组,用 step 数据属性可得到单位是字节的有效宽度。即使图像的类型不是 uchar,step 仍然能提供行的字节数。有以下关系

step1(i) = step[i] / elemsize1;
step[m-1] = elemsize;

其中m是图像的维数,step1(m-1)=channels;

矩阵 (M) 中数据元素的地址计算公式:

$addr(Mi_0,i_1,…i_{m-1}) = M.data + M.step[0] * i0 + M.step[1] * i1 + … + M.step[m-1] * im-1 $
(其中 m = M.dims M的维度)

下面举例:对于2维的情况:
一个 3 X 4 的矩阵,假设其数据类型为 CV_8U,也就是单通道的 uchar 类型

这是一个二维矩阵,那么维度为 2 即:

M.dims == 2
M.rows == 3;
M.cols == 4;

sizeof(uchar) = 1,那么每一个数据元素大小为 1 。即

M.elemSize() == 1
M.elemSize1() == 1;

CV_8U 得到 M.depth() == 0, M.channels() == 1;

因为是二维矩阵,那么 step 数组只有两个值, step[0] 和 step[1] 分别代表一行的数据大小和一个元素的数据大小,则

M.step[0] == 4,
M.step[1] == 1
M.step1(0) == M.cols = 4;
M.step1(1) == 1;

假设上面的矩阵数据类型是 CV_8UC3,也就是三通道,则

M.dims == 2
M.channels() == 3
M.depth() == 0
M.elemSize() == 3 (每一个元素包含3个uchar值)
M.elemSize1() == 1 (elemSize / channels)
M.step[0] == M.cols * M.elemSize() == 12
M.step[1] == M.channels() * M.elemSize1() == M.elemSize() == 3

在这里插入图片描述

上面是一篇博客里边提到的,说的不是太清楚,下面我再细讲一下关于step的概念,step其实是有效宽度,其与图像数据缓存的时候的数据对齐有关。

在彩色图像中,图像数据缓冲区的前 3 字节表示左上角像素的三个通道的值,接下来的 3字节表示第 1 行的第 2 个像素,以此类推(注意 OpenCV 默认的通道次序为 BGR)。一个宽 W高 H 的图像所需的内存块大小为 W×H×3 uchars。不过出于性能上的考虑,我们会用几个额外的像素来填补行的长度。这是因为,如果行数是某个数字(例如 8)的整数倍,图像处理的性能可能会提高,因此最好根据内存配置情况将数据对齐。当然,这些额外的像素既不会显示也不被保存,它们的额外数据会被忽略OpenCV 把经过填充的行的长度指定为有效宽度。如果图像没有用额外的像素填充,那么有效宽度就等于实际的图像宽度。我们已经学过,用 cols和 rows 属性可得到图像的宽度和高度。与之类似,用 step 数据属性可得到单位是字节的有效宽度。即使图像的类型不是 uchar,step 仍然能提供行的字节数。 用 step 属性可得到一行的总字节数(包括填充像素)。通常可以用下面的方法得到第 j 行、 第 i 列的像素的地址:
// (j,i)像素的地址,即&image.at(j,i)
data= image.data+j*image.step+i*image.elemSize();

对于3维时(通道数不算另一维):

在这里插入图片描述
上面是一个 3 X 4 X 6 的矩阵,假设其数据类型为 CV_16SC4,也就是 short 类型

M.dims == 3 ; M.channels() == 4 ;
M.elemSize1() == sizeof(short) == 2 ;
M.rows == M.cols == –1;
M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;
M.step[0] == 4 * 6 * M.elemSize() == 192;
M.step[1] == 6 * M.elemSize() == 48;
M.step[2] == M.elemSize() == 8;
M.step1(0) == M.step[0] / M.elemSize() == 192 / 2 == 96 (第一维度(即面的元素个数) * 通道数);
M.step1(1) == M.step[1] / M.elemSize() == 48 / 2 == 24(第二维度(即行的元素个数/列宽) * 通道数);
M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三维度(即元素) * 通道数);

代码演示

void myLog(Mat src)
{
    
    
	int dim = src.dims;
	for (int i = 0; i < dim; i++)
	{
    
    
		cout << src.type() <<" step[" <<i << "]:"<< src.step[i] << "\t";
		cout << src.type() << " step1[" << i << "]:" << src.step1(i) << endl;
	}
	cout << src.type() << " elemSize:" << src.elemSize() << "\t";
	cout << src.type() << " elemSize1:" << src.elemSize1() << endl;
}

int main(int argc, char** argv)
{
    
    
	Mat test = Mat(3, 4, CV_8UC1);
	myLog(test);
	Mat test1 = Mat(3, 4, CV_8UC3);
	myLog(test1);
	Mat test2 = Mat(3, 4, CV_16SC4);
	myLog(test2);
	int sz[3] = {
    
     3, 4, 6};//sz指定数组尺寸大小3*4*6
	uchar* p; //用于访问像素
	Mat test3(3, sz, CV_16SC4);//指定n维,当前为3维;
	for (int i = 0; i < 3; i++) //行数
		for (int j = 0; j < 4; j++)//列数
			for (int k = 0; k < 6; k++)//高
			{
    
    
				p = test3.data + test3.step[0] * i + test3.step[1] * j + test3.step[2] * k; //访问m_nut(i,j,k)
				(*p) = i + j + k; // 为当前访问的像素赋值
			}
	myLog(test3);

	return 0;
}

结果:
在这里插入图片描述

参考:https://blog.csdn.net/LIYUAN123ZHOUHUI/article/details/52922951

猜你喜欢

转载自blog.csdn.net/weixin_44456692/article/details/110493884