![]() |
OpenCV 4.12.0
開源計算機視覺
|
下個教程: 如何使用OpenCV掃描影像、查詢表和時間測量
| 原始作者 | Bernát Gábor |
| 相容性 | OpenCV >= 3.0 |
我們有多種方法從現實世界獲取數字影像:數碼相機、掃描器、計算機斷層掃描和磁共振成像等。在每種情況下,我們(人類)看到的是影像。然而,當將其轉換到我們的數字裝置時,我們記錄的是影像中每個點的數值。
例如,在上圖中,您可以看到汽車的後視鏡不過是一個包含畫素點所有強度值的矩陣。我們獲取和儲存畫素值的方式可能根據我們的需求而異,但最終計算機世界中的所有影像都可以簡化為數值矩陣以及描述矩陣本身的其它資訊。OpenCV 是一個計算機視覺庫,其主要焦點是處理和操作這些資訊。因此,您需要熟悉的第一件事就是OpenCV如何儲存和處理影像。
OpenCV自2001年開始出現。那時,該庫是圍繞C介面構建的,為了在記憶體中儲存影像,他們使用了一個名為IplImage的C結構。這是您在大多數舊教程和教育材料中會看到的。這樣做的問題是它帶來了C語言的所有缺點。最大的問題是手動記憶體管理。它建立在使用者負責處理記憶體分配和釋放的假設上。雖然這對於較小的程式不是問題,但一旦您的程式碼庫增長,處理所有這些將比專注於解決開發目標更困難。
幸運的是,C++出現了並引入了類的概念,透過自動記憶體管理(或多或少)讓使用者更容易使用。好訊息是C++與C完全相容,因此更改不會引起相容性問題。因此,OpenCV 2.0引入了一個新的C++介面,它提供了一種新的操作方式,這意味著您無需處理記憶體管理,使您的程式碼更簡潔(編寫更少,實現更多)。C++介面的主要缺點是,目前許多嵌入式開發系統只支援C。因此,除非您正在開發嵌入式平臺,否則沒有必要使用舊方法(除非您是自虐型程式設計師並自找麻煩)。
關於Mat,您需要知道的第一件事是,您不再需要手動分配和釋放其記憶體。雖然這樣做仍然是一種可能性,但大多數OpenCV函式將自動分配其輸出資料。一個額外的好處是,如果您傳遞一個已存在的Mat物件,並且該物件已經分配了矩陣所需的空間,那麼該空間將被重用。換句話說,我們始終只使用完成任務所需的記憶體量。
Mat基本上是一個包含兩個資料部分的類:矩陣頭(包含矩陣大小、儲存方法、矩陣儲存地址等資訊)和指向包含畫素值的矩陣的指標(根據所選儲存方法,具有任何維度)。矩陣頭的大小是常量,但矩陣本身的大小可能因影像而異,通常會大幾個數量級。
OpenCV是一個影像處理庫。它包含大量的影像處理函式。為了解決計算挑戰,大多數情況下您最終會使用庫中的多個函式。因此,將影像傳遞給函式是一種常見做法。我們不應忘記,我們談論的是影像處理演算法,它們往往計算量很大。我們最不想做的事情是由於不必要的複製可能大型的影像而進一步降低程式的執行速度。
為了解決這個問題,OpenCV使用了一個引用計數系統。其思想是每個Mat物件都有自己的頭部,但一個矩陣可以透過讓其矩陣指標指向同一地址而在兩個Mat物件之間共享。此外,複製運算子只會複製頭部和大矩陣的指標,而不會複製資料本身。
所有上述物件最終都指向同一個單一資料矩陣,並且使用其中任何一個進行修改都將影響所有其他物件。實際上,不同的物件只是提供了訪問相同底層資料的不同方法。然而,它們的頭部是不同的。真正有趣的部分是,您可以建立只引用完整資料子部分的頭部。例如,要在影像中建立感興趣區域(ROI),您只需建立一個帶有新邊界的新頭部
現在您可能會問——如果矩陣本身可以屬於多個Mat物件,那麼當它不再需要時,誰負責清理它呢?簡短的回答是:最後一個使用它的物件。這是透過引用計數機制處理的。每當有人複製一個Mat物件的頭部時,矩陣的計數器就會增加。每當一個頭部被清理時,這個計數器就會減少。當計數器達到零時,矩陣就會被釋放。有時您也會想複製矩陣本身,所以OpenCV提供了cv::Mat::clone()和cv::Mat::copyTo()函式。
現在修改F或G將不會影響A的頭部所指向的矩陣。您需要記住的是:
這關係到你如何儲存畫素值。你可以選擇顏色空間和使用的資料型別。顏色空間指的是我們如何組合顏色分量以編碼給定的顏色。最簡單的是灰度,我們可用的顏色是黑色和白色。它們的組合使我們能夠建立許多灰色陰影。
對於彩色方式,我們有更多方法可供選擇。每種方法都將其分解為三到四個基本分量,我們可以利用這些分量的組合來建立其他顏色。最流行的是RGB,主要是因為這也是我們眼睛構建顏色的方式。它的基本顏色是紅色、綠色和藍色。為了編碼顏色的透明度,有時會新增第四個元素,即阿爾法(A)。
然而,還有許多其他顏色系統,每種都有其自身的優點
每個組成部分都有其有效的域。這導致了所使用的資料型別。我們儲存一個分量的方式決定了我們對其域的控制。可能最小的資料型別是char,這意味著一個位元組或8位。它可以是無符號的(因此可以儲存0到255的值)或有符號的(-127到+127的值)。儘管在三個分量(如RGB)的情況下,這種寬度已經可以表示1600萬種可能的顏色,但我們可以透過對每個分量使用float(4位元組=32位)或double(8位元組=64位)資料型別來獲得更精細的控制。然而,請記住,增加分量的大小也會增加記憶體中整個圖片的大小。
在影像入門教程中,您已經學會了如何使用cv::imwrite()函式將矩陣寫入影像檔案。然而,為了除錯目的,檢視實際值更為方便。您可以使用Mat的<<運算子來完成此操作。請注意,這僅適用於二維矩陣。
雖然Mat作為影像容器非常好用,但它也是一個通用的矩陣類。因此,可以建立和操作多維矩陣。您可以透過多種方式建立Mat物件
cv::Mat::Mat 建構函式
對於二維和多通道影像,我們首先定義它們的大小:行和列計數。
然後我們需要指定用於儲存元素的資料型別和每個矩陣點的通道數。為此,我們有根據以下約定構建的多種定義
例如,CV_8UC3意味著我們使用8位長的無符號字元型別,每個畫素有三個這樣的通道來組成三個通道。預定義了最多四個通道的型別。cv::Scalar是四元素短向量。指定它,您可以用自定義值初始化所有矩陣點。如果需要更多,您可以使用上方宏建立型別,像下面這樣在括號中設定通道數。
使用 C/C++ 陣列並透過建構函式初始化
上面的例子展示瞭如何建立超過兩維的矩陣。指定其維度,然後傳入一個包含每個維度大小的指標,其餘保持不變。
您無法使用此建構函式初始化矩陣值。它只會在新大小不適合舊記憶體時重新分配其矩陣資料記憶體。
MATLAB 風格的初始化器: cv::Mat::zeros , cv::Mat::ones , cv::Mat::eye 。指定大小和要使用的資料型別
對於小矩陣,您可以使用逗號分隔的初始化器或初始化列表(後一種情況需要C++11支援)
為現有Mat物件建立一個新頭,並對其進行cv::Mat::clone或cv::Mat::copyTo操作。
在上面的例子中,您可以看到預設的格式化選項。然而,OpenCV允許您格式化矩陣輸出
OpenCV 還透過 << 運算子支援輸出其他常見的 OpenCV 資料結構
這裡的大部分示例都已包含在一個小型控制檯應用程式中。您可以從這裡或cpp示例的核心部分下載。
您還可以在YouTube上找到此內容的快速影片演示。