天天看點

OpenCV基礎介紹

opencv 英文文檔位址 :  docs.opencv.org

opencv (open source computer vision library: http://opencv.org) 是一個使用 bsd 許可證的開源庫,包含數百個計算機視覺算法。此文檔詳細的描述了 opencv 2.x api,這主要是 c++ api,相對于

opencv 1.x api 的 c api。

opencv 使用子產品化的結構,這表明其包含很多共享或者靜态庫。opencv 提供如下子產品:

core - 這是一個定義了基本資料結構的緊湊子產品,包含大量被其他子產品調用的多元的 mat 和其他基本函數。

imgproc - 這是一個圖像處理子產品,包含線性和非線性的圖像過濾,幾何圖形轉換 (大小調整, 仿射和透視扭曲, 通用的基于表的重新映射), 色彩空間轉換以及直方圖等等。

video - 這是一個視訊分析子產品,包含運動評估、背景提取以及對象跟蹤算法。

calib3d - 基本的多視圖幾何算法,實作單個和立體的攝像頭校準,對象構成的評估、立體相關算法和一些 3d 構造算法。

features2d - 突出特征檢測、描述以及描述器的比對。

objdetect - 對象檢測以及預定義類的檢測,例如人臉、眼睛、杯子、人物、汽車等等。

highgui - 一個易用的視訊捕獲、圖像和視訊編碼接口,同時提供簡單的圖形化界面。

gpu - 來自不同 opencv 子產品的 gpu 加速算法。

... 其他一些助手子產品,例如 flann 和 google 測試封裝、python 包等等。

該文檔的其他章節描述了每個子產品的功能。但首先,我們需要對一些貫通整個庫的通用 api 熟悉。

所有 opencv 的類和函數被放到了 cv 這個命名空間。是以為了在代碼中通路這些功能,需要使用 cv:: 說明符或者使用 namespace cv; 指令:

或者

目前 opencv 提供的一些命名可能會跟 stl 或者其他庫的命名沖突,可以使用顯式的指定命名空間來解決這個沖突問題,例如:

opencv 的記憶體處理是完全自動化的。

首先 std::vector, mat, 以及其他資料結構提供了析構函數,可在需要的時候釋放底層占用記憶體。這意味着好比是 mat 這個資料結構而言,析構函數并不總是會釋放記憶體,此舉是為了便于資料共享。析構函數隻是減少了所關聯對象的引用計數器而已。如果引用計數器數值為 0 的時候對象才會被釋放,因為再沒有其他結構引用到該資料。同樣的,當一個 mat 執行個體被拷貝,實際上并沒有發送拷貝資料的操作,隻是引用計數值增1來記錄使用關系。當然

mat 還提供了 mat::clone 方法來強制進行資料拷貝。示例如下:

你會發現 mat 和其他基礎結構的使用時很簡單的。但是其他一些進階類和使用者自行建立的資料類型如何呢?是否也可以實作自動的記憶體管理呢?對于這些來說 opencv 提供了 ptr<> 模闆類,類似 c++ tr1 的 std::shared_ptr . 是以使用指針來替換的方法如下:

你可以使用:

也就是說 ptr<t> ptr 封裝了一個指向 t 執行個體的指針和關聯該指針的引用計數,詳情請看 ptr 的較長的描述。

opencv 會自動釋放記憶體,就如同大多數時候為輸出函數的參數自動配置設定記憶體一樣。是以,如果一個函數有一個或者多個輸入的數組 (cv::mat 執行個體) 和一些輸出數組,輸出的數組會實作自動的記憶體配置設定和釋放。輸出數組的大小和類型會根據輸入數組的大小和類型來自動識别。如果需要的話函數可以提供額外的參數來幫助設定輸出數組的屬性。

示例代碼:

一旦視訊捕獲子產品解析到視訊幀以及位深度,那麼幀數組會自動通過 >> 操作符進行配置設定。數組的邊界是通過 cvtcolor 函數自動配置設定的。它跟輸入的數組具有相同的大小和位深度。因為傳遞了 cv_bgr2gray 參數給色彩轉換代碼,是以通道數是 1。需要注意的是幀和邊界隻在首次執行時候配置設定一次,是以緊接着的所有視訊幀都具有相同的分辨率。如果你以某種方式改變了視訊分辨率,那麼數組就會自動重新配置設定。

此技術的關鍵元件是 mat::create 方法,需要指定數組的大小和類型。如果數組已經有指定的大小和類型了,那麼該方法什麼都不做。否則它會釋放之前已配置設定的資料,如果有的話(這一部分設計到引用計數減一并判斷是否為0)然後配置設定一個新的緩沖區以滿足資料要求。大多數函數為每個輸出的數組調用 mat::create 方法來建立,是以實作了輸出資料配置設定的自動化。

一些值得注意的例外是 cv::mixchannels 和 cv::rng::fill ,同時還有其他的函數和方法。他們不會配置設定輸出數組,你必須在調用之前進行配置設定。

作為一個計算機視覺庫,opencv 處理大量的圖像像素,這些都是使用緊縮的每通道、形式或者值範圍的 8或者16位的像素。此外圖像上的特定操作,例如色彩空間轉換、亮度對比度調整、銳化以及其他複雜的操作 (bi-cubic, lanczos) 會産生超出範圍的值。如果你隻是存儲最低的 8或者16位值,這會導緻視覺上的僞影,進而影響進一步的圖像分析。為了解決這個問題,我們可以使用所謂的“飽和算法”。例如,為了存儲操作的結果

r 到一個 8 位的圖像,你可以查找 0 到 255 中最ji,can produce values out of the available range. if you just store the lowest 8 (16) bits of the result, this results in visual artifacts and may affect a further image analysis. to solve this problem, the so-called saturation arithmetics

is used. for example, to store r, the result of an operation, to an 8-bit image, you find the nearest value within the 0..255 range:

OpenCV基礎介紹

帶符号的 8 位、16類型以及不帶符号的類型也使用類似的規則,在整個庫的 c++ 代碼都使用這個語義規則。可使用 saturate_cast<> 函數來實作類似标準 c++ 的 cast 操作。下面一行代碼實作了上圖中的計算公式:

因為 cv::uchar 是一個 opencv 8-bit 無符号整數值,是以在優化的 simd 代碼,例如 sse2 指令:paddusb, packuswb 等等就會被使用到。這實作了與 c++ 代碼類似的相同行為。

注意

當結果是 32位整數時,是無法用到 saturation 飽和的(譯者注:此句該做何解?)。

模闆是 c++ 一個非常棒的特性之一,可以實作非常強大、高效以及安全的資料結構和算法。但是大量使用模闆會戲劇性的增加編譯時間以及代碼的體積。除此之外,很難分離一個模闆的接口以及相應的實作。這用來做一些基本的算法是挺好的,但是對于計算機視覺庫這樣可能包含數千行代碼的複雜工作就不太合适。因為這個同時需要提供其他語言的支援版本,而像 python、java、matlab 等程式設計語言并沒有模闆的概念,就會導緻功能受限。目前的

opencv 實作是基于多态以及模闆之上的運作時排程。這會導緻運作時排程變得非常慢(例如像素通路操作)以及無法運作(泛型 ptr<> 實作),以及可能非常不友善(saturate_cast<>()),是以目前實作使用了小的模闆類、方法和函數。在目前的 opencv 版本中模闆的使用都是受限的。

是以,對于一些可操作的基本類型來說是有一些固定的限制。也就是說,數組元素必須是如下羅列的類型中的其中一個:

8-bit 無符号整數 (uchar)

8-bit 有符号整數 (schar)

16-bit 無符号整數 (ushort)

16-bit 有符号整數 (short)

32-bit 有符号整數 (int)

32-bit 浮點數(float)

64-bit 浮點數 (double)

一組多元素的元組,但所有元素的類型必須一緻,而且必須是上面幾種類型。數組的元素如果是元組,相當于是多通道數組,與單通道數組也跟元組類型相反,這些元素必須是标量類型。最大的通道數是定義為 cv_cn_max 的常量值,目前是 512.

對于這些基本類型,opencv 提供了如下枚舉與之對應:

多通道 (n-channel) 類型可通過如下的選項進行指定:

cv_8uc1 ... cv_64fc4 常量 (對應 1 到 4 的通道編号)

cv_8uc(n) ... cv_64fc(n) 或者 cv_maketype(cv_8u, n) ... cv_maketype(cv_64f, n) 宏,當通道數量超過 4 或者未知時

cv_32fc1 == cv_32f, cv_32fc2 == cv_32fc(2) == cv_maketype(cv_32f, 2), 以及 cv_maketype(depth, n) == (depth&7) + ((n-1)<<3). 意思是常量類型是根據深度形成的。占用了低位 3 比特,此外通道數量減1占用下一個 log2(cv_cn_max) 比特.

示例:

包含更複雜元素的數組沒法使用 opencv 進行建構和處理。此外,每個函數或者方法隻能處理任何可能數組類型的一個子集。通常,算法越複雜,支援的格式子集就越小。下面是這些限制的一些典型示例:

人臉識别算法隻支援 8 位灰階圖以及彩色圖.

線性代數函數以及大多數的機器學習算法隻支援浮點數數組.

基本的函數,例如 cv:add 支援所有類型.

色彩空間轉換函數支援 8位無符号、16位無符号以及32位浮點數類型.

每個函數所支援的類型的子集都是為了滿足實際的需要,并且可根據使用者的需求在将來進行擴充。

很多 opencv 函數密集的處理2維以及多元的數組,例如使用 cpp:class:mat 作為參數的函數,但是在某些情況下使用 std::vector<> 或者 matx<> 更友善(例如一個點陣集合)。為了避免 api 中很多重複的代碼,opencv 專門引入了一個 “proxy”類。一個基本的 “proxy”類就是 inputarray. 它被用來傳遞隻讀數組。而 inputarray

的派生類 outputarray 用來在函數中指定輸出數組。一般情況下你不需要關心這些中間類型(你也不能顯式的定義這種類型的變量),它們都是自動被處理的。你可以假設在使用 mat、std::vector<>、matx<>、vec<> 以及一些标量類型的時候會自動替換成 inputarray/outputarray 。當一個函數包含一個可選的輸入和輸出數組時,你沒有這樣的參數也不想要有,可以傳遞 cv::noarray() 。

opencv 使用異常來表示關鍵錯誤。當輸入的資料包含正确的格式以及屬于指定的值範圍,但是算法因為某種原因無法正确處理時就會傳回特定的錯誤碼(一般是一個布爾值變量)。

這些異常可以是 cv::exception 類或者派生類的執行個體。此外 cv::exception 是 std::exception 的派生類。是以可以使用标準的 c++ 庫元件來處理這些異常。

異常是通過 cv_error(errcode, description) 宏抛出來的,或者可以使用類 printf 函數風格的方法變種 cv_error_(errcode, printf-spec, (printf-args)) ,或者使用斷言宏 cv_assert(condition) 檢查各種條件,并在不滿足的情況下抛出異常。如果你對性能非常在意的話,可以使用 cv_dbgassert(condition)

方法,該方法隻在 debug 模式下有效。因為自動記憶體管理的原因,所有在發生錯誤時所産生的中間緩沖區會被自動的釋放。你隻需要在需要的時候添加 try 語句和 catch 異常即可。

目前的 opencv 版本是完全支援可重入的,這就是說相同的函數、類執行個體的 constant 方法或者是不同類執行個體的相同 non-constant 方法可以在不同的線程中調用。同時,相同的 cv::mat 也可以在不同的線程中使用,因為這裡有引用計數來實作特定架構的原子操作。