Msize類和MStep類是Mat的內部嵌套類:
class CV_EXPORTS Mat{public: ...... struct CV_EXPORTS MSize { MSize(int* _p); ...... int* p; }; struct CV_EXPORTS MStep { MStep(); ...... size_t* p; }; MSize size; MStep step; ......};
size和step成員變數為了方便矩陣訪問。其中size記錄了矩陣中每一維的維數,step記錄了每一維中資料量。下面詳細分析:
矩陣如何儲存:
一維矩陣很簡單,就是數組;二維矩陣可以看作數組的數組,即數組的每一個元素都是數組;三維矩陣可以看作數組的二維矩陣,即二維矩陣的每一個元素都是數組;同理,四維,.....,n維。如所示:
上面一幅矩陣圖只是矩陣在人腦中的形式化表現,在電腦中,無論是一維數組、二維矩陣、三維還是n維,它在記憶體中的表示只有一種,那就是:
不同維度數組在記憶體中的儲存方式如所示:
因此,對於矩陣來說,維度資訊是必須的,Mat類中矩陣的維度資訊儲存在成員變數size中。還有一個大問題,如何定位矩陣中的元素,維度之間步長的確定!
| 維數 |
表示方法 |
編譯器處理後表示方法 |
| 1 |
a[i] |
a[i] |
| 2 |
a[i][j] |
a[i*len2+j] |
| 3 |
a[i][j][k] |
a[i*len2*len3+j*len3+k] |
| ... |
... |
... |
| n |
a[i][j][k]...[w] |
a[i*len2*len3*...*lenn+...w] |
其中leni表示第i維的長度
對上表格作簡單解釋:
一維自不必說;二維空間,一維中每個“槽”包含len2個元素;三維空間中,一維中每個槽包含len2×len3個元素。所以以三維空間定位到(i,j,k)為例,結合矩陣在記憶體中的儲存模式,首先i×len2×len3定位到了該點在一維空間的位置,然後j×len3定位到了該點在二維空間中的位置,最後k定位到了該點在三維空間中的位置。以上分析可知,len2×len3就是一維空間中相鄰兩個“槽”間的步長,len3是二維空間中相鄰兩“槽”的步長。兩過程如,
不同維度中相鄰兩個“槽”間的步長很重要,Mat類中以step資料成員儲存步長,把這些步長儲存起來可以方便編程。下面是Mat類中三維矩陣的ptr函數(該函數通過第1、2維座標,得到第三維資料的首地址)
template<typename _Tp> inline const _Tp* Mat::ptr(int i0, int i1) const{ return (const _Tp*)(data + i0*step.p[0] + i1*step.p[1]);}
上面程式中,data是矩陣首地址,其中step.p[0]中儲存第一維空間槽間步長,step.p[1]儲存第二維空間槽間步長。經過 data + i0*step.p[0] + i1*step.p[1] 操作,資料指標從矩陣首地址-->二維空間首地址-->三維空間首地址。然後直接可以運用(ptr(i,j))[k]擷取矩陣元素了。
綜上可以對步長做如下定義:對於n維矩陣M,第i維空間步長是i後各維資料長度的乘積。即第一維步長step1=len2×len3×...×lenn,第二維步長step2=len3×...×lenn。
下面通過size和step的初始化過程驗證下以上分析:
for( i = _dims-1; i >= 0; i-- )//{ int s = _sz[i]; //_sz[]中儲存矩陣各維度大小 m.size.p[i] = s; //初始化size ...... { m.step.p[i] = total;//初始化 step (注意total的計算方式!就是以上step的定義!) int64 total1 = (int64)total*s;// i*len3*len2+j*len3 total = (size_t)total1; } ...... }
綜上:Mat類中step成員儲存維度步長,size成員儲存維度大小。