最近在看Learning OpenCV,刚看到第三章的Accessing Data in Your Matrix就卡住了,惭愧啊。查找了很多资料终于弄明白了。
首先介绍一下cvPtr*D系列函数,它们都是用来获取CvMat矩阵中指定索引值的数据的。查了一下OpenCV安装目录下的手册,居然发现所有cvPtr*D函数的返回值都是uchar*!这就奇怪了,CvMat矩阵是可以存储多种数据结构的,怎么可能用uchar*一个类型就代表了呢?于是接着查了一下CvMat的定义,如下:
typedef struct CvMat { int type; int step; int* refcount; union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; #ifdef __cplusplus union { int rows; int height; }; union { int cols; int width; }; #else int rows; int cols; #endif } CvMat;
发现它所有的数据都定义在一个union结构中。本人一直都是搞C++的,没怎么接触过union结构,而OpenCV大部分是用c写的,没想到居然使用union,没办法,还是先学习下union到底是干什么的吧。
长话短说,经过查阅一些资料,我发现:其实union和struct很像,区别在于,union中一旦给某个成员初始化,那么只有这一个成员是合法的,可以被外部访问,而其他的成员就会被替换掉,成为未定义状态;而struct是可以给每个成员赋值而不会相互影响的。显然,OpenCV正是利用这一机制灵活的实现了对各种数据类型的支持。
在CvMat中,data的第一个成员就是uchar* ptr,那么,只要得到这个ptr,我们就得到了指向data的第一个字节的指针,接着只要使用强制类型转换即可得到指向相应类型的数据指针了。下面的程序很好的演示了这一点:
#include "stdafx.h" #include <cv.h> #include <stdio.h> int _tmain(int argc, _TCHAR* argv[]) { float arr[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,11.0,12.0}; CvMat mat1 = cvMat(4, 3, CV_32FC1, arr), mat2 = cvMat(3, 4, CV_32FC1, arr), mat3 = cvMat(4, 1, CV_32FC3, arr); //float *elem = (float*)cvPtr2D(&mat1, 1, 1); float *elem = cvPtr2D(&mat2, 1, 1); printf("elem in = %0.1f/n", *elem); for(int i = 0; i < 4; ++i) { elem = (float*)cvPtr2D(&mat3, i, 0); for(int j = 0; j < 3; ++j) printf("val at index %d in channel %d is = %0.1f/n", i, j, elem[j]); } return 0; }
同时还可以看出,对于mat3,尽管它是多通道矩阵,但仍然是一个4×1的二维矩阵,所以依然要使用cvPtr2D,但此时返回的是一个指向包含3个float类型元素的数组指针,为了得到各个通道的元素,还要对这个数组指针进行遍历。
其实,说了这么多,最主要的是要对union结构有较深刻的认识,那这个问题就迎刃而解了!^_^