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

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 返回指向特殊数组元素的指针。数组维数应该与传递给函数的下标数相匹配,它可以被用于顺序存取的1D2DnD密集数组
函数也可以用于稀疏数组,并且如果被需要的节点不存在函数可以创建这个节点并设置为0
就像其它获取数组元素的函数 (cvGet[Real]*D, cvSet[Real]*D)如果元素的下标超出了范围就会产生错误



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

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OpenCV ,可以使用 `cv::Mat` 类来表示矩阵,并且可以使用 `cv::Mat` 类的成员函数 `cv::Mat::at()` 来访问矩阵的元素。要对 `cv::Point2f` 类型的数据进行平移,可以将其转换为一个 `cv::Mat` 类型的矩阵,然后使用矩阵加法来实现平移。 以下是一个简单的示例代码,演示如何使用矩阵进行 `cv::Point2f` 类型数据的平移: ```cpp #include <opencv2/opencv.hpp> #include <iostream> int main() { // 创建一个 2x1 的矩阵,表示平移向量 cv::Mat trans = (cv::Mat_<float>(2, 1) << 50, 50); // 创建一个 Point2f 类型的点 cv::Point2f p(100, 100); // 将 Point2f 类型的点转换成 2x1 的矩阵 cv::Mat p_mat = (cv::Mat_<float>(2, 1) << p.x, p.y); // 对矩阵进行平移 cv::Mat p_trans = p_mat + trans; // 将平移后的矩阵转换成 Point2f 类型的点 cv::Point2f p_trans_point(p_trans.at<float>(0), p_trans.at<float>(1)); // 输出平移前后的点坐标 std::cout << "Original point: (" << p.x << ", " << p.y << ")" << std::endl; std::cout << "Translated point: (" << p_trans_point.x << ", " << p_trans_point.y << ")" << std::endl; return 0; } ``` 在上面的代码,我们首先创建了一个 `cv::Mat` 类型的矩阵 `trans`,表示平移向量。然后创建了一个 `cv::Point2f` 类型的点 `p`,并将其转换为一个 2x1 的矩阵 `p_mat`。接着使用矩阵加法对 `p_mat` 进行平移,得到平移后的矩阵 `p_trans`,最后再将其转换回 `cv::Point2f` 类型的点 `p_trans_point`。最终输出平移前后的点坐标。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值