OpenCV常用的图像IplImage和矩阵CvMat操作总结

1)结构体IplImage

OpenCv中图像的结构体为IplImage,位于头文件cxcore.h中,IplImage结构体的定义如下:


typedef struct _IplImage

  {

  int nSize; /* IplImage大小 */

  int ID; /* 版本 (=0)*/

  int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */

  int alphaChannel; /* 被OpenCV忽略 */

  int depth; /* 像素的位深度,主要有以下支持格式:

 IPL_DEPTH_8U, IPL_DEPTH_8S,IPL_DEPTH_16U,IPL_DEPTH_16S, IPL_DEPTH_32S,

   IPL_DEPTH_32F 和IPL_DEPTH_64F */

  char colorModel[4]; /* 被OpenCV忽略 */

  char channelSeq[4]; /* 同上 */

  int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.

   只有cvCreateImage可以创建交叉存取图像 */

  int origin; /*图像原点位置: 0表示顶-左结构,1表示底-左结构 */

  int align; /* 图像行排列方式 (4or 8),在 OpenCV 被忽略,

使用 widthStep 代替 */

  int width; /* 图像宽像素数 */

  int height;/* 图像高像素数*/

  struct _IplROI *roi;/* 图像感兴趣区域,当该值非空时,

   只对该区域进行处理 */

  struct _IplImage *maskROI; /* 在 OpenCV中必须为NULL */

  void *imageId; /* 同上*/

  struct _IplTileInfo *tileInfo; /*同上*/

  int imageSize; /* 图像数据大小(在交叉存取格式下ImageSize=

image->height*image->widthStep),单位字节*/

  char *imageData; /* 指向排列的图像数据 */

  int widthStep; /* 排列的图像行大小,以字节为单位 */

  int BorderMode[4];/* 边际结束模式, 在 OpenCV 被忽略*/

  int BorderConst[4]; /* 同上 */

  char *imageDataOrigin; /* 指针指向一个不同的图像数据结构

(不是必须排列的),是为了纠正图像内存分配准备的 */

  } IplImage;

主要的成员变量有。

nChannels : 图像的通道数目,即灰度图像:nChannels= 1; RGB图像nChannels= 3

depth:每个像素值的数据类型和所占的存储空间

origin变量可以有两种取值:IPL_ORIGIN_TL 或者 IPL_ORIGIN_BL,分别设置坐标原点的位置于图像的左上角或者左下角。在计算机视觉领域,一个重要的错误来源就是原点位置的定义不统一。具体而言,图像的来源、操作系统、编解码器和存储格式等因素都可以影响图像坐标原点的选取。举例来说,你或许认为自己正在从图像上面的脸部附近取样,但实际上却在图像下方的裙子附近取样。避免此类现象发生的最好办法是在最开始的时候检查一下系统,在所操作的图像块的地方画点东西试试。

dataOrder:   多通道的数据存储方式,dataOrder=0是交叉通道存储方式,即BGRBGRBGRBGR的方式存储;dataOrder=1是采用独立通道方式存储,即RRRRRRR。。。,GGGGGGG…,BBBBBB…,一般都是BGRBGRBGR的这种交叉存储方式,cvCreateImage生成的图像也是这种存储方式。

   width:      图像的宽度

   height:      图像的高度

   imageData:  图像的像素矩阵

    widthStep:   每一行像素所占的字节数目. 参数widthStep包括相邻行的同列点之间的字节数。仅凭变量width是不能计算这个值的,因为为了处理过程更高效每行都会用固定的字节数来对齐;因此在第i行末和第i+1行开始处可能会有些冗于字节。参数imageData包含一个指向第一行图像数据的指针。如果图像中有些独立的平面(如当dataOrder = IPL_DATA_ORDER_PLANE)那么把它们作为单独的图像连续摆放,总行数为height和nChannels的乘积。但通常情况下,它们是交错的,使得行数等于高度,而且每一行都有序地包含交错的通道。

    ROI-- 感兴趣的区域(ROI),实际上它是另一个IPL/IPP 结构IplROI的实例。IplROI包含xOffset,yOffset,height,width和coi成员变量,其中COI代表channel of interest(感兴趣的通道)。ROI的思想是: 一旦设定ROI,通常作用于整幅图像的函数便会只对ROI所表示的子图像进行操作。如果IplImage变量中设置了ROI,则所有的OpenCV函数就会使用该ROI变量。如果COI被设置成非0值,则对该图像的操作就只作用于被指定的通道上了 。不幸的是,许多OpenCV函数都忽略参数COI。


