【第二部分-影象處理】第1章 Opencv影象處理入門
1.1基礎影象容器Mat類的使用
1.1.1基本影象容器Mat
在正式講解OpenCV之前,首先要介紹的就是Mat。
在2001年剛剛出現的時候,OpenCV基於 C 語言介面而建。為了在記憶體(memory)中存放影象,當時採用名為 IplImage 的C語言結構體,時至今日這仍出現在大多數的舊版教程和教學材料。但這種方法必須接受C語言所有的不足,這其中最大的不足要數手動記憶體管理,其依據是使用者要為開闢和銷燬記憶體負責。雖然對於小型的程式來說手動管理記憶體不是問題,但一旦程式碼開始變得越來越龐大,你需要越來越多地糾纏於這個問題,而不是著力解決你的開發目標。
幸運的是,C++出現了,並且帶來類的概念,這給使用者帶來另外一個選擇:自動的記憶體管理(不嚴謹地說)。這是一個好訊息,如果C++完全相容C的話,這個變化不會帶來相容性問題。為此,OpenCV在2.0版本中引入了一個新的C++介面,利用自動記憶體管理給出瞭解決問題的新方法。使用這個方法,你不需要糾結在管理記憶體上,而且你的程式碼會變得簡潔(少寫多得)。但C++介面唯一的不足是當前許多嵌入式開發系統只支援C語言。所以,當目標不是這種開發平臺時,沒有必要使用舊方法。
關於 Mat ,首先要知道的是你不必再手動地(1)為其開闢空間(2)在不需要時立即將空間釋放。但手動地做還是可以的:大多數OpenCV函式仍會手動地為輸出資料開闢空間。當傳遞一個已經存在的 Mat 物件時,開闢好的矩陣空間會被重用。也就是說,我們每次都使用大小正好的記憶體來完成任務。
基本上講 Mat 是一個類,由兩個資料部分組成:矩陣頭(包含矩陣尺寸,儲存方法,儲存地址等資訊)和一個指向儲存所有畫素值的矩陣(根據所選儲存方法的不同矩陣可以是不同的維數)的指標。矩陣頭的尺寸是常數值,但矩陣本身的尺寸會依影象的不同而不同,通常比矩陣頭的尺寸大數個數量級。因此,當在程式中傳遞影象並建立拷貝時,大的開銷是由矩陣造成的,而不是資訊頭。OpenCV是一個影象處理庫,囊括了大量的影象處理函式,為了解決問題通常要使用庫中的多個函式,因此在函式中傳遞影象是家常便飯。同時不要忘了我們正在討論的是計算量很大的影象處理演算法,因此,除非萬不得已,我們不應該拷貝 大 的影象,因為這會降低程式速度。
為了搞定這個問題,OpenCV使用引用計數機制。其思路是讓每個 Mat 物件有自己的資訊頭,但共享同一個矩陣。這通過讓矩陣指標指向同一地址而實現。而拷貝建構函式則只拷貝資訊頭和矩陣指標 ,而不拷貝矩陣。
Mat A, C; // 只建立資訊頭部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 這裡為矩陣開闢記憶體
Mat B(A); // 使用拷貝建構函式
C = A; // 賦值運算子
以上程式碼中的所有Mat物件最終都指向同一個也是唯一一個數據矩陣。雖然它們的資訊頭不同,但通過任何一個物件所做的改變也會影響其它物件。實際上,不同的物件只是訪問相同資料的不同途徑而已。這裡還要提及一個比較棒的功能:你可以建立只引用部分資料的資訊頭。比如想要建立一個感興趣區域( ROI ),你只需要建立包含邊界資訊的資訊頭:
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries
Rect(10,10,100,100):建立一個矩形物件,通過使用四個整數來初始化矩形左上角的橫座標、縱座標以及右下角的橫座標、縱座標。
Range:確定一個連續的序列,Range:all()表示獲取整個序列,Range(1,3)表示獲取第一列到第三列。
現在你也許會問,如果矩陣屬於多個 Mat 物件,那麼當不再需要它時誰來負責清理?簡單的回答是:最後一個使用它的物件。通過引用計數機制來實現。無論什麼時候有人拷貝了一個 Mat 物件的資訊頭,都會增加矩陣的引用次數;反之當一個頭被釋放之後,這個計數被減一;當計數值為零,矩陣會被清理。但某些時候你仍會想拷貝矩陣本身(不只是資訊頭和矩陣指標),這時可以使用函式 clone() 或者 “`
copyTo() 。
Mat F = A.clone();
Mat G;
A.copyTo(G);
現在改變 F 或者 G 就不會影響 Mat 資訊頭所指向的矩陣。總結一下,你需要記住的是
OpenCV函式中輸出影象的記憶體分配是自動完成的(如果不特別指定的話)。
使用OpenCV的C++介面時不需要考慮記憶體釋放問題。
賦值運算子和拷貝建構函式( ctor )只拷貝資訊頭。
使用函式 clone() 或者 copyTo() 來拷貝一副影象的矩陣。
關於Mat更詳細的內容請參考…\opencv\sources\modules\core\include\opencv2\core\mat.hpp檔案的730行開始,原始碼中註釋很詳細,筆者就不細說了,由於內容太多,筆者只是擷取部分內容,有興趣的請去看原始碼吧。
前面講解了什麼是Mat,那麼對於Mat,是如何儲存影象的呢,在這裡要講述如何儲存畫素值。首先需要指定顏色空間和資料型別。顏色空間是指對一個給定的顏色,如何組合顏色元素以對其編碼。最簡單的顏色空間要屬灰度級空間,只處理黑色和白色,對它們進行組合可以產生不同程度的灰色。
對於彩色方式則有更多種類的顏色空間,但不論哪種方式都是把顏色分成三個或者四個基元素,通過組合基元素可以產生所有的顏色。RGB顏色空間是最常用的一種顏色空間,這歸功於它也是人眼內部構成顏色的方式。它的基色是紅色、綠色和藍色,有時為了表示透明顏色也會加入第四個元素 alpha (A)。
有很多的顏色系統,各有自身優勢:
RGB是最常見的,這是因為人眼採用相似的工作機制,它也被顯示裝置所採用。
HSV和HLS把顏色分解成色調、飽和度和亮度/明度。這是描述顏色更自然的方式,比如可以通過拋棄最後一個元素,使演算法對輸入影象的光照條件不敏感。
YCrCb在JPEG影象格式中廣泛使用。
CIE L*a*b*是一種在感知上均勻的顏色空間,它適合用來度量兩個顏色之間的距離 。
關於各個顏色之間的相互轉換,筆者會在以後的文章中會詳細講解。
每個組成元素都有其自己的定義域,取決於其資料型別。如何儲存一個元素決定了我們在其定義域上能夠控制的精度。最小的資料型別是 char ,佔一個位元組或者8位,可以是有符號型(0到255之間)或無符號型(-127到+127之間)。儘管使用三個 char 型元素已經可以表示1600萬種可能的顏色(使用RGB顏色空間),但若使用float(4位元組,32位)或double(8位元組,64位)則能給出更加精細的顏色分辨能力。但同時也要切記增加元素的尺寸也會增加了影象所佔的記憶體空間。
接下來,Mat裡面depth,dims,channels,step,data,elemSize和資料地址計算的理解,矩陣 (M) 中資料元素的地址計算公式:
其中,其中 m = M.dims是 M的維度。
data:Mat物件中的一個指標,指向記憶體中存放矩陣資料的一塊記憶體 (uchar* data);
dims:Mat所代表的矩陣的維度,如 3 * 4 的矩陣為 2 維, 3 * 4 * 5 的為3維;
channels:通道,矩陣中的每一個矩陣元素擁有的值的個數,比如說 3 * 4 矩陣中一共 12 個元素,如果每個元素有三個值,那麼就說這個矩陣是 3 通道的,即 channels = 3。常見的是一張彩色圖片有紅、綠、藍三個通道。;depth:深度,即每一個畫素的位數(bits),在opencv的Mat.depth()中得到的是一個 0 – 6 的數字,分別代表不同的位數:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; 可見 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;;
step:是一個數組,定義了矩陣的佈局,具體見下面圖片分析,另外注意 step1 (step / elemSize1),M.step[m-1] 總是等於 elemSize,M.step1(m-1)總是等於 channels;;
elemSize : 矩陣中每一個元素的資料大小,如果Mat中的資料的資料型別是 CV_8U 那麼 elemSize = 1,CV_8UC3 那麼 elemSize = 3,CV_16UC2 那麼 elemSize = 4;記住另外有個 elemSize1 表示的是矩陣中資料型別的大小,即 elemSize / channels 的大小。
先看看一些Mat結構。
【注】上圖中ch表示通道;dim表示維度;
從上圖可以看出第1個表示雙通道的2維影象,第2個表示單通道的二維影象;第3張表示3通道的2維影象;第4張表示4通道的3維影象。
接下來筆者對二維和三維的情況具體分析。先看看二維的情況,下圖是二維情況(stored row by row)按行儲存。
上面是一個 3 X 4 的矩陣,假設其資料型別為 CV_8U,也就是單通道的 uchar 型別。這是一個二維矩陣,那麼維度為 2 (M.dims == 2);
M.rows == 3; M.cols == 4;
sizeof(uchar) = 1,那麼每一個數據元素大小為 1 (M.elemSize() == 1, M.elemSize1() == 1);
CV_8U 得到 M.depth() == 0, M.channels() == 1;
因為是二維矩陣,那麼 step 陣列只有兩個值, step[0] 和 step[1] 分別代表一行的資料大小和一個元素的資料大小,則 M.step[0] == 4, M.step[1] == 1;
M.step1(0) == M.cols = 4; M.step1(1) == 1;
假設上面的矩陣資料型別是 CV_8UC3,也就是三通道
M.dims == 2; M.channels() == 3;M.depth() == 0;
M.elemSize() == 3 (每一個元素包含3個uchar值) M.elemSize1() == 1 (elemSize / channels)
M.step[0] == M.cols * M.elemSize() == 12, M.step[1] == M.channels() * M.elemSize1() == M.elemSize() == 3;
M.step(0) == M.cols * M.channels() == 12 ; M.step(1) == M.channels() == 3;
三維情況(stored plane by plane)按面儲存。
上面是一個 3 X 4 X 6 的矩陣,假設其資料型別為 CV_16SC4,也就是 short 型別。
M.dims == 3 ; M.channels() == 4 ; M.elemSize1() == sizeof(short) == 2 ;
M.rows == M.cols == –1;
M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;
M.step[0] == 4 * 6 * M.elemSize() == 192;
M.step[1] == 6 * M.elemSize() == 48;
M.step[2] == M.elemSize() == 8;
M.step1(0) == M.step[0] / M.elemSize() == 48 / 2 == 96 (第一維度(即面的元素個數) * 通道數);
M.step1(1) == M.step[1] / M.elemSize() == 12 / 2 == 24(第二維度(即行的元素個數/列寬) * 通道數);
M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三維度(即元素) * 通道數);
說了這這麼多來個例子吧。
int main(int argc, char** argv)
{
//宣告一個uchar型別的單通道矩陣(灰度影象)
Mat m(400, 400, CV_8U, Scalar(0));
for (int col = 0; col < 400; col++)
{
//將影象的中間幾行改為白色
for (int row = 195; row < 205; row++)
{
cout << (int)(*(m.data + m.step[0] * row + m.step[1] * col)) << "==>";
*(m.data + m.step[0] * row + m.step[1] * col) = 255;
cout << (int)(*(m.data + m.step[0] * row + m.step[1] * col)) << endl;
}
}
imshow("Test", m);
waitKey(1000);
return 0;
}
執行效果如下圖所示。
可以看到中間幾行改為了白色。
在來個三通道的例項。直接上程式碼。
效果如下圖所示。
可以看到其中有一塊區域改為了藍色。
1.1.2建立Mat的七種方法
1、【方法一】使用Mat()建構函式
最常用的就是使用Mat()建構函式。
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;
執行結果:
對於二維多通道影象,首先要定義其尺寸,即行數和列數。 然後,需要指定儲存元素的資料型別以及每個矩陣點的通道數 。規則如下。
CV_[The number of this bits per item][Signed or Unsigned][Type Prefix]C[The channel number]
即:CV_[位數][帶符號與否][型別字首]C[通道數]
上文的CV_8UC3表示8位無符號,每個畫素由三個元素組成。
Scalar(0,0,255)表示輸入的畫素值。
2、【方法二】在C/C++中通過建構函式進行初始化
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
如何建立一個超過兩維的矩陣:指定維數,然後傳遞一個指向一個數組的指標,這個陣列包含每個維度的尺寸。
3、【方法三】為已經存在的IPlImage建立資訊頭
具體實現的的程式碼如下:
IplImage * img = cvLoadImage(“1.jpg”,1);
Mat mtx(img);//轉換IplImage*->Mat
4、【方法四】利用Create()函式
Mat M;
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
這個建立方法不能為矩陣設初值,它只是在改變尺寸時重新為矩陣資料開闢記憶體。
5、【方法五】採用MATLAB式的方法
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
MATLAB形式的初始化方式: zeros(), ones(), :eyes()
6、【方法六】對於小矩陣使用逗號分隔式初始化
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
對於小矩陣你可以用逗號分隔的初始化函式。
7、【方法七】為已經存在的建立新的資訊頭
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
使用 clone() 或者 copyTo() 為一個存在的 Mat 物件建立一個新的資訊頭。
1.1.3 OpenCV格式化輸出
首先定義r矩陣 ,通過randu()為矩陣隨機分配畫素值,分配數字的範圍為0-255;
Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255));
1、【風格一】Opencv預設風格
cout << "r (OpenCV預設風格) = " << r << ";" << endl << endl;
2、【風格二】Python風格
此句為OpenCV2的程式碼:
cout << "r (Python風格) = " << format(r,"python") << ";" << endl << endl;
此句為OpenCV3的程式碼:
cout << "r (python風格) =" << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;
3、【風格三】Numpy風格
此句為OpenCV2的程式碼:
cout << "r (Numpy風格) = " << format(r,"numpy") << ";" << endl << endl;
此句為OpenCV3的程式碼:
cout << "r (numpy風格) = " << format(r, Formatter::FMT_NUMPY) << ";" << endl << endl;
4、【風格四】逗號分隔風格(CSV風格)
此句為OpenCV2的程式碼:
cout << "r (逗號分隔風格) = " << format(r,"csv") << ";" << endl<< endl;
此句為OpenCV3的程式碼:
cout << "r (逗號分隔風格) = \n" << format(r, Formatter::FMT_CSV) << ";" << endl << endl;
5、【風格五】C語言風格
此句為OpenCV2的程式碼:
cout << "r (C語言風格) = " << format(r,"C") << ";" << endl << endl;
此句為OpenCV3的程式碼:
cout << "r (C語言風格) = \n" << format(r, Formatter::FMT_C) << ";" << endl << endl;
6、【風格六】MATLAB風格
此句為OpenCV2的程式碼:
cout << "r (matlab風格) =" << format(r, "MATLAB") << ";" << endl << endl;
此句為OpenCV2的程式碼:
cout << "r (matlab風格) = " << format(r, Formatter::FMT_MATLAB) << ";" << endl << endl;
1.1.4輸出其他常用資料結構
1、定義和輸出二維點
Point2f p(6, 2);
cout << "【2維點】p = " << p << ";\n" << endl;
2、定義和輸出三維點
Point3f p3f(8, 2, 0);
cout << "【3維點】p3f = " << p3f << ";\n" << endl;
3、定義輸出基於cv::Mat的std::vector
vector<float> v;
v.push_back(3);
v.push_back(5);
v.push_back(7);
cout << "【基於Mat的vector】shortvec = " << Mat(v) << ";\n"<<endl;
4、定義輸出std::vector 點
vector<Point2f> points(20);
for (size_t i = 0; i < points.size(); ++i)
points[i] = Point2f((float)(i * 5), (float)(i % 7));
cout << "【二維點向量】points = " << points<<";";
1.1.5 OpenCV的名稱空間
引入了一個顯示圖片的案例,在標頭檔案引入之後和main函式之前,我們可以看到這樣一句宣告:
using namespace cv;
這就是名稱空間的宣告,在OpenCV中,C++類和函式都是定義在cv名稱空間內的,所以假如我們需要在程式碼中引用OpenCV的時,需要宣告其名稱空間,方法有兩個:
- 在main函式之後用“using namespace cv;”來宣告名稱空間
- 在要呼叫的類或者函式前面加上“cv::”來宣告名稱空間
顯然,使用第一種方法的宣告更加簡潔方便。
1.1.6 Mat使用例項
【程式碼-參看附件demo1】
【程式碼註釋】
- randu
把image分成一個符合正太分佈的隨機數矩陣。
【函式原型】
C++: void randu(InputOutputArray mtx, InputArray low, InputArray high)
【引數】
第一個引數,dst:輸出隨機資料的矩陣
第二個引數,low:產生隨機數的下邊界
第三個引數,high:產生隨機數的上邊界
dst的範圍為:lowc ≤ dst(I)c < highc
- vextor
vector容器是一個模板類,可以存放任何型別的物件(但必須是同一類物件)。vector物件可以在執行時高效地新增元素,並且vector中元素是連續儲存的。
【函式原型】
template<typename T>
explicit vector(); // 預設建構函式,vector物件為空
explicit vector(size_type n, const T& v = T()); // 建立有n個元素的vector物件
vector(const vector& x);
vector(const_iterator first, const_iterator last);
注:vector容器記憶體放的所有物件都是經過初始化的。如果沒有指定儲存物件的初始值,那麼對於內建型別將用0初始化,對於類型別將呼叫其預設建構函式進行初始化(如果有其它建構函式而沒有預設建構函式,那麼此時必須提供元素初始值才能放入容器中)。
【舉例】
vector<string> v1; // 建立空容器,其物件型別為string類
vector<string> v2(10); // 建立有10個具有初始值(即空串)的string類物件的容器
vector<string> v3(5, "hello"); // 建立有5個值為“hello”的string類物件的容器
vector<string> v4(v3.begin(), v3.end()); // v4是與v3相同的容器(完全複製)
- 本例程預設使用的是Linux作業系統和OPencv3的庫
筆者在Linux上執行用的是Opencv3,因此要加以下巨集定義。
#define OPENCV3
在VS12上,也就是在Windows上要加巨集
#define VS12
1.2影象的讀取與顯示
1.2.1圖片載入、顯示及輸出
1.圖片載入
- 讀取圖片:imread()函式
C++: Mat imread(const string& filename, int flags=1 )
【引數】
第一個引數,filename- 用於填寫圖片存放的路徑
第二個引數,flags-載入標誌,用於指定載入圖片的顏色型別。顯然這是一個列舉型別的,而且其預設值為1,具體的列舉表可以在higui_ch中找到。當然除了列舉也可以根據flag的賦值範圍來判斷顏色型別:
flags >0:返回一個3通道的彩色影象
flags =0:返回灰度影象
flags <0:返回包含Alpha通道的載入的影象
(需要注意的點:輸出的影象預設情況下是不載入Alpha通道進來的。如果我們需要載入Alpha通道的話呢,這裡就需要取負值。)
2.圖片顯示
- 建立圖片顯示視窗:namedWindow()函式
C++: void namedWindow(const string& winname, int flags)
【引數】
第一個引數,string& winname- 用於標識作用的,視窗的名稱,不作為顯示名稱
第二個引數,flags -int型別的視窗標識(跟視窗的大小有關)
- 顯示圖片:imshow()函式
C++: void imshow(const string& winname, InputArray image)
【引數】
第一個引數,winname-視窗的名稱,作為顯示名稱
第二個引數,InputArray - 圖片矩陣型別資料
3.圖片輸出
- 圖片輸出:imwrite()函式
C++: bool imwrite(const string& filename, InputArray image, const vector<int>& params=vector<int>())
【引數】
第一個引數,string& filename - 輸出之後的檔名,需要加上字尾
第二個引數,InputArray - 輸出的一個圖片Mat資料
第三個引數,vector& params - 特定格式儲存的引數編碼,具有預設值,一般不填
1.2.2賦值與複製
如果我們想得到一個影象的副本並進行了下面的操作。
Mat newImage=Image;
如果我們對newImage進行修改或操作,則會直接影響Image影象,因為newImage與Image共用了資料內容。這也是影象的淺拷貝。
想要真正得到一個副本可以這樣做,即影象的深拷貝。
Mat newImage;
Image.copyTo(newImage);//方法一
Mat newImage=image.clone();//方法二
copyTo還有一個重構函式copyTo(B,MASK)。意思是可以得到一個附加掩膜MASK的矩陣B。我們從影象的角度來看這個函式的作用。首先需要生成一張掩膜MASK,一般情況下這個膜和你需要操作的物件影象一樣大。生成方法見下面例子:
Mat MASK(A.rows,A.cols,CV_8UC3,Scalar(0,0,0));//生成一個三通道的彩色掩膜,初始化為黑色。
Mat MASK(A.rows,A.cols,CV_8UC1,Scalar(0));//生成一個灰度的掩膜,初始化為黑色。
Mat MASK=Mat::zeros(A.size( ), CV_8UC3);//生成一個三通道的彩色掩膜,初始化為黑色。需要改成灰度的只需把CV_8UC3改為CV_8UC1。
對一幅圖加一個掩膜顧名思義,就是想要蓋住圖片的某一部分。所以使用A.copyTo(B,MASK)之後得到的是A被MASK掩蓋後的影象。因為初始化的掩膜時黑色的,如果直接加上去整個圖片都會被掩蓋了,所以需要把一部分你不想蓋住的位置改成別的顏色。在後文會詳細講解掩膜的使用。
很多時候,我們並不想得到原影象的複製,但是要建立一個跟原影象大小相同的影象。下面的程式碼可以完成這樣的功能。
Mat newImage;
newImage.create(Image.size(),Image.type());
前文也說了很多了,這裡再總結一下,Mat包括頭和資料指標,當使用Mat的建構函式初始化的時候,會將頭和資料指標複製(注意:只是指標複製,指標指向的地址不會複製),若要將資料也複製,則必須使用copyTo或clone函式關於深拷貝和淺拷貝參考下圖。
圖6
官方參考:
掩碼操作英文
掩碼操作中文
1.2.3圖片顯示例項
【程式碼-參看附件demo2】
1.3常用資料結構和函式
1.3.1點的表示:Point類
Point_類
/*【Point_原始碼】*****************************************************************
* @Version:OpenCV 3.0.0
* @原始碼路徑:…\opencv\sources\modules\core\include\opencv2\core\types.hpp
* @起始行數:147行
********************************************************************************/
template<typename _Tp> class Point_
{
public:
typedef _Tp value_type;
// various constructors各種建構函式
Point_();
Point_(_Tp _x, _Tp _y);
Point_(const Point_& pt);
Point_(const Size_<_Tp>& sz);
Point_(const Vec<_Tp, 2>& v);
Point_& operator = (const Point_& pt);
//! conversion to another data type轉換為另一種型別
template<typename _Tp2> operator Point_<_Tp2>() const;
//! conversion to the old-style C structures轉換為舊式風格的C的結構體
operator Vec<_Tp, 2>() const;
//! dot product點積運算
_Tp dot(const Point_& pt) const;
//! dot product computed in double-precision arithmetics
double ddot(const Point_& pt) const;
//! cross-product向量積運算
double cross(const Point_& pt) const;
//! checks whether the point is inside the specified rectangle判斷當前這個點是否在指定的矩形之內
bool inside(const Rect_<_Tp>& r) const;
_Tp x, y; //< the point coordinates這是這個Point類模板最重要的兩個資訊------Point點的x和y座標
};
typedef Point_<int> Point2i;// 二維單精度浮點型點類
typedef Point_<float> Point2f;// 二維雙精度浮點型點類
typedef Point_<double> Point2d;// 維整形點類
typedef Point2i Point;
點與點、點與數值可以進行直接運算,如下:
pt1 = pt2 + pt3;
pt1 = pt2 - pt3;
pt1 = pt2 * a;
pt1 = a * pt2;
pt1 += pt2;
pt1 -= pt2;
pt1 *= a;
double value = norm(pt); // L2 norm
pt1 == pt2;
pt1 != pt2;
例項:
Point2f a(0.3f, 0.f), b(0.f, 0.4f);
Point pt = (a + b)*10.f;
cout << pt.x << ", " << pt.y << endl; // 3,4
Point3_
Point3_和Point2_類似,是三維點。
/*【Point_原始碼】*****************************************************************
* @Version:OpenCV 3.0.0
* @原始碼路徑:…\opencv\sources\modules\core\include\opencv2\core\types.hpp
* @起始行數:218行
********************************************************************************/
template<typename _Tp> class Point3_
{
public:
typedef _Tp value_type;
// various constructors各種建構函式
Point3_();
Point3_(_Tp _x, _Tp _y, _Tp _z);
Point3_(const Point3_& pt);
explicit Point3_(const Point_<_Tp>& pt);
Point3_(const Vec<_Tp, 3>& v);
Point3_& operator = (const Point3_& pt);
//! conversion to another data type轉換為另一種型別
template<typename _Tp2> operator Point3_<_Tp2>() const;
//! conversion to cv::Vec<>
operator Vec<_Tp, 3>() const;
//! dot product點積運算
_Tp dot(const Point3_& pt) const;
//! dot product computed in double-precision arithmetics
double ddot(const Point3_& pt) const;
//! cross product of the 2 3D points
Point3_ cross(const Point3_& pt) const;
_Tp x, y, z; //< the point coordinates
};
typedef Point3_<int> Point3i;
typedef Point3_<float> Point3f;
typedef Point3_<double> Point3d;
1.3.2尺寸的表示:Size類
常用形式:Size(5,5);//表示長寬都為5。
二維尺寸,用來描述矩陣大小、矩形區域大小、寬高等。型別有,
/*【Size_原始碼】*****************************************************************
* @Version:OpenCV 3.0.0
* @原始碼路徑:…\opencv\sources\modules\core\include\opencv2\core\types.hpp
* @起始行數:284行
********************************************************************************/
template<typename _Tp> class Size_
{
public:
typedef _Tp value_type;
//! various constructors
Size_();
Size_(_Tp _width, _Tp _height);
Size_(const Size_& sz);
Size_(const Point_<_Tp>& pt);
Size_& operator = (const Size_& sz);
//! the area (width*height)
_Tp area() const;
//! conversion of another data type.
template<typename _Tp2> operator Size_<_Tp2>() const;
_Tp width, height; // the width and the height
};
typedef Size_<int> Size2i;
typedef Size_<float> Size2f;
typedef Size_<double> Size2d;
typedef Size2i Size;
同Point2_一樣,可以通過二維資料型別CvSize、CvSize2D32f等構造,成員變數有 width和height;還能計算面積area()=height*width;
1.3.3矩形的表示:Rect類
二維矩形,有效型別為 typedef Rect_ Rect; 該資料型別由頂點x,y和尺寸width、height定義。
/*【Rect _原始碼】*****************************************************************
* @Version:OpenCV 3.0.0
* @原始碼路徑:…\opencv\sources\modules\core\include\opencv2\core\types.hpp
* @起始行數:374行
********************************************************************************/
template<typename _Tp> class Rect_
{
public:
typedef _Tp value_type;
//! various constructors
Rect_();
Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);
Rect_(const Rect_& r);
Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz);
Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2);
Rect_& operator = ( const Rect_& r );
//! the top-left corner
Point_<_Tp> tl() const;
//! the bottom-right corner
Point_<_Tp> br() const;
//! size (width, height) of the rectangle
Size_<_Tp> size() const;
//! area (width*height) of the rectangle
_Tp area() const;
//! conversion to another data type
template<typename _Tp2> operator Rect_<_Tp2>() const;
//! checks whether the rectangle contains the point
bool contains(const Point_<_Tp>& pt) const;
_Tp x, y, width, height; //< the top-left corner, as well as width and height of the rectangle
};
typedef Rect_<int> Rect2i;
typedef Rect_<float> Rect2f;
typedef Rect_<double> Rect2d;
typedef Rect2i Rect;
建構函式比較多,Rect(x,y,width,height)和Rect_( Rect )、Rect_( CvRect )結構上是一樣的,4個引數;還可以用2個引數構造,頂點x,y這兩個可以用Point表示,height和width可以用Point或Szie表示。
成員函式可以返回左上角tl()和右下角br()的二維點座標,還有面積area(); 和Point對應,能夠檢查矩形內是是否有點contains()。
以下是rect的基本用法示例:
//如果建立一個Rect物件rect(100, 50, 50, 100),那麼rect會有以下幾個功能:
rect.area(); //返回rect的面積 5000
rect.size(); //返回rect的尺寸 [50 × 100]
rect.tl(); //返回rect的左上頂點的座標 [100, 50]
rect.br(); //返回rect的右下頂點的座標 [150, 150]
rect.width(); //返回rect的寬度 50
rect.height(); //返回rect的高度 100
rect.contains(Point(x, y)); //返回布林變數,判斷rect是否包含Point(x, y)點
//還可以求兩個矩形的交集和並集
rect = rect1 & rect2;
rect = rect1 | rect2;
//還可以對矩形進行平移和縮放
rect = rect + Point(-100, 100); //平移,也就是左上頂點的x座標-100,y座標+100
rect = rect + Size(-100, 100); //縮放,左上頂點不變,寬度-100,高度+100
//還可以對矩形進行對比,返回布林變數
rect1 == rect2;
rect1 != rect2;
//OpenCV裡貌似沒有判斷rect1是否在rect2裡面的功能,所以自己寫一個吧
bool isInside(Rect rect1, Rect rect2)
{
return (rect1 == (rect1&rect2));
}
//OpenCV貌似也沒有獲取矩形中心點的功能,還是自己寫一個
Point getCenterPoint(Rect rect)
{
Point cpt;
cpt.x = rect.x + cvRound(rect.width/2.0);
cpt.y = rect.y + cvRound(rect.height/2.0);
return cpt;
}
//圍繞矩形中心縮放
Rect rectCenterScale(Rect rect, Size size)
{
rect = rect + size;
Point pt;
pt.x = cvRound(size.width/2.0);
pt.y = cvRound(size.height/2.0);
return (rect-pt);
}
1.3.4標量的表示:Scalar類
標量,由Vec<_Tp, 4>模板類派生。在矩陣運算中,特別是影象矩陣涉及到多通道的運算中,對某個畫素素的取值和賦值都是多個通道同時進行,因此Vec和Scalar的優勢體現明顯,被廣泛用於影象畫素賦值。
/*【Scalar_原始碼】*****************************************************************
* @Version:OpenCV 3.0.0
* @原始碼路徑:…\opencv\sources\modules\core\include\opencv2\core\types.hpp
* @起始行數:570行
********************************************************************************/
template<typename _Tp> class Scalar_ : public Vec<_Tp, 4>
{
public:
//! various constructors
Scalar_();
Scalar_(_Tp v0, _Tp v1, _Tp v2=0, _Tp v3=0);
相關推薦
【第二部分-影象處理】第1章 Opencv影象處理入門
1.1基礎影象容器Mat類的使用
1.1.1基本影象容器Mat
在正式講解OpenCV之前,首先要介紹的就是Mat。
在2001年剛剛出現的時候,OpenCV基於 C 語言介面而建。為了在記憶體(memory)中存放影象,當時採用名為 IplImage
【機器學習系列文章】第1部分:為什麼機器學習很重要 ?
目錄
路線圖
關於作者
簡單,簡單的解釋,附有數學,程式碼和現實世界的例子。
這個系列是一本完整的電子書!在這裡下載。免費下載,貢獻讚賞(paypal.me/ml4h)
路線圖
第1部分:為什麼機器學習很重要。人工智慧和機器學習的大
【讀書筆記】《Python自然語言處理》第1章 語言處理與Python
1.1 語言計算:文字和詞彙
入門
nltk下載地址 使用pip安裝 >>>import nltk 檢驗是否成功。 >>>nltk.download() 選擇語料下載
使用python直譯器載入book模組中的條目 >&g
【HTTP權威指南】第1 章 HTTP 概述
1.4 狀態 1.8 網關 資源 ip 地址 gen 歷史 客戶端 1.1 HTTP——因特網的多媒體信使 ...................................................................................
【構建之法】第1章 概論
order ble 可見性 科學 行為 summary 模式 enter strong 1 公式
軟件 = 程序 + 軟件工程
軟件的質量 = 程序的質量 + 軟件工程的質量
程序 = 數據結構 + 算法
軟件企業 = 軟件 + 商業模式
2 軟件的特殊性
復
【vue大師晉級之路第一集:Vue基礎】第1章——介紹
背景
Vue (讀音 /vjuː/,類似於 view) 是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。另一方面,當與現代化的工具鏈以及各種支援類庫結合使用時,
【深入理解JVM虛擬機器】第1章 走進java
1.1Java技術體系
Sun 官方定義的Java技術體系包括以下幾個組成部分:
Java 程式設計語言
各種硬體平臺上的 Java 虛擬機器
Class 檔案格式
Java API 類庫
第三方 Java 類庫
1.1.1 Java API
按照技術所服
【機器學習實戰】第1章 機器學習基礎
第1章 機器學習基礎機器學習 概述機器學習就是把無序的資料轉換成有用的資訊。獲取海量的資料從海量資料中獲取有用的資訊我們會利用計算機來彰顯資料背後的真實含義,這才是機器學習的意義。機器學習 場景例如:
【二代示波器教程】第15章 FreeRTOS操作系統版本二代示波器實現
per lamp 轉換 length 失去 最大值 ucd 參數 state 第15章 FreeRTOS操作系統版本二代示波器實現
本章教程為大家講解FreeRTOS操作系統版本的二代示波器實現。主要講解RTOS設計框架,即各個任務實現的功能,任務間的通信方案選擇,
【RL-TCPnet網路教程】第40章 RL-TCPnet之TFTP客戶端(精簡版)
第40章 RL-TCPnet之TFTP客戶端
本章節為大家講解RL-TCPnet的TFTP客戶端應用,學習本章節前,務必要優先學習第38章的TFTP基礎知識。有了這些基礎知識之後,再搞本章節會有事半功倍的效果。
本章教程含STM32F407開發板和STM32F429開發板。
40.1 初學者重要提
【RL-TCPnet網路教程】第36章 RL-TCPnet之FTP伺服器
第36章 RL-TCPnet之FTP伺服器
本章節為大家講解RL-TCPnet的FTP伺服器應用,學習本章節前,務必要優先學習第35章的FTP基礎知識。有了這些基礎知識之後,再搞本章節會有事半功倍的效果。
本章教程含STM32F407開發板和STM32F429開
【計算機組成與設計】第五章 大容量和高速度:開發儲存器層次結構
#5.1引言
區域性性原理表明了在任何時間內,程式訪問的只是它地址空間內相對較小的一部分。以下是兩種不同的區域性性:
時間區域性性(temporal locality):如果一個數據被訪問,那麼在
《用Python進行自然語言處理》第 1 章 語言處理與 Python
1. 將簡單的程式與大量的文字結合起來,我們能實現什麼?2. 我們如何能自動提取概括文字風格和內容的關鍵詞和短語?3. Python 程式語言為上述工作提供了哪些工具和技術?4. 自然語言處理中的有哪些有趣的挑戰?1.1 語言計算:文字和單詞python入門NLTK 入門fr
《OpenCV影象處理》 第七章 加速影象處理
OpenCV庫包括了對OpenCL和CUDA GPU架構的支援。
OpenCL(Open Computing Language):開放計算語言,可以附加在主機處理器的CPU或GPU上執行。
OpenCV有一個新的統一資料結構UMat,用於在必要和可能的時候,負責將資料傳輸
【Java程式設計思想筆記】第六章-訪問許可權控制
要學會把變動的程式碼與保持不變的程式碼區分開來。
如果有必要,你儘可能將一切方法都定為private。
非public類在其它包中是訪問不到的。
所有預設包的類都是屬於同一個包,儘管它們在不同的資料夾下面。
private,只允許本類所有物件可訪問,其他任何類
【西瓜書學習筆記】第3章:線性模型
課程前言:
arg max的引數是函式最大化的某個函式的域的點,與全域性最大值相比引數函式的最大輸出,arg max指的是函式輸出儘可能大的輸入或引數
閉式解:
給出任意自變數,就可以求出因變數
最小二乘法:
通過最小化誤差的平方和尋找資料的最佳函式匹配
【Java程式設計思想筆記】第四章-流程控制
Foreach迴圈可用於陣列,以及實現了java.util.Iterator介面的物件。
public interface Iterable<T> {
Iterator<T> iterator();
}
如果在返回v
【Redis設計與實現】第3章 連結串列
開發十年,就只剩下這套架構體系了!
>>>
【Redis設計與實現】第2章 簡單動態字串
開發十年,就只剩下這套架構體系了!
>>>
【軟件構造】第七章第二節 錯誤與異常處理
throw 之間 IE 程序猿 數組越界 它的 extends 希望 nds 第七章第二節 錯誤與異常處理
本節關註:Java中錯誤和異常處理的典 型技術——把原理落實到代碼上!
Outline:
Java中的錯誤和異常(java.lang.throwable)
異常