您的位置:首页 > 运维架构

OpenCV中矩阵数据的访问(一)(Learning OpenCV第三章2)

2014-10-28 22:58 405 查看
OpenCV中有三种方式访问矩阵中的数据元素:容易的方式,困难的方式,以及正确的方式。以下先讲容易的方式和困难的方式。

容易的方式

最容易的方式是使用宏CV_MAT_ELEM( matrix, elemtype, row, col ),输入参数是矩阵的指针,矩阵元素类型,行,列,返回值是相应行,列的矩阵元素,例如:

CvMat* mat = cvCreateMat(5,5,CV_32FC1);

float element = CV_MAT_ELEM(*mat,float,3,2);

以下是一个例子:

#pragma comment( lib, "cxcore.lib" )

#include "cv.h"

#include <stdio.h>

void main()

{

CvMat* mat = cvCreateMat(3,3,CV_32FC1);

cvZero(mat);//将矩阵置0

//为矩阵元素赋值

CV_MAT_ELEM( *mat, float, 0, 0 ) = 1.f;

CV_MAT_ELEM( *mat, float, 0, 1 ) = 2.f;

CV_MAT_ELEM( *mat, float, 0, 2 ) = 3.f;

CV_MAT_ELEM( *mat, float, 1, 0 ) = 4.f;

CV_MAT_ELEM( *mat, float, 1, 1 ) = 5.f;

CV_MAT_ELEM( *mat, float, 1, 2 ) = 6.f;

CV_MAT_ELEM( *mat, float, 2, 0 ) = 7.f;

CV_MAT_ELEM( *mat, float, 2, 1 ) = 8.f;

CV_MAT_ELEM( *mat, float, 2, 2 ) = 9.f;

//获得矩阵元素的值

float element = CV_MAT_ELEM(*mat,float,2,2);

printf("%f/n",element);

}

CV_MAT_ELEM宏实际上会调用CV_MAT_ELEM_PTR(matrix,row,col)宏来完成任务。CV_MAT_ELEM_PTR()宏的参数是矩阵的指针,行,列。

CV_MAT_ELEM()宏和CV_MAT_ELEM_PTR()宏的区别是,在调用CV_MAT_ELEM时,指向矩阵元素的指针的数据类型已经依据输入参数中的元素类型而

做了强制转换。,以下是使用CV_MAT_ELEM_PTR()来设置元素的值:

#pragma comment( lib, "cxcore.lib" )

#include "cv.h"

#include <stdio.h>

void main()

{

CvMat* mat = cvCreateMat(3,3,CV_32FC1);

cvZero(mat);//将矩阵置0

float element_0_2 = 7.7f;

*((float*)CV_MAT_ELEM_PTR( *mat, 0, 2 ) ) = element_0_2;

//获得矩阵元素的值

float element = CV_MAT_ELEM(*mat,float,0,2);

printf("%f/n",element);

}

以上使用矩阵中元素的方式很方便,但不幸的是,该宏在每次调用时,都会重新计算指针的位置。这意味着,先查找矩阵数据区中第0个元素的位置,然后,根据参数中的行和列,计算所需要的元素的地址偏移量,然后将地址偏移量与第0个元素的地址相加,获得所需要的元素的地址。

所以,以上的方式虽然很容易使用,但是却不是获得矩阵元素的最好方式。特别是当你要顺序遍历整个矩阵中所有元素时,这种每次对地址的重复计算就更加显得不合理。

困难的方式

以上两个宏只适合获得一维或二维的矩阵(数组)元素,OpenCV提供了处理多维矩阵(数组)的方式。实际上你可以不受限制地使用N维。

当访问这样一种N维矩阵中元素时,你需要使用一个系列的函数,叫做cvPtr*D,*代表1,2,3,4....,例如,cvPtr1D(),cvPtr2D(),cvPtr3D(),以及cvPtrND().以下为此系列函数的定义:

cvPtr*D函数用于返回指向某数组元素的指针

uchar* cvPtr1D( const CvArr* arr, int idx0, int* type=NULL );

uchar* cvPtr2D( const CvArr* arr, int idx0, int idx1, int* type=NULL );

uchar* cvPtr3D( const CvArr* arr, int idx0, int idx1, int idx2, int* type=NULL );

uchar* cvPtrND( const CvArr* arr, int* idx, int* type=NULL, int create_node=1, unsigned* precalc_hashval=NULL );

arr

输入数组(矩阵).

idx0

