• 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;
    }
  • 相关阅读:
    双主双写、只备份某些表且要在建表ID自增
    我的系统资源呢?php-fpm你知道吗?
    apache常用的两种工作模式 prefork和worker
    Binlog的三种模式
    Tomcat
    JVM初识、调优
    httpclient 3.1跳过https请求SSL的验证
    POM报错Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.6 from
    eclipse快捷键,移动和复制一段代码
    org.codehaus.jettison.json.JSONObject使用方法
  • 原文地址:https://www.cnblogs.com/hyb1/p/3929206.html
Copyright © 2020-2023  润新知