CvMat、Mat、IplImage之间的转换详解及实例

来源:互联网 发布:现货模拟软件 编辑:程序博客网 时间:2024/05/13 00:12

OpenCV学习之CvMat的用法详解及实例

    CvMat是OpenCV比较基础的函数。初学者应该掌握并熟练应用。但是我认为计算机专业学习的方法是,不断的总结并且提炼,同时还要做大量的实践,如编码,才能记忆深刻,体会深刻,从而引导自己想更高层次迈进。

1.初始化矩阵: 

方式一、逐点赋值式: 

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

方式二、连接现有数组式: 

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

2.IplImage <----->cvMat的转换 

A.CvMat-> IplImage

IplImage* img cvCreateImage(cvGetSize(mat),8,1);
cvGetImage(matI,img);

cvSaveImage("rice1.bmp",img);

B.IplImage -> CvMat

IplImage* img cvLoadimage("leda.jpg",1);

法2CvMat *mat cvCreateMat( img->height, img->width, CV_64FC3 );
     cvConvert( img, mat );


1CvMat mathdr;

          CvMat  *mat cvGetMat( img, &mathdr );

3.IplImage <--->Mat的转换 

(1)将IplImage----- > Mat类型

Mat::Mat(const IplImage* img, bool copyData=false);

默认情况下,新的Mat类型与原来的IplImage类型共享图像数据,转换只是创建一个Mat矩阵头。当将参数copyData设为true后,就会复制整个图像数据。

例:

IplImage*iplImg = cvLoadImage("greatwave.jpg", 1);

Matmtx(iplImg); // IplImage* ->Mat 共享数据

// or : Mat mtx = iplImg; 或者是:Mat mtx(iplImg,0); // 0是不复制影像,也就是iplImgdata共用同个记意位置,header各自有

(2)将Mat类型转换-----> IplImage类型

同样只是创建图像头,而没有复制数据。

例:

IplImage ipl_img = img; // Mat -> IplImage

 

IplImage*-> BYTE*

BYTE* data= img->imageData;

4.CvMat<--->Mat的转换

(1)将CvMat类型转换为Mat类型

B.CvMat->Mat

与IplImage的转换类似,可以选择是否复制数据。

CvMat*m= cvCreatMat(int rows ,int cols int type);

Mat::Mat(const CvMat* m, bool copyData=false);

在openCV中,没有向量(vector)的数据结构。任何时候,但我们要表示向量时,用矩阵数据表示即可。

但是,CvMat类型与我们在线性代数课程上学的向量概念相比,更抽象,比如CvMat的元素数据类型并不仅限于基础数据类型,比如,下面创建一个二维数据矩阵:

              CvMat*m= cvCreatMat(int rows ,int cols int type);

这里的type可以是任意的预定义数据类型,比如RGB或者别的多通道数据。这样我们便可以在一个CvMat矩阵上表示丰富多彩的图像了。

(2)将Mat类型转换为CvMat类型

与IplImage的转换类似,不复制数据,只创建矩阵头。

例:

// 假设Mat类型的imgMat图像数据存在

CvMat cvMat = imgMat; // Mat -> CvMat

5.cv::Mat--->const cvArr*

cvArr * 数组的指针。就是opencv里面的一种类型。

Mat img;
const CvArr* s=(CvArr*)&img;
上面就可以了,CvArr是Mat的虚基类,所有直接强制转换就可以了