元素下标的第一个以0为基准的成员

idx1

元素下标的第二个以0为基准的成员

idx2

元素下标的第三个以0为基准的成员

idx

数组元素下标

type

可选的,表示输出参数的数据类型

create_node

可选的,为稀疏矩阵输入的参数。如果这个参数非零就意味着被需要的元素如果不存在就会被创建。

precalc_hashval

可选的,为稀疏矩阵设置的输入参数。如果这个指针非NULL,函数不会重新计算节点的HASH值,而是从指定位置获取。这种方法有利于提高智能组合数据的操作

函数cvPtr*D 返回指向特殊数组元素的指针。数组维数应该与传递给函数的下标数相匹配,它可以被用于顺序存取的1D,2D或nD密集数组

函数也可以用于稀疏数组,并且如果被需要的节点不存在函数可以创建这个节点并设置为0

就像其它获取数组元素的函数 (cvGet[Real]*D, cvSet[Real]*D)如果元素的下标超出了范围就会产生错误

很明显,如果是一维数组(矩阵),那就可以使用cvPtr1D,用参数idx0来指向要获得的第idx0个元素,返回值为指向该元素的指针,如果是二维数组(矩阵),就可以使用cvPtr2D,用idx0,idx1来指向相应的元素。

如果是N维数组,则int*
idx参数指向对N维数组中某元素定位用的下标序列。

#pragma comment( lib, "cxcore.lib" )

#include "cv.h"

#include <stdio.h>

void main()

{

CvMat* mat = cvCreateMat(3,3,CV_32FC1);

cvZero(mat);//将矩阵置0

//为矩阵元素赋值

CV_MAT_ELEM( *mat, float, 0, 0 ) = 1.f;

CV_MAT_ELEM( *mat, float, 0, 1 ) = 2.f;

CV_MAT_ELEM( *mat, float, 0, 2 ) = 3.f;

CV_MAT_ELEM( *mat, float, 1, 0 ) = 4.f;

CV_MAT_ELEM( *mat, float, 1, 1 ) = 5.f;

CV_MAT_ELEM( *mat, float, 1, 2 ) = 6.f;

CV_MAT_ELEM( *mat, float, 2, 0 ) = 7.f;

CV_MAT_ELEM( *mat, float, 2, 1 ) = 8.f;

CV_MAT_ELEM( *mat, float, 2, 2 ) = 9.f;

//获得矩阵元素(0,2)的值

float *p = (float*)cvPtr2D(mat, 0, 2);

printf("%f/n",*p);

}

我们使用cvPtr*D()函数的一个理由是,通过此函数,我们可以用指针指向矩阵中的某元素,并使用指针运算符,来设置该元素的值,或者,用指针运算来移动指针,指向从起始位置开始的矩阵中的其他元素。例如,我们可以用以下方式遍历矩阵中的元素:

#pragma comment( lib, "cxcore.lib" )

#include "cv.h"

#include <stdio.h>

void main()

{

CvMat* mat = cvCreateMat(3,3,CV_32FC1);

cvZero(mat);//将矩阵置0

//获得矩阵元素(0,0)的指针

float *p = (float*)cvPtr2D(mat, 0, 0);

//为矩阵赋值

for(int i = 0; i < 9; i++)

{

*p = (float)i;

p++;

}

//打印矩阵的值

p = (float*)cvPtr2D(mat, 0, 0);

for(i = 0; i < 9; i++)

{

printf("%f/t",*p);

p++;

if((i+1) % 3 == 0)

printf("/n");

}

}

但是要注意,以上为矩阵中元素的通道数为1时,可以用p++来访问下一个矩阵中元素,但是如果通道数不为1,例如一个三通道的二维矩阵,矩阵中每个元素的值为RGB值,则矩阵中数据按以下方式存储:rgbrgbrgb......,因此,使用指针指向下一个元素时,就需要加上相应的通道数。

举例如下:

#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");

}

}

如果你不想使用指向数据的指针,而只是想获得矩阵中的数据,你还可以使用cvGet*D函数系列。如下所示,该函数系列以返回值类型划分有两种,一种返回double类型数据,另一种返回CvScalar类型数据。

Get*D返回特殊的数组元素

CvScalar cvGet1D( const CvArr* arr, int idx0 );

CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );

CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );

CvScalar cvGetND( const CvArr* arr, int* idx );

arr输入数组.

idx0元素下标第一个以0为基准的成员

idx1元素下标第二个以0为基准的成员

idx2元素下标第三个以0为基准的成员

idx元素下标数组

函数cvGet*D 返回指定的数组元素。对于稀疏数组如果需要的节点不存在函数返回0 (不会创建新的节点)

GetReal*D返回单通道数组的指定元素

double cvGetReal1D( const CvArr* arr, int idx0 );

double cvGetReal2D( const CvArr* arr, int idx0, int idx1 );

double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 );

double cvGetRealND( const CvArr* arr, int* idx );

arr输入数组,必须是单通道.

idx0元素下标的第一个成员,以0为基准

idx1元素下标的第二个成员,以0为基准

idx2元素下标的第三个成员,以0为基准

idx

元素下标数组

函数cvGetReal*D 返回单通道数组的指定元素,如果数组是多通道的,就会产生运行时错误,而 cvGet*D 函数可以安全的被用于单通道和多通道数组,注意,该方法返回值类型是double类型的,这意味着,矩阵中如果保存的是int类型数据,不能用此系列方法。

#pragma comment(lib,"cxcore.lib")

#include"cv.h"

#include<stdio.h>

void main()

{

//矩阵元素为1通道浮点型数据

CvMat*mat=cvCreateMat(3,3,CV_32FC1 );

cvZero(mat);//将矩阵置0

//为矩阵元素赋值

//获得矩阵元素(0,0)的指针

float *p=(float*)cvPtr2D(mat,0,0);

//为矩阵赋值

for(int i=0;i<9;i++)

{

//为每个通道赋值

*p=(float)i*10;

p++;

}

for(i=0;i<3;i++)

for(int j=0;j<3;j++)

{

printf("%lf/t",cvGetReal2D(mat,i,j));

}

}

另外,我们还有类似于cvGet*D()的方法为矩阵元素赋值:cvSetReal*D()和cvSet*D()。

Set*D

修改指定的数组

void cvSet1D( CvArr* arr, int idx0, CvScalar value );

void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );

void cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value );

void cvSetND( CvArr* arr, int* idx, CvScalar value );

arr

输入数组

idx0

元素下标的第一个成员,以0为基点

idx1

元素下标的第二个成员,以0为基点

idx2

元素下标的第三个成员,以0为基点

idx

元素下标数组

value

指派的值

函数 cvSet*D 指定新的值给指定的数组元素。对于稀疏矩阵如果指定节点不存在函数创建新的节点

SetReal*D

修改指定数组元素值

void cvSetReal1D( CvArr* arr, int idx0, double value );

void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );

void cvSetReal3D( CvArr* arr, int idx0, int idx1, int idx2, double value );

void cvSetRealND( CvArr* arr, int* idx, double value );

arr

输入数组.

idx0

元素下标的第一个成员,以0为基点

idx1

元素下标的第二个成员,以0为基点

idx2

元素下标的第三个成员,以0为基点

idx

元素下标数组

value

指派的值

函数 cvSetReal*D 分配新的值给单通道数组的指定元素,如果数组是多通道就会产生运行时错误。然而cvSet*D 可以安全的被用于多通道和单通道数组。

对于稀疏数组如果指定的节点不存在函数会创建该节点。

以下是一个例子:

#pragma comment(lib,"cxcore.lib")

#include"cv.h"

#include<stdio.h>

void main()

{

//矩阵元素为三通道8位浮点数

CvMat *mat=cvCreateMat(3,3,CV_32FC3 );

cvZero(mat);//将矩阵置0

//为矩阵元素赋值

for(int i = 0; i < 3; i++)

for(int j = 0; j < 3; j++)

cvSet2D( mat, i, j, cvScalar(i*10,j*10,i*j*10) );

for(i=0;i<3;i++)

for(int j=0;j<3;j++)

{

printf("%lf,%lf,%lf/t",cvGet2D( mat, i, j ).val[0],cvGet2D( mat, i, j ).val[1],cvGet2D( mat, i, j ).val[2]);

}

}

为了方便起见,OpenCV还定义了两个函数:cvmSet()和cvmGet(),这两个函数用于单通道浮点型元素矩阵的存取。

例如,cvmSet(mat,2,2,0.5);就类似于cvSetReal2D(mat,2,2,0.5);

返回单通道浮点矩阵指定元素

double cvmGet( const CvMat* mat, int row, int col );

为单通道浮点矩阵的指定元素赋值。

void cvmSet( CvMat* mat, int row, int col, double value );
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: