本文共 13301 字,大约阅读时间需要 44 分钟。
CvMat是OpenCV比较基础的函数。初学者应该掌握并熟练应用。但是我认为计算机专业学习的方法是,不断的总结并且提炼,同时还要做大量的实践,如编码,才能记忆深刻,体会深刻,从而引导自己想更高层次迈进。
综述: OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率与OpenCV一样.
OpenCV将向量作为1维矩阵处理. 矩阵按行存储,每行有4字节的校整.分配矩阵空间: CvMat* cvCreateMat(int rows, int cols, int type);
type: 矩阵元素类型. 格式为CV_(S|U|F)C. 例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵. 例程:CvMat* M = cvCreateMat(4,4,CV_32FC1);
1
•释放矩阵空间:
1.CvMat* M = cvCreateMat(4,4,CV_32FC1);
2.cvReleaseMat(&M);1
2•复制矩阵:
1.CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
2.CvMat* M2; 3.M2=cvCloneMat(M1);1
2 3 4•初始化矩阵:
1.double a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
2.CvMat Ma=cvMat(3, 4, CV_64FC1, a);1
2 3另一种方法:
1.CvMat Ma;
2.cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a);1
2 3•初始化矩阵为单位阵:
1.CvMat* M = cvCreateMat(4,4,CV_32FC1);
2.cvSetIdentity(M); // 这里似乎有问题,不成功1
2 3 4存取矩阵元素
•假设需要存取一个2维浮点矩阵的第(i,j)个元素. •间接存取矩阵元素:1.cvmSet(M,i,j,2.0); // Set M(i,j)
2.t = cvmGet(M,i,j); // Get M(i,j)1
2•直接存取,假设使用4-字节校正:
1.CvMat* M = cvCreateMat(4,4,CV_32FC1);
2.int n = M->cols; 3.float *data = M->data.fl; 4.data[i*n+j] = 3.0;1
2 3 4•直接存取,校正字节任意:
1.CvMat* M = cvCreateMat(4,4,CV_32FC1);
2.int step = M->step/sizeof (float ); 3.float *data = M->data.fl; 4.(data+i*step)[j] = 3.0;1
2 3 4 5 6•直接存取一个初始化的矩阵元素:
1.double a[16];
2.CvMat Ma = cvMat(3, 4, CV_64FC1, a); 3.a[i*4+j] = 2.0; // Ma(i,j)=2.0;1
2 3矩阵/向量操作
•矩阵-矩阵操作:1.CvMat *Ma, *Mb, *Mc;
2.cvAdd(Ma, Mb, Mc); // Ma+Mb -> Mc 3.cvSub(Ma, Mb, Mc); // Ma-Mb -> Mc 4.cvMatMul(Ma, Mb, Mc); // Ma*Mb -> Mc1
2 3 4 5•按元素的矩阵操作:
1.CvMat *Ma, *Mb, *Mc;
2.cvMul(Ma, Mb, Mc); // Ma.*Mb -> Mc 3.cvDiv(Ma, Mb, Mc); // Ma./Mb -> Mc 4.cvAddS(Ma, cvScalar(-10.0), Mc); // Ma.-10 -> Mc1
2 3 4 5•向量乘积:
1.double va[] = {1, 2, 3};
2.double vb[] = {0, 0, 1}; 3.double vc[3]; 4.CvMat Va=cvMat(3, 1, CV_64FC1, va); 5.CvMat Vb=cvMat(3, 1, CV_64FC1, vb); 6.CvMat Vc=cvMat(3, 1, CV_64FC1, vc); 7.double res=cvDotProduct(&Va,&Vb); // 点乘: Va . Vb -> res 8.cvCrossProduct(&Va, &Vb, &Vc); // 向量积: Va x Vb -> Vc 9.end{verbatim}1
2 3 4 5 6 7 8 9 10注意 Va, Vb, Vc 在向量积中向量元素个数须相同.
•单矩阵操作:
1.CvMat *Ma, *Mb;
2.cvTranspose(Ma, Mb); // transpose(Ma) -> Mb (不能对自身进行转置) 3.CvScalar t = cvTrace(Ma); // trace(Ma) -> t.val[0] 4.double d = cvDet(Ma); // det(Ma) -> d 5.cvInvert(Ma, Mb); // inv(Ma) -> Mb1
2 3 4 5 6•非齐次线性系统求解:
1.CvMat* A = cvCreateMat(3,3,CV_32FC1);
2.CvMat* x = cvCreateMat(3,1,CV_32FC1); 3.CvMat* b = cvCreateMat(3,1,CV_32FC1); 4.cvSolve(&A, &b, &x); // solve (Ax=b) for x1
2 3 4 5•特征值分析(针对对称矩阵):
1.CvMat* A = cvCreateMat(3,3,CV_32FC1);
2.CvMat* E = cvCreateMat(3,3,CV_32FC1); 3.CvMat* l = cvCreateMat(3,1,CV_32FC1); 4.cvEigenVV(&A, &E, &l); // l = A的特征值 (降序排列) , E = 对应的特征向量 (每行)1
2 3 4 5•奇异值分解SVD:
1.CvMat* A = cvCreateMat(3,3,CV_32FC1);
2.CvMat* U = cvCreateMat(3,3,CV_32FC1); 3.CvMat* D = cvCreateMat(3,3,CV_32FC1); 4.CvMat* V = cvCreateMat(3,3,CV_32FC1); 5.cvSVD(A, D, U, V, CV_SVD_U_T|CV_SVD_V_T); // A = U D V^T1
2 3 4 51.初始化矩阵:
方式一、逐点赋值式: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 );1
2 3 4 5 6 7方式二、连接现有数组式:
double a[] = { 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12 }; CvMat mat = cvMat( 3, 4, CV_64FC1, a ); // 64FC1 for double1
2 3 4 5// 不需要cvReleaseMat,因为数据内存分配是由double定义的数组进行的。
2.IplImage 到cvMat的转换
方式一、cvGetMat方式:
CvMat mathdr, *mat = cvGetMat( img, &mathdr );
1
2方式二、cvConvert方式:
CvMat *mat = cvCreateMat( img->height, img->width, CV_64FC3 );
cvConvert( img, mat ); // #define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )1
2 3 43.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); }1
2 3 4 5 6 7写成函数为:
// 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__; }1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 184.图像直接操作
方式一:直接数组操作 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]; } }1
2 3 4 5 6 7 8 9 10 11方式二:宏操作:
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 ); } }1
2 3 4 5 6 7 8 9 10 11 12注: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;1
2 3对于CV_64FC1 (1 channel double):
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
M->data.db[ row * M->cols + col ] = 3.0;1
2 3一般的,对于1通道的数组:
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
CV_MAT_ELEM( *M, double, row, col ) = 3.0;1
2 3注意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;1
2 3 4 5 6 7更优化的方法是:
#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]; } } }1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18对于多通道的数组,以下操作是推荐的:
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; } }1
2 3 4 5 6 7 8 9 10 11对于两通道和四通道而言:
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);1
2 3 4 56.间接访问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" ); } }1
2 3 4 5 6 7 8 9 10 11 12 13而对于其他的,比如是多通道的后者是其他数据类型的,cvGet/Set2D是个不错的选择
CvScalar scalar = cvGet2D( mat, row, col );
cvSet2D( mat, row, col, cvScalar( r, g, b ) );1
2 3注意:数据不能为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" ); } }1
2 3 4 5 6 7 8 9 10 11 12 13 147.修改矩阵的形状——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 recover1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67对于三通道
// 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 );1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 378.计算色彩距离
我们要计算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 );1
2 3 4比较笨的思路是: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 );1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25比较聪明的思路是
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\n",*p); return 0; }转载地址:http://mwdab.baihongyu.com/