OpenCV中矩阵数据的访问(二)

来源:互联网 发布:云计算就业前景 编辑:程序博客网 时间:2024/05/19 16:06
上一篇文章提到了访问矩阵中元素的前两种方式,下面讲第三种方式:正确的访问矩阵中数据的方式:

正确的方式
前面介绍的一些读取和写入矩阵数据的方式,实际上,你可能很少会使用它们。因为,在大多数情况下,你需要使用最有效率的方式来访问矩阵中的数据。如果使用以上的函数界面来访问数据,效率比较低,你应该使用指针方式来直接访问矩阵中数据。特别是,如果你想遍历矩阵中所有元素时,就更需要这样做了。
在用指针直接访问矩阵元素时,就需要格外注意矩阵结构体中的step成员。该成员是以字节为单位的每行的长度。而矩阵结构体的colswidth就不适合此时使用,因为为了访问效率,矩阵中的内存分配上,是以每四个字节做为最小单位的。因此如果一个矩阵的宽度是三个字节,那么就会在宽度上分配四个字节,而此时每行最后一个字节会被忽略掉。所以我们用step则会准确地按行访问数据。
我们可以通过以下例子,看一下rows,cols,height,width,step的数据,你可以通过改变矩阵的元素类型定义,来查看step的改变:
#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
//
矩阵元素为三通道8位浮点数
CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
printf("rows=%d,cols=%d,height=%d,width=%d,step=%d/n",mat->rows,mat->cols,mat->height,mat->width,mat->step);

}
如果我们的矩阵存储的是浮点型(或整数类型)数据,此时矩阵中每个元素占4字节,则如果我们用float类型指针指向下一行时,我们实际上要用float类型指针挪动step/4的长度,因为float类型指针每挪动一个单位就是4个字节长度。
如果我们的矩阵存储的是double类型数据,此时矩阵中每个元素占8字节,则如果我们用double类型指针指向下一行时,我们实际上要用double类型指针挪动step/8的长度,因为double类型指针每挪动一个单位就是8个字节长度。
我们重新看一下CvMat类型的数据结构定义,其中,data就是数据部分,指向data的指针可以是多种数据类型的:
typedef struct CvMat {
int type;
int step;
int* refcount; // for internal use only
union {
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;//
数据部分
union {
int rows;
int height;
};
union {
int cols;
int width;
};
} CvMat;

我们可以通过为矩阵赋值,和读取的例子,查看怎样使用step
:
#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
//
矩阵元素为三通道8位浮点数

CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
float *p;
int row,col;
for(row=0; row< mat->rows; row++)
{
p = mat->data.fl + row * (mat->step/4);
for(col = 0; col < mat->cols; col++)
{
*p = (float) row+col;
*(p+1) = (float) row+col+1;
*(p+2) =(float) row+col+2;
p+=3;
}
}

for(row = 0; row < mat->rows; row++)
{
p = mat->data.fl + row * (mat->step/4);
for(col = 0; col < mat->cols; col++)
{
printf("%f,%f,%f/t",*p,*(p+1),*(p+2));
p+=3;
}
printf("/n");
}
}

如果我们使用的指针类型为uchar*类型,则事情可能会简单一些,不用考虑step/4step/8等类似情况,我们推荐用这种方式。如下例所示:

#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
//
矩阵元素为三通道8位浮点数
CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
float *p;
int row,col;
for(row=0; row< mat->rows; row++)
{
p = (float*)(mat->data.ptr + row * mat->step);
for(col = 0; col < mat->cols; col++)
{
*p = (float) row+col;
*(p+1) = (float) row+col+1;
*(p+2) =(float) row+col+2;
p+=3;
}
}

for(row = 0; row < mat->rows; row++)
{
p = (float*)(mat->data.ptr + row * mat->step);
for(col = 0; col < mat->cols; col++)
{
printf("%f,%f,%f/t",*p,*(p+1),*(p+2));
p+=3;
}
printf("/n");
}
}

最后要注意一下,我们在每行都要使用step重新计算一下指针的位置,这好象不如从首指针从头到尾一直指下去,如我们上一文章的例子一样


#pragma comment( lib, "cxcore.lib" )
#include "cv.h"
#include <stdio.h>
void main()
{
//
矩阵元素为三通道浮点数
CvMat* mat = cvCreateMat(3,3,CV_32FC3);
cvZero(mat);//
将矩阵置0
//
为矩阵元素赋值


//
获得矩阵元素(0,0)的指针
float *p = (float*)cvPtr2D(mat, 0, 0);
//
为矩阵赋值
for(int i = 0; i < 9; i++)
{
//
为每个通道赋值
*p = (float)i*10; 
p++;
*p = (float)i*10+1;
p++;
*p = (float)i*10+2;
p++;
}

//
打印矩阵的值
p = (float*)cvPtr2D(mat, 0, 0);

for(i = 0; i < 9; i++)
{
printf("%2.1f,%2.1f,%2.1f/t",*p,*(p+1),*(p+2));
p+=3;
if((i+1) % 3 == 0)
printf("/n");
}
}

但是一定要注意了,这个例子其实是不对的!因为我们说过,分配矩阵内存空间时,是以四字节为最小单位的,这就很有可能有不到四个字节而取成四个字节的情况,所以,如果用矩阵首地址从头到尾指下去访问数据,就很有可能访问到不是数据的字节上去!这一点请务必牢记!!
综上所述,如果要直接访问矩阵中数据,请记住使用step的方案。


另一个需要知道的情况是,我们需要了解一个多维数组(矩阵)和一个一维,但是包含高维数据的数组之间的区别。假设,你有n个点(每个点有x,y,z坐标值需要保存到CvMat*中,你其实有四种方式可以使用,但这四种方式的存储形式不同。你可能使用一个二维矩阵,矩阵大小为n3列,数据类型为CV32FC1。你还可以使用一个二维矩阵,矩阵大小为3n列,数据类型为CV32FC1;第三种可能性是,你使用一个一维矩阵,n1列,数据类型为 CV32FC3;最后,你还可以使用1行三列,数据类型为CV32FC3.这几种方式,在内存分配上,有些是相同的,有些是不同的,如下所示:

n
个点的集合(n=5)
(x0 y0 z0) (x1 y1 z1) (x2 y2 z2) (x3 y3 z3) (x4 y4 z4)

n
1列时(数据类型CV32FC3)内存分配情况
x0 y0 z0 x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4

1
n列时(数据类型CV32FC3)内存分配情况
x0 y0 z0 x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4

n
3列时(数据类型CV32FC1)内存分配情况
x0 y0 z0 x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4

3
n列时(数据类型CV32FC1)内存分配情况
x0 x1 x2 x3 x4 y0 y1 y2 y3 y4 z0 z1 z2 z3 z4


我们可以看出,前三种的内存分配情况相同,但最后一种的内存分配不同。更复杂的是,如果有n维数组,每个数组的元素是c(c可能是通道数)时。所以,多维数组(矩阵)和一个一维但包含多维数据的数组一般是不同的。

对于一个RowsCols列,通道数为Channels的矩阵,访问其中第row,col,channel通道的数据,可以使用如下公式:
数据地址偏移量=row*Cols*Channels+col*Channels+channel
0 0
原创粉丝点击