2)图像载入函数     cvLoadImage

函数cvLoadImage载入指定图像文件,并返回指向该文件的IplImage指针。函数支持bmpjpg png tiff等格式的图像。其函数原型如下:

IplImage*cvLoadImage( const char* filename, int iscolor);

其中,filename是待载入图像的名称,包括图像的扩展名;iscolor是一个辅助参数项,可选正数、零和负数三种值,正数表示作为三通道图像载入,表示该图像作为单通道图像,负数表示载入图像的通道数由图像文件自身决定。


3)窗口定义函数  cvNamedWindow

  函数cvNamedWindow定义一个窗口,用于显示图像。其函数原型如下:

  int cvNamedWindow( const char* name, unsignedlong flags );

  其中,name是窗口名,flags是窗口属性指标值,可以选择CV_WINDOW_AUTOSIZE0两种值。CV_WINDOW_AUTOSIZE表示窗口尺寸与图像原始尺寸相同,0表示以固定的窗口尺寸显示图像。


4)图像显示函数 cvShowImage

函数cvShowImage是在指定的窗口中显示图像,其函数原型如下:

  void cvShowImage( const char* name, constCvArr* image );

其中,name是窗口名称,image是图像类型指针,一般是IplImage指针。


5)图像保存函数 cvSaveImage


函数cvSaveImage以指定的文件名保存IplImage类型的指针变量,其函数原型如下:

  int cvSaveImage( const char* filename, constCvArr* image );

其中,filename是图像保存路径和名称,image是IplImage指针变量。

Trick

如果要保存一组图像到result文件夹,图像个数为n,保存名称按照一定的序号递增,假设为imgTmp0.jpg,imgTmp1.jpg,imgTmp2.jpg,imgTmp3.jpg,…, imgTmpn.jpg,则

操作为:

char* f[30];

for(inti=0; i<n; i++)

{

sprintf(f,”result/imgTmp%d.jpg”,i);//把格式化的数据写入某个字符串缓冲区f中

cvSaveImage(f,img);

}

借用sprintf函数即可以完成依次命名的功能。


6)图像销毁函数  cvReleaseImage

函数cvReleaseImage销毁已定义的IplImage指针变量,释放占用内存空间。其函数原型如下:

  void cvReleaseImage( IplImage** image );

其中,image为已定义的IplImage指针。


7)矩阵结构体 CvMat


CvMat的结构体定义为:

typedef struct CvMat
    {
        int type; /* CvMat signature (CV_MAT_MAGIC_VAL), element type and flags */
        int step; /* full row length in bytes */
        int* refcount; /* underlying data reference counter */
        union
        {
            uchar* ptr;//数据头指针
            short* s;
            int* i;   //int,pMat->data.i;
            float* fl;//float,pMat->data.fl;
            double* db;
        } data; /* data pointers */
 
    #ifdef __cplusplus
        union
        {
            int rows;
            int height;
        };
        union
        {
            int cols;
            int width;
        };
    #else
        int rows; /* number of rows */
        int cols; /* number of columns */
    #endif
 
    } CvMat;

step是每一行数据的长度,以字节来表示

data是存放矩阵数据的联合体,如果矩阵pMat是float类型的,那么获取矩阵数据指针的方式为pMat->data.fl,如果是整型的 pMat->data.i

行数和列数在c和c++中定义略有不同,但是rows和cols是通用的两个变量。

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;
}


8)分配矩阵空间   cvCreateMat


CvMat* cvCreateMat(int rows, int cols, int type);

   type: 矩阵元素类型. 格式为CV_<bit_depth>(S|U|F)C<number_of_channels>.  
   例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵.

 例:  CvMat* M = cvCreateMat(4,4,CV_32FC1);


9)逐点赋值方式初始化

CvMat* mat = cvCreateMat( 2, 2,CV_64FC1 );
cvZero( mat );
cvmSet( mat, 0, 0, 1 );
cvmSet( mat, 0, 1, 2 );
cvmSet( mat, 1, 0, 3 );
cvmSet( mat, 2, 2, 4 );
cvReleaseMat( &mat ); 

