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 );
方式2、鏈接現有數組式: 學習
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定義的數組進行的。
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 );this
法1:CvMat mathdr;編碼
CvMat *mat = cvGetMat( img, &mathdr );spa
(1)將IplImage----- > Mat類型scala
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;
(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
cvArr * 數組的指針。就是opencv裏面的一種類型。
Mat img; const CvArr* s=(CvArr*)&img;
上面就能夠了,CvArr是Mat的虛基類,全部直接強制轉換就能夠了
void cvResize( src 就是以前的lplimage類型的一個指針變量
方式1、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__; }
方式一:直接數組操做 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 )
數組的直接操做比較鬱悶,這是因爲其決定於數組的數據類型。
對於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);
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" ); } }
經實驗代表矩陣操做的進行的順序是:首先知足通道,而後知足列,最後是知足行。
注意:這和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 );
咱們要計算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\n",*p); return 0; }