void cvResize( const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR ); // src 就是之前的lplimage类型的一个指针变量
6.cvArr(IplImage或者cvMat)转化为cvMat
方式一、cvGetMat方式:
int coi 0;
cvMat *mat (CvMat*)arr;
if( !CV_IS_MAT(mat) )
{
    mat cvGetMat( mat, &matstub, &coi );
    if (coi != 0) reutn; // CV_ERROR_FROM_CODE(CV_BadCOI);
}
写成函数为:
// This is just an example of function
// to support both IplImage and cvMat as an input
CVAPI( void cvIamArr( const CvArr* arr )
{
    CV_FUNCNAME( "cvIamArr" );
    __BEGIN__;
    CV_ASSERT( mat == NULL );
    CvMat matstub, *mat (CvMat*)arr;
    int coi 0;
    if( !CV_IS_MAT(mat) )
    {
        CV_CALL( mat cvGetMat( mat, &matstub, &coi );
        if (coi != 0) CV_ERROR_FROM_CODE(CV_BadCOI);
    }
    // Process as cvMat
    __END__;

7.图像直接操作
方式一:直接数组操作 int col, row, z;
uchar b, g, r;
for( row 0; row img->height; y++ )
{
   for col 0; col img->width; col++ )
   {
     img->imageData[img->widthStep row col 3]
     img->imageData[img->widthStep row col 1];
     img->imageData[img->widthStep row col 2];
   }
}
方式二:宏操作:
int row, col;
uchar b, g, r;
for( row 0; row img->height; row++ )
{
   for col 0; col img->width; col++ )
   {
     CV_IMAGE_ELEM
img, uchar, row, col );
     CV_IMAGE_ELEM( img, uchar, row, col );
     CV_IMAGE_ELEM( img, uchar, row, col );
   }
}
注:CV_IMAGE_ELEM( img, uchar, row, col img->nChannels ch 

8.cvMat的直接操作
数组的直接操作比较郁闷,这是由于其决定于数组的数据类型。 

对于CV_32FC1 (1 channel float):
CvMat* cvCreateMat( 4, 4, CV_32FC1 );
M->data.fl[ row M->cols col (float)3.0; 

对于CV_64FC1 (1 channel double):
CvMat* cvCreateMat( 4, 4, CV_64FC1 );
M->data.db[ row M->cols col 3.0; 

一般的,对于1通道的数组:
CvMat* cvCreateMat( 4, 4, CV_64FC1 );
CV_MAT_ELEM( *M, double, row, col 3.0;
注意double要根据数组的数据类型来传入,这个宏对多通道无能为力。 

对于多通道:
看看这个宏的定义:#define CV_MAT_ELEM_CN( mat, elemtype, row, col \
    (*(elemtype*)((mat).data.ptr (size_t)(mat).step*(row) sizeof(elemtype)*(col)))
if( CV_MAT_DEPTH(M->type) == CV_32F )
    CV_MAT_ELEM_CN( *M, float, row, col CV_MAT_CN(M->type) ch 3.0;
if( CV_MAT_DEPTH(M->type) == CV_64F )
    CV_MAT_ELEM_CN( *M, double, row, col CV_MAT_CN(M->type) ch 3.0;
更优化的方法是:
   #define CV_8U   0
   #define CV_8S   1
   #define CV_16U 2
   #define CV_16S 3
   #define CV_32S 4
   #define CV_32F 5
   #define CV_64F 6
   #define CV_USRTYPE1 

int elem_size CV_ELEM_SIZE( mat->type );
for( col start_col; col end_col; col++ {
    for( row 0; row mat->rows; row++ {
        for( elem 0; elem elem_size; elem++ {
            (mat->data.ptr ((size_t)mat->step row) (elem_size col))[elem] =
                (submat->data.ptr ((size_t)submat->step row) (elem_size (col start_col)))[elem];
        }
    }

对于多通道的数组,以下操作是推荐的:
for(row=0; row< mat->rows; row++)
    {
        mat->data.fl row (mat->step/4);
        for(col 0; col mat->cols; col++)
        {
            *p (float) row+col;
            *(p+1) (float) row+col+1;
            *(p+2) =(float) row+col+2;
            p+=3;
        }
    }
对于两通道和四通道而言:
CvMat* vector cvCreateMat( 1, 3, CV_32SC2 );
CV_MAT_ELEM( *vector, CvPoint, 0, cvPoint(100,100); 

CvMat* vector cvCreateMat( 1, 3, CV_64FC4 );
CV_MAT_ELEM( *vector, CvScalar, 0, cvScalar(0,0,0,0); 

9.间接访问cvMat
cvmGet/Set是访问CV_32FC1 和 CV_64FC1型数组的最简便的方式,其访问速度和直接访问几乎相同
cvmSet( mat, row, col, value );
cvmGet( mat, row, col );
举例:打印一个数组
inline void cvDoubleMatPrint( const CvMat* mat )
{
    int i, j;
    for( 0; mat->rows; i++ )
    {
        for( 0; mat->cols; j++ )
        {
            printf( "%f ",cvmGet( mat, i, );
        }
        printf( "\n" );
    }

而对于其他的,比如是多通道的后者是其他数据类型的,cvGet/Set2D是个不错的选择
CvScalar scalar cvGet2D( mat, row, col );
cvSet2D( mat, row, col, cvScalar( r, g, ); 

注意:数据不能为int,因为cvGet2D得到的实质是double类型。
举例:打印一个多通道矩阵:
inline void cv3DoubleMatPrint( const CvMat* mat )
{
    int i, j;
    for( 0; mat->rows; i++ )
    {
        for( 0; mat->cols; j++ )
        {
            CvScalar scal cvGet2D( mat, i, );
            printf( "(%f,%f,%f) ", scal.val[0], scal.val[1], scal.val[2] );
        }
        printf( "\n" );
    }

10.修改矩阵的形状——cvReshape的操作
经实验表明矩阵操作的进行的顺序是:首先满足通道,然后满足列,最后是满足行。
注意:这和Matlab是不同的,Matlab是行、列、通道的顺序。
我们在此举例如下:
对于一通道:
// channel
CvMat *mat, mathdr;
double data[] 11, 12, 13, 14,
                   21, 22, 23, 24,
                   31, 32, 33, 34 };
CvMat* orig &cvMat( 3, 4, CV_64FC1, data );
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat cvReshape( orig, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12 13 14 21 22 23 24 31 32 33 34
mat cvReshape( mat, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat cvReshape( orig, &mathdr, 1, 12 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11
// 12
// 13
// 14
// 21
// 22
// 23
// 24
// 31
// 32
// 33
// 34
mat cvReshape( mat, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat cvReshape( orig, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14 21 22
//23 24 31 32 33 34
mat cvReshape( mat, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat cvReshape( orig, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12
// 13 14
// 21 22
// 23 24
// 31 32
// 33 34
mat cvReshape( mat, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
// Use cvTranspose and cvReshape( mat, &mathdr, 1, to get
// 11 23
// 12 24
// 13 31
// 14 32
// 21 33
// 22 34
// Use cvTranspose again when to recover

对于三通道
// channels
CvMat mathdr, *mat;
double data[] 111, 112, 113, 121, 122, 123,211, 212, 213, 221, 222, 223 };
CvMat* orig &cvMat( 2, 2, CV_64FC3, data );
//(111,112,113) (121,122,123)
//(211,212,213) (221,222,223)
mat cvReshape( orig, &mathdr, 3, ); // new_ch, new_rows
cv3DoubleMatPrint( mat ); // above
// (111,112,113) (121,122,123) (211,212,213) (221,222,223)
// concatinate in column first order
mat cvReshape( orig, &mathdr, 1, );// new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 111 112 113 121 122 123 211 212 213 221 222 223
// concatinate in channel first, column second, row third
mat cvReshape( orig, &mathdr, 1, 3); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113 121
//122 123 211 212
//213 221 222 223
// channel first, column second, row third
mat cvReshape( orig, &mathdr, 1, ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113
//121 122 123
//211 212 213
//221 222 223
// channel first, column second, row third
// memorize this transform because this is useful to
// add (or do something) color channels
CvMat* mat2 cvCreateMat( mat->cols, mat->rows, mat->type );
cvTranspose( mat, mat2 );
cvDoubleMatPrint( mat2 ); // above
//111 121 211 221
//112 122 212 222
//113 123 213 223
cvReleaseMat( &mat2 ); 

11.计算色彩距离
我们要计算img1,img2的每个像素的距离,用dist表示,定义如下
IplImage *img1 cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, );
IplImage *img2 cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, );
CvMat *dist cvCreateMat( h, w, CV_64FC1 );
比较笨的思路是:cvSplit->cvSub->cvMul->cvAdd
代码如下:
IplImage *img1B cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img1G cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img1R cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2B cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2G cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2R cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *diff    cvCreateImage( cvGetSize(img1), IPL_DEPTH_64F, );
cvSplit( img1, img1B, img1G, img1R );
cvSplit( img2, img2B, img2G, img2R );
cvSub( img1B, img2B, diff );
cvMul( diff, diff, dist );
cvSub( img1G, img2G, diff );
cvMul( diff, diff, diff);
cvAdd( diff, dist, dist );
cvSub( img1R, img2R, diff );
cvMul( diff, diff, diff );
cvAdd( diff, dist, dist );
cvReleaseImage( &img1B );
cvReleaseImage( &img1G );
cvReleaseImage( &img1R );
cvReleaseImage( &img2B );
cvReleaseImage( &img2G );
cvReleaseImage( &img2R );
cvReleaseImage( &diff ); 

比较聪明的思路是
int img1->nChannels; // D: Number of colors (dimension)
int img1->width img1->height; // N: number of pixels
CvMat mat1hdr, *mat1 cvReshape( img1, &mat1hdr, 1, ); // D(colors)
CvMat mat2hdr, *mat2 cvReshape( img2, &mat2hdr, 1, ); // D(colors)
CvMat diffhdr, *diff cvCreateMat( N, D, CV_64FC1 ); // D, temporal buff
cvSub( mat1, mat2, diff );
cvMul( diff, diff, diff );
dist cvReshape( dist, &disthdr, 1, ); // nRow nCol to 1
cvReduce( diff, dist, 1, CV_REDUCE_SUM ); // to 1
dist cvReshape( dist, &disthdr, 1, img1->height ); // Restore to nRow nCol
cvReleaseMat( &diff ); 

#pragma comment( lib, "cxcore.lib" )
#include "cv.h"
#include 
int main()
{
CvMat* mat cvCreateMat(3,3,CV_32FC1);
cvZero(mat);//将矩阵置0
//为矩阵元素赋值
CV_MAT_ELEM( *mat, float, 0, 1.f;
CV_MAT_ELEM( *mat, float, 0, 2.f;
CV_MAT_ELEM( *mat, float, 0, 3.f;
CV_MAT_ELEM( *mat, float, 1, 4.f;
CV_MAT_ELEM( *mat, float, 1, 5.f;
CV_MAT_ELEM( *mat, float, 1, 6.f;
CV_MAT_ELEM( *mat, float, 2, 7.f;
CV_MAT_ELEM( *mat, float, 2, 8.f;
CV_MAT_ELEM( *mat, float, 2, 9.f;
//获得矩阵元素(0,2)的值
float *p (float*)cvPtr2D(mat, 0, 2);
printf("%f\n",*p);
    return 0;
}

0 0
原创粉丝点击