10)使用现有数组初始化

doublea[] = { 1, 2, 3, 4,
           5, 6, 7, 8,
                     9, 10, 11, 12 };
CvMat mat = cvMat( 3, 4, CV_64FC1, a ); // 64FC1 for double
// 不需要cvReleaseMat,因为数据内存分配是由double定义的数组进行的。

或者这样:

float vals[]={0.866000,0.555000,0.333000,0.222000};

CvMat rotmat;

cvInitMatHeader(&rotmat,2,2,CV_32FC1,vals);


11)释放矩阵

CvMat* M = cvCreateMat(4,4,CV_32FC1);
cvReleaseMat(&M);//矩阵指针的地址
或者 void cvReleaseMat(CvMat  * * mat);//矩阵指针的指针

12)复制矩阵

CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
CvMat* M2;
M2=cvCloneMat(M1);

13)存取矩阵元素

a,从矩阵中得到一个元素的最简单的方法是利用宏CV_MAT_ELEM()

例1,利用CV_MAT_ELEM()宏存取矩阵

1.  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );  
2.  float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 ); 

b,还有一个与此宏类似的宏,叫CV_MAT_ELEM_PTR()CV_MAT_ELEM_ PTR()传入矩阵、待返回元素的行和列号这3个参数,返回指向这个元素的指针。

例2:利用宏CV_MAT_ELEM_PTR()为矩阵设置一个数值

1.  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );  
2.  float element_3_2 = 7.7;  
3.  *( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2; 

c,例3:指针访问矩阵结构

1.  uchar* cvPtr1D(  
2.    const CvArr*  arr,  
3.    int           idx0,  
4.    int*          type = NULL 
5.  );  
6.   
7.  uchar* cvPtr2D(  
8.    const CvArr*  arr,  
9.    int           idx0,   
13.   int           idx1,  
14.   int*          type = NULL 
15. );  
16.  
17. uchar* cvPtr3D(  
18.   const CvArr*  arr,  
19.   int           idx0,  
20.   int           idx1,  
21.   int           idx2,  
22.   int*          type = NULL 
23. );  
24. uchar* cvPtrND(  
25.   const CvArr*  arr,  
26.   int*          idx,  
27.   int*          type            = NULL,  
28.   int           create_node     = 1,  
29.   unsigned*     precalc_hashval = NULL 
30. ); 


d,如果仅仅是读取数据,可用另一个函数族cvGet*D但是返回矩阵元素的实际值。


例4:CvMat和IPlImage元素函数

1.  double cvGetReal1D( const CvArr* arr, int idx0 );  
2.  double cvGetReal2D( const CvArr* arr, int idx0, int idx1 );  
3.  double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 );  
4.  double cvGetRealND( const CvArr* arr, int* idx );  
5.   
6.  CvScalar cvGet1D( const CvArr* arr, int idx0 );  
7.  CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );  
8.  CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );  
9.  CvScalar cvGetND( const CvArr* arr, int* idx ); 

e,例5:为CvMat或者IplImage元素设定值的函数

1.  void cvSetReal1D( CvArr* arr, int idx0, double value );  
2.  void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );  
3.  void cvSetReal3D(  
4.    CvArr* arr,  
5.    int idx0,  
6.    int idx1,  
7.    int idx2,  
8.    double value  
9.  );  
10. void cvSetRealND( CvArr* arr, int* idx, double value );  
11.  
12. void cvSet1D( CvArr* arr, int idx0, CvScalar value );  
13. void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );  
14. void cvSet3D(  
15.   CvArr* arr,  
16.   int idx0,  
18.   int idx1,  
19.   int idx2,  
20.   CvScalar value  
21. );  
22. void cvSetND( CvArr* arr, int* idx, CvScalar value ); 

为了方便,我们也可以使用cvmSet()和cvmGet(),这两个函数用于处理浮点型单通道矩阵,非常简单。

1.  double cvmGet( const CvMat* mat, int row, int col )  
2.  void cvmSet( CvMat* mat, int row, int col, double value ) 

以下函数调用cvmSet():

1.  cvmSet( mat, 2, 2, 0.5000 ); 

等同于cvSetReal2D函数调用:

1.  cvSetReal2D( mat, 2, 2, 0.5000 ); 

f,例6:累加一个三通道矩阵中的所有元素

