在我們討論IplImage之前,我們需要看另一個數據類型:CvMat,即OpenCV的矩陣類型。儘管OpenCV是用C語言實現的,但是CvMat和IplImage的關係其實就類似於C++中的類的繼承關係。IplImage類繼承自CvMat類。所以,我們最好先了解一下IplImage的基類CvMat類的情況,然後再看更復雜的IplImage類。而CvArr類,是CvMat類的抽象基類。正因爲CvArr類是基類,所以當我們看到OpenCV的函數參數爲CvArr*類型的參數時,我們可以代入CvMat*或者IplImage*類型的實參。
CvMat矩陣數據結構
當我們學習CvMat之前,我們必須知道兩個事情,首先OpenCV中是沒有"vector(向量)"數據類型的,當我們需要一個"vector"時,我們就使用一個三行一列的矩陣。其次,OpenCV中矩陣的概念比線性代數中矩陣的概念更抽象和複雜一些一些。例如,創建矩陣的函數:CvMat* cvCreateMat(int rows,int cols,int type),其中type代表預定義的數據類型,即矩陣中每一個元素的數據類型,該類型的形式是:CV_<bit數>(S|U|F)C<通道數>,例如,數據類型可能是CV32FC1,即32bit的浮點數,或CV_8UC3,8bit的無符號整數,或CV_8UC3,無符號8bit整數,3通道,等等。我們會發現,cvMat裏,矩陣中行和列上的每一個元素,不必是一個單獨的數字,可能是一系列數字(有幾個通道就有幾個數字)。每一個元素可以代表多個值,就允許了我們在矩陣中包含一個RGB的圖像。
從內部的結構上看,CvMat相當的簡單,我們可以通過代碼看一下該數據結構的原型(代碼在.../opencv/cxcore/include/cxtypes.h):
其中包含了width,height,type,step(是一行元素的長度,與width類似,但以字節計算),以及指向數據的指針.你可以通過CvMat數據類型的變量直接接觸該類型內部的成員,例如,CvMat* matrix ,就可以用matrix->height,matrix->width來獲得矩陣的尺寸。又或者通過OpenCV的函數來獲得。例如,可以用cvGetSize(CvMat*)來獲得CvSize對象,這代表該矩陣的長和寬。
typedef struct CvMat
{
int type;
int step;
/* for internal use only */
int* refcount;
int hdr_refcount;
union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;
#ifdef __cplusplus
union
{
int rows;
int height;
};
union
{
int cols;
int width;
};
#else
int rows;
int cols;
#endif
}
CvMat;
以上,是該CvMat類型的數據的“頭部”,即矩陣的定義部分。許多OpenCV的函數,將矩陣的頭部和數據部分分開處理。
矩陣的創建可以用多種方法,最簡單的一種是CvMat* cvCreateMat(int rows,int cols,int type),該方法既設置了矩陣的頭部,又爲數據部分分配了內存空間,該函數是cvCreateMatHeader()和cvCreateData()的合併縮寫。cvCreateMatHeader()只創建CvMat頭部,但不爲數據部分分配空間。而cvCreateData()則是爲矩陣的數據部分分配了內存空間。有時候,我們只需要cvCreateMatHeader()就可以了,因爲基於一些理由,我們可能已經爲數據部分分配了空間,或者此時分配空間還不是時候。另一個創建矩陣的方法cvCloneMat(CvMat*)是從一個已經有的矩陣,來“克隆”出一個新的矩陣來。當我們不再需要某矩陣時,我們需要調用長cvReleaseMat(CvMat*)來釋放它。
就象其他的OpenCV數據類型一樣,矩陣數據類型有一個構造函數,CvMat cvMat( int rows, int cols, int type, void* data=NULL );這個函數沒有爲矩陣的數據部分分配空間,只是初始化了矩陣的頭部,類似cvInitMatheader()。
以下是這些函數的原型:
1,CreateMat
創建矩陣
CvMat* cvCreateMat( int rows, int cols, int type );
rows
矩陣行數。
cols
矩陣列數。
type
矩陣元素類型。 通常以 CV_<比特數>(S|U|F)C<通道數>型式描述, 例如:
CV_8UC1 意思是一個8-bit 無符號單通道矩陣, CV_32SC2 意思是一個32-bit 有符號二個通道的矩陣。
函數 cvCreateMat 爲新的矩陣分配頭和下面的數據,並且返回一個指向新創建的矩陣的指針。是下列操作的縮寫型式:
CvMat* mat = cvCreateMatHeader( rows, cols, type );
cvCreateData( mat );
矩陣按行存貯。所有的行以4個字節對齊。
2,CreateMatHeader
創建新的矩陣頭
CvMat* cvCreateMatHeader( int rows, int cols, int type );
rows
矩陣行數.
cols
矩陣列數.
type
矩陣元素類型(見 cvCreateMat).
函數 cvCreateMatHeader 分配新的矩陣頭並且返回指向它的指針. 矩陣數據可被進一步的分配,使用cvCreateData 或通過 cvSetData明確的
分配數據.
3,ReleaseMat
刪除矩陣
void cvReleaseMat( CvMat** mat );
mat
雙指針指向矩陣.
函數cvReleaseMat 縮減矩陣數據參考計數並且釋放矩陣頭 :
if( *mat )
cvDecRefData( *mat );
cvFree( (void**)mat );
4,InitMatHeader
初始化矩陣頭
CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,
void* data=NULL, int step=CV_AUTOSTEP );
mat
指針指向要被初始化的矩陣頭.
rows
矩陣的行數.
cols
矩陣的列數.
type
矩陣元素類型.
data
可選的,將指向數據指針分配給矩陣頭.
step
排列後的數據的整個行寬,默認狀態下,使用STEP的最小可能值。也就是說默認情況下假定矩陣的行與行之間無隙.
函數 cvInitMatHeader 初始化已經分配了的 CvMat 結構. 它可以被OpenCV矩陣函數用於處理原始數據。
例如, 下面的代碼計算通用數組格式存貯的數據的矩陣乘積.
計算兩個矩陣的積
double a[] = { 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12 };
double b[] = { 1, 5, 9,
2, 6, 10,
3, 7, 11,
4, 8, 12 };
double c[9];
CvMat Ma, Mb, Mc ;
cvInitMatHeader( &Ma, 3, 4, CV_64FC1, a );
cvInitMatHeader( &Mb, 4, 3, CV_64FC1, b );
cvInitMatHeader( &Mc, 3, 3, CV_64FC1, c );
cvMatMulAdd( &Ma, &Mb, 0, &Mc );
// c 數組存貯 a(3x4) 和 b(4x3) 矩陣的積
5,Mat
初始化矩陣的頭
CvMat cvMat( int rows, int cols, int type, void* data=NULL );
rows
矩陣行數
cols
列數.
type
元素類型(見CreateMat).
data
可選的分配給矩陣頭的數據指針 .
函數 cvMat 是個一快速內連函數,替代函數 cvInitMatHeader. 也就是說它相當於:
CvMat mat;
cvInitMatHeader( &mat, rows, cols, type, data, CV_AUTOSTEP );
6,CloneMat
創建矩陣拷貝
CvMat* cvCloneMat( const CvMat* mat );
mat
輸入矩陣.
函數 cvCloneMat 創建輸入矩陣的一個拷貝並且返回該矩陣的指針.
以下的例子中,我們讓矩陣的數據部分指向了已經分配好了的數據:
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025};
CvMat rotmat;
cvInitMatHeader(
&rotmat,
2,
2,
CV_32FC1,
vals
);
當我們定義好了矩陣,我們可以通過一些函數查看矩陣的屬性,例如:cvGetElemType(const cvArr* arr),cvGetDims(const CvArr* arr,int*
sizes=NULL),cvGetDimSize(const CvArr* arr,index)
7.GetElemType
返回數組元素類型
int cvGetElemType( const CvArr* arr );
arr
輸入數組.
函數 GetElemType 返回數組元素類型就像在cvCreateMat 中討論的一樣:
CV_8UC1 ... CV_64FC4
8.GetDims, GetDimSize
返回數組維數和他們的大小或者特殊維的大小
int cvGetDims( const CvArr* arr, int* sizes=NULL );
int cvGetDimSize( const CvArr* arr, int index );
arr
輸入數組.
sizes
可選的輸出數組維尺寸向量,對於2D數組第一位是數組行數(高),第二位是數組列數(寬)
index
以0爲基準的維索引下標(對於矩陣0意味着行數,1意味着列數,對於圖象0意味着高,1意味着寬。
函數 cvGetDims 返回維數和他們的大小。如果是 IplImage 或 CvMat 總是返回2,不管圖像/矩陣行數。函數 cvGetDimSize 返回特定的維大小(每維的元素數)。
以下是一個例子:
#pragma comment( lib, "cxcore.lib" )
#include "cv.h"
#include <stdio.h>
void main()
{
CvMat rotmat;
int sizes[2];
float array[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
//注意,此句更改爲 cvInitMatHeader(&rotmat,3,5,CV_32FC1,NULL);時,cvGetDimSize不能起作用
cvInitMatHeader(&rotmat,3,5,CV_32FC1,array);
int type = cvGetElemType(&rotmat);
cvGetDims(&rotmat,sizes);
printf("type=%d/n",type);
printf("height=%d,width=%d/n",sizes[0],sizes[1]);
printf("height=%d,width=%d/n",cvGetDimSize(&rotmat, 0),cvGetDimSize(&rotmat, 1));
}