opencv 入門必學姿勢
影象基本操作
一般來說,灰度圖用 2 維矩陣表示,彩色(多通道)影象用 3 維矩陣(M× N × 3)表示。對於影象顯示來說,目前大部分裝置都是用無符號 8 位整數(型別為 CV_8U)表示畫素亮度。影象資料在計算機記憶體中的儲存順序為以影象最左上點(也可能是最左下點)開始。如果是多通道影象,比如 RGB 影象,則每個畫素用三個位元組表示。在 OpenCV 中,RGB 影象的通道順序為BGR 。
Mat 類定義:
class CV_EXPORTS Mat { public: //一系列函式 …… /*flag引數中包含很多關於矩陣的資訊,如: -Mat的標識 -資料是否連續 -深度 -通道數目*/ int flags; //矩陣的維數,>=2 int dims; //矩陣的行數和列數,如果矩陣超過2維,這兩個變數的值都為 -1 int rows,clos; //只想資料的指標 uchar *data; //指向引用計數的指標,如果資料是由使用者分配的,則為NULL int *refcount; //其他成員變數和成員函式 …… };
建立Mat物件
Mat 類提供了一系列建構函式,可以方便的根據需要建立 Mat 物件。下面是一個使用建構函式建立物件的例子
Mat M(3,2,CV_8UC3,Scalar(0,0,255)); cout<<"M = "<<endl<<" "<<M<<endl;
第一行程式碼建立一個行數(高度)為 3,列數(寬度)為 2 的影象,影象元素是 8 位無符號整數型別,且有三個通道。影象的所有畫素值被初始化為(0, 0,255)。由於 OpenCV 中預設的顏色順序為 BGR,因此這是一個全紅色的影象。
該段程式碼的輸出如圖:
常用的建構函式:
1 //無參構造 2 Mat::Mat(); 3 4 //建立行數為rows,列數為cols,型別為type的影象 5 Mat::Mat(int rows, int cols, int type); 6 7//建立大小為size,型別為type的影象 8 Mat::Mat(Size size, int type); 9 10 //建立行為rows,列為cols,型別為type的影象,並將所有元素初始化為值s 11 Mat::Mat(int rows, int cols, int type, const Scalar &s); 12 13 //建立大小為 size,型別為 type 的影象,並將所有元素初始化為值s 14 Mat::Mat(Size size, int type, const Scalar &s); 15 16 //將 m 賦值給新建立的物件,此處不會對影象資料進行復制,m 和新物件共用影象資料 17 Mat::Mat(const Mat &m); 18 19 //建立行數為 rows,列數為 col,型別為 type 的影象,此建構函式不建立影象資料所需記憶體,而是直接使用 data 所指記憶體,影象的行步長由 step指定 20 Mat::Mat(int rows, int cols, int type, void *data, size_t step=AUTO_STEP); 21 22 //建立大小為 size,型別為 type 的影象,此建構函式不建立影象資料所需記憶體,而是直接使用 data 所指記憶體,影象的行步長由 step 指定 23 Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP); 24 25 //建立的新影象為 m 的一部分,具體的範圍由 rowRange 和 colRange 指定,此建構函式也不進行影象資料的複製操作,新影象與 m 共用影象資料 26 Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange); 27 28 //建立的新影象為 m 的一部分,具體的範圍 roi 指定,此建構函式也不進行影象資料的複製操作,新影象與 m 共用影象資料 29 Mat::Mat(const Mat& m, const Rect& roi);
這些建構函式中,很多都涉及到型別type。type可以是CV_8UC1,CV_16SC1,…,CV_64FC4 等。裡面的 8U 表示 8 位無符號整數,16S 表示 16 位有符號整數,64F表示 64 位浮點數(即 double 型別);C 後面的數表示通道數,例如 C1 表示一個通道的影象,C4 表示 4 個通道的影象,以此類推。
create() 函式建立物件
除了在建構函式中可以建立影象,也可以使用 Mat 類的 create()函式建立影象。如果 create()函式指定的引數與影象之前的引數相同,則不進行實質的記憶體申請操作;如果引數不同,則減少原始資料記憶體的索引,並重新申請記憶體。使用方法如下面例程所示:
1 Mat M(2,2, CV_8UC3);//建構函式建立影象 2 M.create(3,2, CV_8UC2);//釋放記憶體重新建立影象
需要注意的時,使用 create()函式無法設定影象畫素的初始值。
Matlab 風格的建立物件方法
OpenCV 2 中提供了 Matlab 風格的函式,如 zeros(),ones()和 eyes()。這種方法使得程式碼非常簡潔,使用起來也非常方便。使用這些函式需要指定影象的大小和型別,使用方法如下:
1 Mat Z = Mat::zeros(2,3, CV_8UC1); 2 cout << "Z = " << endl << " " << Z << endl; 3 Mat O = Mat::ones(2, 3, CV_32F); 4 cout << "O = " << endl << " " << O << endl; 5 Mat E = Mat::eye(2, 3, CV_64F); 6 cout << "E = " << endl << " " << E << endl;
該程式碼中,有些 type 引數如 CV_32F 未註明通道數目,這種情況下它表示單通道。上面程式碼的輸出結果如圖所示。
矩陣的基本元素表達
對於單通道影象,其元素型別一般為 8U(即 8 位無符號整數),當然也可以是 16S、32F 等;這些型別可以直接用 uchar、short、float 等 C/C++語言中的基本資料型別表達。
如果多通道影象,如 RGB 彩色影象,需要用三個通道來表示。在這種情況下,如果依然將影象視作一個二維矩陣,那麼矩陣的元素不再是基本的資料型別。OpenCV 中有模板類 Vec,可以表示一個向量。OpenCV 中使用 Vec 類預定義了一些小向量,可以將之用於矩陣元素的表達。
1 typedef Vec<uchar, 2> Vec2b; 2 typedef Vec<uchar, 3> Vec3b; 3 typedef Vec<uchar, 4> Vec4b; 4 typedef Vec<short, 2> Vec2s; 5 typedef Vec<short, 3> Vec3s; 6 typedef Vec<short, 4> Vec4s; 7 typedef Vec<int, 2> Vec2i; 8 typedef Vec<int, 3> Vec3i; 9 typedef Vec<int, 4> Vec4i; 10 typedef Vec<float, 2> Vec2f; 11 typedef Vec<float, 3> Vec3f; 12 typedef Vec<float, 4> Vec4f; 13 typedef Vec<float, 6> Vec6f; 14 typedef Vec<double, 2> Vec2d; 15 typedef Vec<double, 3> Vec3d; 16 typedef Vec<double, 4> Vec4d; 17 typedef Vec<double, 6> Vec6d;
例如 8U 型別的 RGB 彩色影象可以使用 Vec3b,3 通道 float 型別的矩陣可以使用 Vec3f。
對於 Vec 物件,可以使用[]符號如運算元組般讀寫其元素,如:
1 Vec3b color; //用 color 變數描述一種 RGB 顏色 2 color[0]=255; //B 分量 3 color[1]=0; //G 分量 4 color[2]=0; //R 分量
畫素值的讀寫
at()函式
函式 at()來實現讀去矩陣中的某個畫素,或者對某個畫素進行賦值操作。下面兩行程式碼演示了 at()函式的使用方法。
1 uchar value = grayim.at<uchar>(i,j);//讀出第 i 行第 j 列畫素值 2 grayim.at<uchar>(i,j)=128; //將第 i 行第 j 列畫素值設定為 128
如果要對影象進行遍歷,可以參考下面的例程。這個例程建立了兩個影象,分別是單通道的 grayim 以及 3 個通道的 colorim,然後對兩個影象的所有畫素值進行賦值,最後現實結果。
1 #include <iostream> 2 #include "opencv2/opencv.hpp" 3 using namespace std; 4 using namespace cv; 5 int main(int argc, char* argv[]) 6 { 7 Mat grayim(600, 800, CV_8UC1); 8 Mat colorim(600, 800, CV_8UC3); 9 10 //遍歷所有畫素,並設定畫素值 11 for(int i = 0; i < grayim.raws; ++i) 12 for(int j = 0; j < grayim.cols; ++j) 13 grayim.at<uchar>(i,j) = (i + j) % 255; 14 15 //遍歷所有畫素,並設定畫素值 16 for(int i = 0; i < colorim.raws; ++i) 17 for(int j = 0; j < colorim.cols; ++j) 18 { 19 Vec3b pixel; 20 pixel[0] = i%255; //blue 21 pixel[1] = j%255; //green 22 pixel[2] = 0; //red 23 colorim.at<Vec3b>(i,j) = pixel; 24 } 25 //顯示結果 26 imshow("grayim",grayim); 27 imshow("colorim",colorim); 28 waitKey(0); 29 return 0 ; 30 }
執行結果如下:
使用迭代器
1 #include <iostream> 2 #include "opencv2/opencv.hpp" 3 using namespace std; 4 using namespace cv; 5 6 int main(int argc, char* argv[]) 7 { 8 Mat grayim(600, 800, CV_8UC1); 9 Mat colorim(600, 800, CV_8UC3); 10 11 //遍歷所有畫素,並設定畫素值 12 MatIterator_<uchar> grayit, grayend; 13 for( grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>(); grayit != grayend; ++grayit) 14 *grayit = rand()%255; 15 16 //遍歷所有畫素,並設定畫素值 17 MatIterator_<Vec3b> colorit, colorend; 18 for( colorit = colorim.begin<Vec3b>(), colorend = colorim.end<Vec3b>(); colorit != colorend; ++colorit) 19 { 20 (*colorit)[0] = rand()%255; //Blue 21 (*colorit)[1] = rand()%255; //Green 22 (*colorit)[2] = rand()%255; //Red 23 } 24 //顯示結果 25 imshow("grayim", grayim); 26 imshow("colorim", colorim); 27 waitKey(0); 28 return 0; 29 }
通過資料指標
如果你非常注重程式的執行速度,那麼遍歷畫素時,建議使用指標。下面的例程演示如何使用指標來遍歷影象中的所有畫素。
1 #include <iostream> 2 #include "opencv2/opencv.hpp" 3 using namespace std; 4 using namespace cv; 5 6 int main(int argc, char* argv[]) 7 { 8 Mat grayim(600, 800, CV_8UC1); 9 Mat colorim(600, 800, CV_8UC3); 10 11 //遍歷所有畫素,並設定畫素值 12 for( int i = 0; i < grayim.rows; ++i) 13 { 14 //獲取第 i 行首畫素指標 15 uchar * p = grayim.ptr<uchar>(i); 16 //對第 i 行的每個畫素(byte)操作 17 for( int j = 0; j < grayim.cols; ++j ) 18 p[j] = (i+j)%255; 19 } 20 21 //遍歷所有畫素,並設定畫素值 22 for( int i = 0; i < colorim.rows; ++i) 23 { 24 //獲取第 i 行首畫素指標 25 Vec3b * p = colorim.ptr<Vec3b>(i); 26 for( int j = 0; j < colorim.cols; ++j ) 27 { 28 p[j][0] = i%255; //Blue 29 p[j][1] = j%255; //Green 30 p[j][2] = 0; //Red 31 } 32 } 33 34 //顯示結果 35 imshow("grayim", grayim); 36 imshow("colorim", colorim); 37 waitKey(0); 38 return 0; 39 }