• 【OpenCV学习】矩阵操作总结


    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/

    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的转换
    方式一、cvGetMat方式:
    CvMat mathdr, *mat = cvGetMat( img, &mathdr );
    方式二、cvConvert方式:
    CvMat *mat = cvCreateMat( img->height, img->width, CV_64FC3 );
    cvConvert( img, mat );
    // #define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )
    3.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__;
    }
    4.图像直接操作
    方式一:直接数组操作 int col, row, z;
    uchar b, g, r;
    for( y = 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 )
    5.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);
            /* 除以4是因为一个float占4个字节,若为double则除以8,uchar不除*/
            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);
    6.间接访问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( "/n" );
        }
    }
    而对于其他的,比如是多通道的后者是其他数据类型的,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( "/n" );
        }
    }
    7.修改矩阵的形状——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
    对于三通道
    // 3 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, 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 );
    8.计算色彩距离
    我们要计算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 );

    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/


                   作者:gnuhpc
                   出处:http://www.cnblogs.com/gnuhpc/
                   除非另有声明,本网站采用知识共享“署名 2.5 中国大陆”许可协议授权。


    分享到:

  • 相关阅读:
    Java实现 LeetCode 30 串联所有单词的子串
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 29 两数相除
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 28 实现strStr()
    Java实现 LeetCode 27 移除元素
    Java实现 LeetCode 27 移除元素
    字符编码终极笔记:ASCII、Unicode、UTF-8、UTF-16、UCS、BOM、Endian
  • 原文地址:https://www.cnblogs.com/gnuhpc/p/2802172.html
Copyright © 2020-2023  润新知