在OpenCV2中Mat類無疑使佔據着核心地位的,前段時間初學OpenCV2時對Mat類有了個初步的瞭解,見OpenCV2:Mat初學。這幾天試着用OpenCV2實現了圖像縮小的兩種算法:基於等間隔採樣和基於局部均值的圖像縮小,發現對Mat中的數據佈局和一些屬性的認知仍是懵懵懂懂,本文對Mat的一些重要屬性和數據佈局作一個總結。html
The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and matrices, grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms (though, very high-dimensional histograms may be better stored in a SparseMat ).算法
上面的一段話引用自官方的文檔,Mat類用於表示一個多維的單通道或者多通道的稠密數組。可以用來保存實數或複數的向量、矩陣,灰度或彩色圖像,立體元素,點雲,張量以及直方圖(高維的直方圖使用SparseMat保存比較好)。簡而言之,Mat就是用來保存多維的矩陣的。數組
下面的幾個屬性是和Mat中元素的數據類型相關的。函數
CV_8UC1 | CV_8UC2 | CV_8UC3 | CV_8UC4 |
CV_8SC1 | CV_8SC2 | CV_8SC3 | CV_8SC4 |
CV_16UC1 | CV_16UC2 | CV_16UC3 | CV_16UC4 |
CV_16SC1 | CV_16SC2 | CV_16SC3 | CV_16SC4 |
CV_32SC1 | CV_32SC2 | CV_32SC3 | CV_32SC4 |
CV_32FC1 | CV_32FC2 | CV_32FC3 | CV_32FC4 |
CV_64FC1 | CV_64FC2 | CV_64FC3 | CV_64FC4 |
下面是一個示例程序,具體說明Mat的各個屬性: 佈局
Mat img(3, 4, CV_16UC4, Scalar_<uchar>(1, 2, 3, 4)); cout << img << endl; cout << "dims:" << img.dims << endl; cout << "rows:" << img.rows << endl; cout << "cols:" << img.cols << endl; cout << "channels:" << img.channels() << endl; cout << "type:" << img.type() << endl; cout << "depth:" << img.depth() << endl; cout << "elemSize:" << img.elemSize() << endl; cout << "elemSize1:" << img.elemSize1() << endl;
首先建立了一個3*4的具備4個通道的矩陣,其元素類型是CV_16U。Scalar_是一個模板向量,用來初始化矩陣的每一個像素,由於矩陣具備4個通道,Scalar_有四個值。其運行結果: 運行結果首先打印了Mat中的矩陣,接着是Mat的各個屬性。注意其type = 26,而depth = 2。這是因爲上面所說的各類預約義類型
例如,CV_16UC4,CV_8U是一些預約義的常量。spa
Mat中的step是一個MStep的一個實例。其聲明以下: 指針
struct CV_EXPORTS MStep { MStep(); MStep(size_t s); const size_t& operator[](int i) const; size_t& operator[](int i); operator size_t() const; MStep& operator = (size_t s); size_t* p; size_t buf[2]; protected: MStep& operator = (const MStep&); };
從其聲明中能夠看出,MStep和size_t有比較深的關係。用size_t做爲參數的構造函數和重載的賦值運算符code
MStep(size_t s); MStep& operator = (size_t s);
向size_t的類型轉換以及重載的[ ]運算符返回size_thtm
const size_t& operator[](int i) const; size_t& operator[](int i);
size_t的數組以及指針 對象
size_t* p; size_t buf[2];
那麼size_t又是什麼呢,看代碼
typedef unsigned int size_t;
size_t就是無符號整數。
再看一下MStep的構造函數,就能夠知道其究竟保存的是什麼了。
inline Mat::MStep::MStep(size_t s) { p = buf; p[0] = s; p[1] = 0; }
從MStep的定義能夠知道,buff是一個size_t[2],而p是size_t *,也就是能夠把MStep看作一個size_t[2]。那麼step中保存的這個size_t[2]和Mat中的數據有何種關係呢。
step[0]是矩陣中一行元素的字節數。
step[1]是矩陣中一個元素的本身數,也就是和上面所說的elemSize相等。
上面說到,Mat中一個uchar* data指向矩陣數據的首地址,而如今又知道了每一行和每個元素的數據大小,就能夠快速的訪問Mat中的任意元素了。下面公式:
規整化的step,值爲step / elemSize1。 定義以下:
inline size_t Mat::step1(int i) const { return step.p[i]/elemSize1(); }
仍以上例代碼中定義的img爲例,來看下step,step1具體的值: img(3*4)的type是CV_16UC4,step[0]是其一行所佔的數據字節數4 *4 * 16 / 8 = 32.
step[1] 是一個元素所佔的字節數,img的一個元素具備4個通道,故:4 * 16 / 8 = 2
step1 = step / elemSize1,elemSize1是元素的每一個通道所佔的字節數。
上面分析step是一個size_t[2],實際不是很正確,正確的來講step應該是size_t[dims],dims是Mat的維度,因此對於上面的二維的Mat來講,step是size_t[2]也是正確的。
下面就對三維的Mat數據佈局以及step(維度大於3的就算了吧)。
上圖引用自http://ggicci.blog.163.com/blog/static/210364096201261052543349/ 蒐集資料時發現了這幅圖,一切就變的簡單了 感謝做者 Ggicci
三維的數據在Mat中是按面來存儲的,上圖描述的很清晰,這裏再也不多說。
上面言道,step是一個size_t[dims],dims是維度。so,三維的step就是size_t[3]。其他的很少說了,看圖就有了。下面來建立一個三維的Mat,實際看看
int dims[3] = { 3, 3, 3 }; Mat src(3, dims, CV_16SC2, Scalar_<short>(1,2)); cout << "step[0]:" << src.step[0] << endl; cout << "step[1]:" << src.step[1] << endl; cout << "step[2]:" << src.step[2] << endl;
首先建立一個3*3*3,depth爲CV_16S的兩通道的Mat
step[0]是一個數據面的大小 3 * 3 * (16 / 8 ) * 2 = 36
step[1]是一行數據的大小 3 * (16 / 8 ) * 2 = 12
step[2]是一個元素的大小 2 * (16 / 8) = 4
PS: 三維的Mat 不能使用 <<運算符進行輸出的。
over