1.  float sum( const CvMat* mat ) 

    { 

3.     float s = 0.0f;  

4.     for(int row=0; row<mat->rows; row++ ) 

const float* ptr=(const float*)(mat->data.ptr + row * mat->step); 

6.       for( col=0; col<mat->cols; col++ ) 

7.         s += *ptr++; 

8.    }  

10.   }  

11.   return( s );  

12. } 


g,间接存取矩阵元素

cvmSet(M,i,j,2.0); // Set M(i,j)
t = cvmGet(M,i,j); // Get M(i,j)

注意:cvmGet()和cvmSet()函数只支持CV_32FC1(float)和CV_64FC1(double)的类型


h,直接存取,使用4字节校正


CvMat* M = cvCreateMat(4,4,CV_32FC1);
int n = M->cols;
float *data = M->data.fl;//注意:是浮点型数据

data[i*n+j] = 3.0;

i,直接存取,使用任意字节


CvMat* M = cvCreateMat(4,4,CV_32FC1);
int step = M->step/sizeof(float);
float *data = M->data.fl;

(data+i*step)[j] = 3.0;

j,直接存取一个初始化的矩阵


double a[16];
CvMat Ma = cvMat(3, 4, CV_64FC1, a);
a[i*4+j] = 2.0; // Ma(i,j)=2.0;

14,矩阵/向量数学操作

a,矩阵-矩阵操作

CvMat *Ma, *Mb, *Mc;
cvAdd(Ma, Mb, Mc);            // Ma+Mb -> Mc
cvSub(Ma, Mb, Mc);            // Ma-Mb -> Mc
cvMatMul(Ma, Mb, Mc);         // Ma*Mb -> Mc


b,矩阵元素操作

CvMat *Ma, *Mb, *Mc;
cvMul(Ma, Mb, Mc);            // Ma.*Mb -> Mc
cvDiv(Ma, Mb, Mc);            // Ma./Mb -> Mc
cvAddS(Ma, cvScalar(-10.0), Mc); // Ma.-10 -> Mc

c,向量乘积

double va[] = {1, 2, 3};
double vb[] = {0, 0, 1};
double vc[3];

CvMat Va=cvMat(3, 1, CV_64FC1, va);
CvMat Vb=cvMat(3, 1, CV_64FC1, vb);
CvMat Vc=cvMat(3, 1, CV_64FC1, vc);

double res=cvDotProduct(&Va,&Vb); // 点乘:  Va . Vb -> res
cvCrossProduct(&Va, &Vb, &Vc);    // 向量积: Va x Vb -> Vc

注意 Va, Vb, Vc 在向量积中向量元素个数须相同.


d,单矩阵操作

CvMat *Ma, *Mb;
cvTranspose(Ma, Mb);   // transpose(Ma) -> Mb (不能对自身进行转置)
CvScalar t = cvTrace(Ma); // trace(Ma) -> t.val[0] 
double d = cvDet(Ma);     // det(Ma) -> d
cvInvert(Ma, Mb);         // inv(Ma) -> Mb

e,非齐次线性系统求解


CvMat* A  = cvCreateMat(3,3,CV_32FC1);
CvMat* x  = cvCreateMat(3,1,CV_32FC1);
CvMat* b  = cvCreateMat(3,1,CV_32FC1);
cvSolve(&A, &b, &x);          // solve (Ax=b) for x

f,特征值分析(对称矩阵)


CvMat* A  = cvCreateMat(3,3,CV_32FC1);
CvMat* E  = cvCreateMat(3,3,CV_32FC1);
CvMat* l  = cvCreateMat(3,1,CV_32FC1);
cvEigenVV(&A, &E, &l);        // l = A的特征值 (降序排列)
                              // E = 对应的特征向量 (每行)

g,奇异值分解

CvMat* A  = cvCreateMat(3,3,CV_32FC1);
CvMat* U  = cvCreateMat(3,3,CV_32FC1);
CvMat* D  = cvCreateMat(3,3,CV_32FC1);
CvMat* V  = cvCreateMat(3,3,CV_32FC1);
cvSVD(A, D, U, V, CV_SVD_U_T|CV_SVD_V_T); // A = U D V^T

标号使得 U 和 V 返回时被转置(若没有转置标号,则有问题不成功!!!).






你可能感兴趣的:(opencv)