Opencv step by step - 基本数据类型
CvArr,CvMat,IplImage这三者是继承的关系。
打开opencv 3.0的源码:
cvArr
/* CvArr* is used to pass arbitrary
* array-like data structures
* into functions where the particular
* array type is recognized at runtime:
*/
typedef void CvArr;
oh god,它是可变类型的数据
cvMat
再看一下cvMat。
typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr_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 #ifdef __cplusplus CvMat() {} CvMat(const CvMat& m) { memcpy(this, &m, sizeof(CvMat));} CvMat(const cv::Mat& m); #endif } CvMat;
这里先不管C++了,纯c的做法是:
typedef struct CvMat { int type; int step; /* for internal use only */ int* refcount; int hdr_refcount; union { uchar* ptr; short* s; int* i; float* fl; double* db; } data; int rows; int cols; } CvMat;
这里,cvMat有几个属性,到底是干什么的呢?
可以看看它的初始化函数:
CV_INLINE CvMat cvMat( int rows, int cols, int type, void* data CV_DEFAULT(NULL)) { CvMat m; assert( (unsigned)CV_MAT_DEPTH(type) <= CV_64F ); type = CV_MAT_TYPE(type); m.type = CV_MAT_MAGIC_VAL | CV_MAT_CONT_FLAG | type; m.cols = cols; m.rows = rows; m.step = m.cols*CV_ELEM_SIZE(type); m.data.ptr = (uchar*)data; m.refcount = NULL; m.hdr_refcount = 0; return m; }
看了之后恍然大悟:
type是矩阵中每一个数据点的类型。
cols是矩阵的列数。
rows当然是行数。
step是每一行的数据大小(应该是byte数吧)。
data是一个union的类型,里面是不同类型的指针,这样做的目的应该是可以接收 不同的数据类型吧。
比如你传了一个float*的指针进去,他就是float*了,反正这个联合体的大小就是sizeof(pointer) = 4了
internal use的先不管了。
在其他版本中把rows和height写在union里面的做法应该是做变量的兼容性吧。
IplImage
下面来重点看一下这个结构体:
typedef struct #ifdef __cplusplus CV_EXPORTS #endif _IplImage { int nSize; /* sizeof(IplImage) */ int ID; /* version (=0)*/ int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */ int alphaChannel; /* Ignored by OpenCV */ int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */ char colorModel[4]; /* Ignored by OpenCV */ char channelSeq[4]; /* ditto */ int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels. cvCreateImage can only create interleaved images */ int origin; /* 0 - top-left origin, 1 - bottom-left origin (Windows bitmaps style). */ int align; /* Alignment of image rows (4 or 8). OpenCV ignores it and uses widthStep instead. */ int width; /* Image width in pixels. */ int height; /* Image height in pixels. */ struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */ struct _IplImage *maskROI; /* Must be NULL. */ void *imageId; /* " " */ struct _IplTileInfo *tileInfo; /* " " */ int imageSize; /* Image data size in bytes (==image->height*image->widthStep in case of interleaved data)*/ char *imageData; /* Pointer to aligned image data. */ int widthStep; /* Size of aligned image row in bytes. */ int BorderMode[4]; /* Ignored by OpenCV. */ int BorderConst[4]; /* Ditto. */ char *imageDataOrigin; /* Pointer to very origin of image data (not necessarily aligned) - needed for correct deallocation */ #ifdef __cplusplus _IplImage() {} _IplImage(const cv::Mat& m); #endif } IplImage;
可以看出来,这个结构体和cvMat很像。
下面通过实际操作来understand一下这个结构体:
#include <cv.h> #include <highgui.h> #define u8 unsigned char void image_operation(IplImage *image) { printf("nchannels:%d\n",image->nChannels); printf("depth:%d\n",image->depth); /* ./modules/core/include/opencv2/core/types_c.h: * #define IPL_DATA_ORDER_PIXEL 0 */ printf("dataOrder:%d\n",image->dataOrder); printf("width:%d\n",image->width); printf("height:%d\n",image->height); printf("origin:%d\n",image->origin); printf("widthStep:%d\n",image->widthStep); for (int y = 0; y<image->height ; y++) { u8* row = (u8*)(image->imageData + y*image->widthStep); for(int x = 0; x<image->width; x++) { row[3*x + 1] = 0; row[3*x + 2] = 0; } } } int main(int argc,char **argv) { printf("this is image transforming plus\n"); IplImage *image; image = cvLoadImage(argv[1]); if(argc != 2) { std::cout << "No image data\n"; return -1; } cvNamedWindow("image input"); cvNamedWindow("image output"); cvShowImage("image input", image); image_operation(image); cvShowImage("image output", image); cvReleaseImage(&image); cvWaitKey(0); cvDestroyWindow("image input"); cvDestroyWindow("image output"); return 0; }
这里读取一个图像,并且将两个通道变成0,结果如下:
tan@ubuntu:~/cv$ ./TransformImage image/1.jpg this is image transforming plus nchannels:3 depth:8 dataOrder:0 width:400 height:300 origin:0 widthStep:1200
从结果来看,这里把蓝色通道提取了,再次验证一下就可以发现opencv的存储是 BGR 顺序存储的。
重要的一个变量是image->widthStep,因为opencv会把图像数据4字节对齐,每过一行需要加上这个值而不是自己进行处理。