zoukankan      html  css  js  c++  java
  • OpenCV之CvMat、Mat、IplImage之间相互转换实例(转)

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

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

    1.初始化矩阵: 

    方式一、逐点赋值式: 

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

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

    double a[] = { 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定义的数组进行的。 

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

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


    法1:CvMat 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是不复制影像,也就是iplImg的data共用同个记意位置,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( 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++ )
       {
         b = img->imageData[img->widthStep * row + col * 3]
         g = img->imageData[img->widthStep * row + col * 3 + 1];
         r = img->imageData[img->widthStep * row + col * 3 + 2];
       }
    }


    方式二:宏操作:

    int row, col;
    uchar b, g, r;
    for( row = 0; row < img->height; row++ )
    {
       for ( col = 0; col < img->width; col++ )
       {
         b = CV_IMAGE_ELEM( img, uchar, row, col * 3 );
         g = CV_IMAGE_ELEM( img, uchar, row, col * 3 + 1 );
         r = CV_IMAGE_ELEM( img, uchar, row, col * 3 + 2 );
       }
    }


    注:CV_IMAGE_ELEM( img, uchar, row, col * img->nChannels + ch ) 

    8.cvMat的直接操作

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

    对于CV_32FC1 (1 channel float):

    CvMat* M = cvCreateMat( 4, 4, CV_32FC1 );
    M->data.fl[ row * M->cols + col ] = (float)3.0; 

    对于CV_64FC1 (1 channel double):

    CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
    M->data.db[ row * M->cols + col ] = 3.0; 

    一般的,对于1通道的数组:

    CvMat* M = 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 7 
    
    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++)
        {
            p = 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, 0 ) = cvPoint(100,100); 
    
    CvMat* vector = cvCreateMat( 1, 3, CV_64FC4 );
    CV_MAT_ELEM( *vector, CvScalar, 0, 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( i = 0; i < mat->rows; i++ )
        {
            for( j = 0; j < mat->cols; j++ )
            {
                printf( "%f ",cvmGet( mat, i, j ) );
            }
            printf( "
    " );
        }
    } 

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

    注意:数据不能为int,因为cvGet2D得到的实质是double类型。
    举例:打印一个多通道矩阵:

    inline void cv3DoubleMatPrint( const CvMat* mat )
    {
        int i, j;
        for( i = 0; i < mat->rows; i++ )
        {
            for( j = 0; j < mat->cols; j++ )
            {
                CvScalar scal = cvGet2D( mat, i, j );
                printf( "(%f,%f,%f) ", scal.val[0], scal.val[1], scal.val[2] );
            }
            printf( "
    " );
        }
    } 

    10.修改矩阵的形状——cvReshape的操作


    经实验表明矩阵操作的进行的顺序是:首先满足通道,然后满足列,最后是满足行。
    注意:这和Matlab是不同的,Matlab是行、列、通道的顺序。
    我们在此举例如下:
    对于一通道:

    // 1 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, 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, 3 ); // 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, 3 ); // new_ch, new_rows
    cvDoubleMatPrint( mat ); // above
    //11 12 13 14
    //21 22 23 24
    //31 32 33 34
    mat = cvReshape( orig, &mathdr, 1, 2 ); // new_ch, new_rows
    cvDoubleMatPrint( mat ); // above
    //11 12 13 14 21 22
    //23 24 31 32 33 34
    mat = cvReshape( mat, &mathdr, 1, 3 ); // new_ch, new_rows
    cvDoubleMatPrint( mat ); // above
    //11 12 13 14
    //21 22 23 24
    //31 32 33 34
    mat = cvReshape( orig, &mathdr, 1, 6 ); // new_ch, new_rows
    cvDoubleMatPrint( mat ); // above
    // 11 12
    // 13 14
    // 21 22
    // 23 24
    // 31 32
    // 33 34
    mat = cvReshape( mat, &mathdr, 1, 3 ); // 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, 2 ) to get
    // 11 23
    // 12 24
    // 13 31
    // 14 32
    // 21 33
    // 22 34
    // Use cvTranspose again when to recover

    对于三通道:

    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, 1 ); // 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, 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, 4 ); // 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, 3 );
    IplImage *img2 = cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, 3 );
    CvMat *dist = cvCreateMat( h, w, CV_64FC1 );


    比较笨的思路是:
    代码如下:

    cvSplit->cvSub->cvMul->cvAdd
    IplImage *img1B = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *img1G = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *img1R = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *img2B = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *img2G = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *img2R = cvCreateImage( cvGetSize(img1), img1->depth, 1 ); IplImage *diff = cvCreateImage( cvGetSize(img1), IPL_DEPTH_64F, 1 ); 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 D = img1->nChannels; // D: Number of colors (dimension)
    int N = img1->width * img1->height; // N: number of pixels
    CvMat mat1hdr, *mat1 = cvReshape( img1, &mat1hdr, 1, N ); // N x D(colors)
    CvMat mat2hdr, *mat2 = cvReshape( img2, &mat2hdr, 1, N ); // N x D(colors)
    CvMat diffhdr, *diff = cvCreateMat( N, D, CV_64FC1 ); // N x D, temporal buff
    cvSub( mat1, mat2, diff );
    cvMul( diff, diff, diff );
    dist = cvReshape( dist, &disthdr, 1, N ); // nRow x nCol to N x 1
    cvReduce( diff, dist, 1, CV_REDUCE_SUM ); // N x D to N x 1
    dist = cvReshape( dist, &disthdr, 1, img1->height ); // Restore N x 1 to nRow x nCol
    cvReleaseMat( &diff ); 
    
    #pragma comment( lib, "cxcore.lib" )
    #include "cv.h"
    #include <stdio.h>
    int 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
    ",*p);
        return 0;
    }
  • 相关阅读:
    88. Merge Sorted Array
    87. Scramble String
    86. Partition List
    85. Maximal Rectangle
    84. Largest Rectangle in Histogram
    83. Remove Duplicates from Sorted List
    82. Remove Duplicates from Sorted List II
    81. Search in Rotated Sorted Array II
    80. Remove Duplicates from Sorted Array II
    计算几何——点线关系(叉积)poj2318
  • 原文地址:https://www.cnblogs.com/hyb1/p/3929206.html
Copyright © 2011-2022 走看看