OpenCV教程這里提到的所有源代碼都是作為OpenCV常規(guī)版本的一部分提供的,因此請在開始復制和粘貼代碼之前檢查。以下教程列表將自動從位于我們的GIT存儲庫中的reST文件生成。
和往常一樣,我們很樂意聽到您的意見,并收到您對任何教程的貢獻。
該文檔的其他章節(jié)描述了每個模塊的功能。但首先,請確保熟悉庫中使用的常見API概念。
所有OpenCV類和函數(shù)都放在cv命名空間中。因此,要從代碼訪問此功能,請使用cv :: specifier或使用命名空間cv; 指示:
#include“opencv2 / core.hpp”
...
cv :: Mat H = cv :: findHomography(points1,points2,CV_RANSAC,5);
...
or:
#include "opencv2/core.hpp"
using namespace cv;
...
Mat H = findHomography(points1, points2, CV_RANSAC, 5 );
...
當前或未來的OpenCV外部名稱中的一些可能與STL或其他庫沖突。在這種情況下,使用顯式命名空間說明符來解決名稱沖突:
Mat a(100, 100, CV_32F);
randu(a, Scalar::all(1), Scalar::all(std::rand()));
cv::log(a, a);
a /= std::log(2.);
OpenCV自動處理所有內(nèi)存。
首先,函數(shù)和方法使用的std :: vector,Mat和其他數(shù)據(jù)結構具有析構函數(shù),在需要時釋放底層內(nèi)存緩沖區(qū)。這意味著析構函數(shù)并不總是像Mat一樣釋放緩沖區(qū)。他們考慮到可能的數(shù)據(jù)共享。析構函數(shù)遞減與矩陣數(shù)據(jù)緩沖器相關聯(lián)的引用計數(shù)器。當且僅當引用計數(shù)器達到零時,即當沒有其他結構指向相同的緩沖區(qū)時,緩沖區(qū)被解除分配。類似地,當Mat實例被復制時,實際數(shù)據(jù)沒有被真正復制。相反,引用計數(shù)器增加以記住相同數(shù)據(jù)的另一個所有者。還有Mat :: clone方法可以創(chuàng)建矩陣數(shù)據(jù)的完整副本。見下面的例子:
// create a big 8Mb matrix
Mat A(1000, 1000, CV_64F);
// create another header for the same matrix;
// this is an instant operation, regardless of the matrix size.
Mat B = A;
// create another header for the 3-rd row of A; no data is copied either
Mat C = B.row(3);
// now create a separate copy of the matrix
Mat D = B.clone();
// copy the 5-th row of B to C, that is, copy the 5-th row of A
// to the 3-rd row of A.
B.row(5).copyTo(C);
// now let A and D share the data; after that the modified version
// of A is still referenced by B and C.
A = D;
// now make B an empty matrix (which references no memory buffers),
// but the modified version of A will still be referenced by C,
// despite that C is just a single row of the original A
B.release();
// finally, make a full copy of C. As a result, the big modified
// matrix will be deallocated, since it is not referenced by anyone
C = C.clone();
你看到Mat和其他基本結構的使用很簡單。但是,如果不考慮自動內(nèi)存管理,高級類甚至用戶數(shù)據(jù)類型的創(chuàng)建呢?對于他們來說,OpenCV提供了類似于C ++ 11中的std :: shared_ptr的Ptr模板類。所以,而不是使用簡單的指針:
T * ptr = new T(...);
您可以使用:
Ptr <T> ptr(new T(...));
or:
Ptr <T> ptr = makePtr <T>(...);
Ptr <T>封裝了一個指向T實例的指針和與該指針關聯(lián)的引用計數(shù)器。有關詳細信息,請參閱Ptr說明。
OpenCV自動釋放內(nèi)存,并且大部分時間自動為輸出功能參數(shù)分配內(nèi)存。因此,如果一個函數(shù)有一個或多個輸入數(shù)組(cv :: Mat實例)和一些輸出數(shù)組,輸出數(shù)組將被自動分配或重新分配。輸出數(shù)組的大小和類型根據(jù)輸入數(shù)組的大小和類型確定。如果需要,函數(shù)需要額外的參數(shù)來幫助找出輸出數(shù)組屬性。
例:
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main(int, char**)
{
VideoCapture cap(0);
if(!cap.isOpened()) return -1;
Mat frame, edges;
namedWindow("edges",1);
for(;;)
{
cap >> frame;
cvtColor(frame, edges, COLOR_BGR2GRAY);
GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
Canny(edges, edges, 0, 30, 3);
imshow("edges", edges);
if(waitKey(30) >= 0) break;
}
return 0;
}
由于視頻幀分辨率和比特深度對于視頻采集模塊是已知的,所以陣列幀由>>運算符自動分配。陣列邊緣由cvtColor函數(shù)自動分配。它具有與輸入數(shù)組相同的大小和位深度。通道的數(shù)量為1,因為顏色轉(zhuǎn)換代碼COLOR_BGR2GRAY被傳遞,這意味著要進行灰度轉(zhuǎn)換的顏色。請注意,幀和邊緣在循環(huán)體的第一次執(zhí)行期間僅被分配一次,因為所有下一個視頻幀具有相同的分辨率。如果您以某種方式更改視頻分辨率,則會自動重新分配陣列。
該技術的關鍵組件是Mat :: create方法。它需要所需的數(shù)組大小和類型。如果數(shù)組已經(jīng)具有指定的大小和類型,該方法什么也不做。否則,它釋放先前分配的數(shù)據(jù)(如果有的話)(這部分涉及遞減引用計數(shù)器并將其與零進行比較),然后分配所需大小的新緩沖區(qū)。大多數(shù)函數(shù)調(diào)用每個輸出數(shù)組的Mat :: create方法,因此實現(xiàn)了自動輸出數(shù)據(jù)分配。
這個方案的一些顯著的例外是cv :: mixChannels,cv :: RNG :: fill和一些其他的功能和方法。他們不能分配輸出數(shù)組,所以你必須提前做到這一點。
作為計算機視覺庫,OpenCV對圖像像素進行了大量的處理,圖像像素通常以每個通道緊湊的8位或16位形式編碼,因此具有有限的范圍。此外,對于圖像的某些操作,如顏色空間轉(zhuǎn)換,亮度/對比度調(diào)整,銳化,復雜插值(雙立方體,Lanczos)可以在可用范圍之外產(chǎn)生值。如果只存儲結果中最低的8(16)位,則會導致視覺偽影,并可能影響進一步的圖像分析。為了解決這個問題,使用了所謂的飽和算術。例如,要將r操作的結果存儲到8位圖像,您可以在0..255范圍內(nèi)找到最接近的值:
類似的規(guī)則適用于8位有符號的16位有符號和無符號類型。這個語義在庫中隨處可見。在C ++代碼中,使用類似于標準C ++轉(zhuǎn)換操作的saturate_cast <>函數(shù)完成。見下面提供的公式的實現(xiàn):
(y,x)= saturate_cast <uchar>(r);
其中cv :: uchar是一個OpenCV 8位無符號整數(shù)類型。在優(yōu)化的SIMD代碼中,使用諸如paddusb,packuswb等SSE2指令。它們幫助實現(xiàn)與C ++代碼完全相同的行為。
模板是C ++的一個很好的功能,可以實現(xiàn)非常強大,高效且安全的數(shù)據(jù)結構和算法。然而,廣泛使用模板可能會大大增加編譯時間和代碼大小。此外,當僅使用模板時,難以分離接口和實現(xiàn)。這對于基本算法來說可能是很好的,但對于單個算法可能跨越數(shù)千行代碼的計算機視覺庫而言并不好。因此,為了簡化其他語言的綁定,如Python,Java,沒有模板或模板功能有限的Matlab,目前的OpenCV實現(xiàn)是基于模板上的多態(tài)和運行時調(diào)度。在那些運行時調(diào)度太慢(像像素訪問操作符)的地方,不可能(通用的Ptr <>實現(xiàn)),或只是非常不方便(saturate_cast <>()),目前的實現(xiàn)引入了小模板類,方法和函數(shù)。目前OpenCV版本中的其他任何地方,模板的使用受到限制。
因此,庫可以操作的原始數(shù)據(jù)類型有限。也就是說,數(shù)組元素應該具有以下類型之一:
對于這些基本類型,應用以下計算:
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };
可以使用以下選項指定多通道(n通道)類型:
例:
Mat mtx(3, 3, CV_32F); // make a 3x3 floating-point matrix
Mat cmtx(10, 1, CV_64FC2); // make a 10x1 2-channel floating-point
// matrix (10-element complex vector)
Mat img(Size(1920, 1080), CV_8UC3); // make a 3-channel (color) image
// of 1920 columns and 1080 rows.
Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1)); // make a 1-channel image of
// the same size and same
// channel type as img
使用OpenCV無法構建或處理具有更復雜元素的數(shù)組。此外,每個函數(shù)或方法只能處理所有可能的數(shù)組類型的子集。通常,算法越復雜,支持的格式子集越小。見下文典型的例子:
每個功能的支持類型的子集已經(jīng)根據(jù)實際需要定義,并且可以在將來根據(jù)用戶請求進行擴展。
許多OpenCV功能處理密集的二維或多維數(shù)字數(shù)組。通常,這些函數(shù)以cppMat為參數(shù),但在某些情況下,使用std :: vector <>(例如,對于一個點集合)或Matx <>(對于3x3單應性矩陣等)來說更方便。為了避免API中的許多重復,引入了特殊的“代理”類?;镜摹按怼鳖愂荌nputArray。它用于在函數(shù)輸入上傳遞只讀數(shù)組。派生自InputArray類的OutputArray用于指定函數(shù)的輸出數(shù)組。通常,您不應該關心這些中間類型(而不應該明確地聲明這些類型的變量) - 它們都將自動工作。你可以假設,而不是InputArray / OutputArray,你可以隨時使用Mat,std :: vector <>,Matx <>,Vec <>或Scalar。當一個函數(shù)有一個可選的輸入或輸出數(shù)組,并且你沒有或不想要一個,通過cv :: noArray()。
OpenCV使用異常來表示關鍵錯誤。當輸入數(shù)據(jù)具有正確的格式并且屬于指定的值范圍時,但是由于某種原因(例如,優(yōu)化算法沒有收斂),算法無法成功,它返回一個特殊的錯誤代碼(通常只是一個布爾變量) 。
例外可以是cv :: Exception類或其衍生物的實例。反過來,cv :: Exception是std :: exception的派生詞。因此,可以使用其他標準C ++庫組件在代碼中正確處理。
通常會使用CV_Error(errcode,description)宏或其類似printf的CV_Error_(errcode,printf-spec,(printf-args))變體或使用CV_Assert(condition)宏來檢查該條件和當不滿意時拋出異常。對于性能關鍵的代碼,CV_DbgAssert(條件)僅保留在調(diào)試配置中。由于自動內(nèi)存管理,所有中間緩沖區(qū)會在突發(fā)錯誤的情況下自動釋放。如果需要,您只需要添加一個try語句即可捕獲異常:
try
{
... // call OpenCV
}
catch( cv::Exception& e )
{
const char* err_msg = e.what();
std::cout << "exception caught: " << err_msg << std::endl;
}
目前的OpenCV實現(xiàn)完全可重新進入。也就是說,可以從不同的線程調(diào)用類實例的相同函數(shù),相同的常量方法或不同類實例的相同的非常量方法。另外,同樣的cv :: mat可以用于不同的線程,因為引用計數(shù)操作使用特定于架構的原子指令。
更多建議: