现在的位置: 首页 > 综合 > 正文

【OpenCV学习】矩阵操作总结

2013年01月03日 ⁄ 综合 ⁄ 共 8389字 ⁄ 字号 评论关闭

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

 

抱歉!评论已关闭.