OpenCV Mat类详解和用法(官网原文)
參考文章:OpenCV Mat類詳解和用法
我馬克一下,日后更
官網原文鏈接:https://docs.opencv.org/3.2.0/d6/d6d/tutorial_mat_the_basic_image_container.html
opencv3.2.0全套教程
文章目錄
- 20211124 上面那篇文章也太長了吧,看完得幾個小時呢!這里就做些簡單筆記吧!
- 全文 OpenCV Mat類詳解和用法
- 目標
- Mat(包含矩陣大小、存儲方法、矩陣存儲地址的矩陣頭和一個指向包含像素值的矩陣的指針)
- 重點
- 存儲方法
- 顯式創建 Mat 對象
- cv::Mat::Mat構造函數
- 使用 C/C++ 數組并通過構造函數初始化
- cv::Mat::create函數:
- MATLAB 樣式初始值設定項:cv::Mat::zeros、cv::Mat::ones、cv::Mat::eye。指定要使用的大小和數據類型:
- 對于小矩陣,您可以使用逗號分隔的初始值設定項:
- 為現有的Mat對象和cv::Mat::clone或cv::Mat::copyTo創建一個新頭部(header)
- cv::randu()函數用隨機值填充矩陣
- 輸出格式
- 默認
- Python(python風格會把一個像素點的不同通道像素值組合成組顯示,比默認顯示風格更加清晰)
- 逗號分隔值(Comma separated values) (CSV)(額,,這種風格連括號都懶得給你打印了)
- Numpy(Numpy風格挺詳細,連數據類型都給你打印出來了)
- C
- 其他常用項??目的輸出
- 二維點
- 3D點
- std::vector via cv::Mat
- std::vector of points
20211124 上面那篇文章也太長了吧,看完得幾個小時呢!這里就做些簡單筆記吧!
Mat本質上是由兩個數據部分組成的類: (包含信息有矩陣的大小,用于存儲的方法,矩陣存儲的地址等) 的矩陣頭和一個指針,指向包含了像素值的矩陣(可根據選擇用于存儲的方法采用任何維度存儲數據)。矩陣頭部的大小是恒定的。然而,矩陣本身的大小因圖像的不同而不同,通常是較大的數量級。
不行還是全文拷貝過來吧,因為重點內容太多了!
全文 OpenCV Mat類詳解和用法
目標
我們有多種方法可以獲得從現實世界的數字圖像:數碼相機、掃描儀、計算機體層攝影或磁共振成像就是其中的幾種。在每種情況下我們(人類)看到了什么是圖像。但是,轉換圖像到我們的數字設備時我們的記錄是圖像的每個點的數值。
例如在上圖中你可以看到車的鏡子只是一個包含所有強度值的像素點矩陣。現在,我們如何獲取和存儲像素值可能根據最適合我們的需要而變化,最終可能減少計算機世界內的所有圖像數值矩陣和一些其他的信息的描述基質本身。OpenCV 是一個計算機視覺庫,其主要的工作是處理和操作,進一步了解這些信息。因此,你需要學習和開始熟悉它的第一件事是理解OpenCV 是如何存儲和處理圖像。
Mat(包含矩陣大小、存儲方法、矩陣存儲地址的矩陣頭和一個指向包含像素值的矩陣的指針)
OpenCV 自 2001 年出現以來。在那些日子里庫是圍繞C接口構建的。在那些日子里,他們使用名為IplImage C 的結構在內存中存儲圖像。這是您將在大多數較舊的教程和教材中看到的那個。使用這個結構的問題是將 C 語言的所有負面效果都擺到了桌面上。最大的問題是手動管理。它是建立在用戶來負責處理內存分配和解除分配的假設之上的。當程序規模較小時,這是沒有問題的,一旦代碼基開始變得越來越大它將會越來越掙扎著處理所有這一切而不是著眼于實際解決自己的開發目標。
幸運的是 c + + 出現了,并引入了類的概念,使得為用戶開辟另一條路成為可能:
自動內存管理 (或多或少)。好消息是,c + +,如果完全兼容 C 所以進行更改時沒有兼容性問題產生。因此, OpenCV其2.0 版本引入一個新的c + + 接口,通過利用這些優點將為你的工作提供新的方法。某種程度上,在其中您不需要撥弄內存管理讓你的代碼簡潔 (寫得更少,實現的更多)。C + + 接口的唯一主要缺點在于,目前許多嵌入式的開發系統支持僅 C.因此,除非您的目標是這一平臺,否則就沒有理由再使用舊的方法(除非你是個受虐狂程序員和喜歡自討苦吃)。
你需要知道的關于Mat的第一件事是你不再需要手動分配其大小并且當你不需要它的時候你不再需要手動釋放它。雖然這樣做仍然是可能的,大多數 OpenCV 函數將手動分配其輸出數據。還有一個額外的好處是如果傳遞一個已存在Mat對象,它已經為矩陣分配所需的空間,這段空間將被重用。也就是說我們在任何時候只使用與我們執行任務時所必須多的內存一樣多的內存。
Mat本質上是由兩個數據部分組成的類: (包含信息有矩陣的大小,用于存儲的方法,矩陣存儲的地址等)
的矩陣頭和一個指針,指向包含了像素值的矩陣(可根據選擇用于存儲的方法采用任何維度存儲數據)。矩陣頭部的大小是恒定的。然而,矩陣本身的大小因圖像的不同而不同,通常是較大的數量級。因此,當你在您的程序中傳遞圖像并在有些時候創建圖像副本您需要花費很大的代價生成圖像矩陣本身,而不是圖像的頭部。OpenCV
是圖像處理庫,它包含大量的圖像處理函數。若要解決的計算挑戰,最終大部分時間你會使用庫中的多個函數。由于這一原因圖像傳給庫中的函數是一種常見的做法。我們不應忘記我們正在談論往往是計算量相當大的圖像處理算法。我們想要做的最后一件事是通過制作不必要的可能很大的圖像的拷貝進一步降低您的程序的速度。
為了解決這一問題 OpenCV 使用引用計數系統。其思想是Mat的每個對象具有其自己的頭,但可能他們通過讓他們矩陣指針指向同一地址的兩個實例之間共享該矩陣。此外,拷貝運算符將只能復制矩陣頭部,也還將復制指針到大型矩陣,但不是矩陣本身。
Mat A, C; //僅創建了頭部 A = imread(argv[1], CV_LOAD_IMAGE_COLOR); //在此我們知道使用的方法(分配矩陣) Mat B(A); //使用拷貝構造函數 C = A; //賦值運算符上文中的所有對象,以相同的單個數據矩陣的結束點。他們頭不同,但是使用的其中任何一個對矩陣進行任何修改,也將影響所有其他的。在實踐中的不同對象只是提供相同的底層數據不同的訪問方法,然而,它們的頭部是不同的。真正有趣的部分是您可以創建僅指向完整數據的一小部分的頭。例如,要在圖像中創建興趣區域 ( ROI) 您只需創建一個新頭設置新邊界:
Mat D (A, Rect(10, 10, 100, 100) ); // 用矩形界定 (x1, y1)(x2, y2)?Mat E = A(Range:all(), Range(1,3)); // 用行和列來界定(這個用不了不知怎么回事?)
現在,你可能會問是否矩陣的本身可以屬于多個Mat對象在不再需要時負責清理數據。簡短的回答是:最后一個使用它的對象。這對于使用引用計數的機制,每當有人復制Mat對象的頭,矩陣的計數器被增加。每當一個頭被清除,此計數器被下調。當該計數器變為零,矩陣也就被釋放了。因為有時會仍然也要復制矩陣的本身,存在著 clone() 或 copyTo() 函數。
現在修改F或G不會影響Mat頭指向的矩陣。你需要記住的是:
重點
? 輸出圖像分配 OpenCV 功能是自動 (除非另行指定,否則)。? 用c + + OpenCV的接口就無需考慮內存釋放。? 賦值運算符和復制構造函數 (構造函數)只復制頭。? 使用clone () 或copyTo () 函數將復制的圖像的基礎矩陣。存儲方法
這是關于您如何存儲像素值。您可以選擇顏色空間和使用的數據類型。顏色空間是指我們如何組合顏色分量以對給定顏色進行編碼。最簡單的一種是灰度,我們可以使用的顏色是黑色和白色。這些的組合使我們能夠創建許多灰色陰影。
對于豐富多彩的方式,我們有更多的方法可供選擇。他們每個人都將其分解為三個或四個基本組件,我們可以使用這些組件的組合來創建其他組件。最受歡迎的是RGB,主要是因為這也是我們的眼睛構建顏色的方式。它的基色是紅色、綠色和藍色。為了編碼顏色的透明度,有時會添加第四個元素:alpha (A)。
然而,還有許多其他顏色系統,每一個都有自己的優勢:
- RGB 是最常見的,因為我們的眼睛使用類似的東西,但請記住,OpenCV 標準顯示系統使用 BGR 色彩空間(紅色和藍色通道的切換)組合顏色。
- HSV 和 HLS 將顏色分解為它們的色調、飽和度和值/亮度分量,這是我們描述顏色的更自然的方式。例如,您可能會忽略最后一個組件,使您的算法對輸入圖像的光照條件不太敏感。
- 流行的 JPEG 圖像格式使用 YCrCb(YUV)。
- CIE Lab* 是感知上均勻的顏色空間,如果您需要測量給定顏色到另一種顏色的距離,它會派上用場。
每個建筑組件都有自己的有效域。這導致使用的數據類型。我們如何存儲組件定義了我們對其域的控制。可能的最小數據類型是char,這意味著 1 個字節或 8 位。這可能是無符號的(因此可以存儲從 0 到 255 的值)或有符號的(從 -127 到 +127 的值)。雖然在三個組件的情況下,這已經提供了 1600 萬種可能的顏色來表示(如在 RGB 的情況下)我們可以通過使用浮點數(4 字節 = 32 位)或雙精度浮點型(8 字節 = 64 位)數據獲得更精細的控制每個組件的類型。盡管如此,請記住,增加組件的大小也會增加內存中整個圖片的大小。
顯式創建 Mat 對象
在加載、修改和保存圖像教程中,您已經學習了如何使用cv::imwrite()函數將矩陣寫入圖像文件。但是,出于調試目的,查看實際值要方便得多。您可以使用Mat的 << 運算符執行此操作。請注意,這僅適用于二維矩陣。
盡管Mat作為圖像容器非常有效,但它也是一個通用的矩陣類。因此,可以創建和操作多維矩陣。您可以通過多種方式創建 Mat 對象:
cv::Mat::Mat構造函數
Mat M(2,2, CV_8UC3, Scalar(0,0,255));cout << "M = " << endl << " " << M << endl << endl;
我640×360的圖片打印了好久才打印完。。。
對于二維和多通道圖像,我們首先定義它們的大小:行和列計數。
然后我們需要指定用于存儲元素的數據類型和每個矩陣點的通道數。為此,我們根據以下約定構造了多個定義:
CV_[每項的位數][有符號或無符號][類型前綴]C[通道編號]例如,CV_8UC3意味著我們使用 8 位長的 unsigned char 類型,并且每個像素具有其中的三個以形成三個通道。這是為最多四個通道編號預定義的。該cv::Scalar是四個元件短矢量。指定此項,您可以使用自定義值初始化所有矩陣點。如果您需要更多,您可以使用上面的宏創建類型,在括號中設置通道號,如下所示。
使用 C/C++ 數組并通過構造函數初始化
int sz[3] = {2,2,2};Mat L(3,sz, CV_8UC(1), Scalar::all(0));上面的例子展示了如何創建一個多于二維的矩陣。指定其維度,然后傳遞一個包含每個維度大小的指針,其余保持不變。
(這L存的是什么數據,我用std::cout打印不出來怎么回事,還報錯)
cv::Mat::create函數:
M.create(4,4, CV_8UC(2));cout << "M = "<< endl << " " << M << endl << endl;
您無法使用此構造初始化矩陣值。如果新大小不適合舊大小,它只會重新分配其矩陣數據內存。
MATLAB 樣式初始值設定項:cv::Mat::zeros、cv::Mat::ones、cv::Mat::eye。指定要使用的大小和數據類型:
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; E =[1, 0, 0, 0;0, 1, 0, 0;0, 0, 1, 0;0, 0, 0, 1]O =[1, 1;1, 1]Z =[ 0, 0, 0;0, 0, 0;0, 0, 0]對于小矩陣,您可以使用逗號分隔的初始值設定項:
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);cout << "C = " << endl << " " << C << endl << endl;為現有的Mat對象和cv::Mat::clone或cv::Mat::copyTo創建一個新頭部(header)
Mat RowClone = C.row(1).clone();cout << "RowClone = " << endl << " " << RowClone << endl << endl;cv::randu()函數用隨機值填充矩陣
筆記
您可以使用cv::randu()函數用隨機值填充矩陣。您需要為隨機值提供下限值和上限值:
輸出結果:
R =[ 91, 2, 79, 179, 52, 205;236, 8, 181, 239, 26, 248;207, 218, 45, 183, 158, 101]輸出格式
在上面的示例中,您可以看到默認格式選項。但是,OpenCV 允許您格式化矩陣輸出:
默認
cout << "R (default) = " << endl << R << endl << endl;結果同上
Python(python風格會把一個像素點的不同通道像素值組合成組顯示,比默認顯示風格更加清晰)
cout << "R (python) = " << endl << format(R, Formatter::FMT_PYTHON) << endl << endl; R (python) = [[[ 91, 2, 79], [179, 52, 205]],[[236, 8, 181], [239, 26, 248]],[[207, 218, 45], [183, 158, 101]]]逗號分隔值(Comma separated values) (CSV)(額,,這種風格連括號都懶得給你打印了)
cout << "R (csv) = " << endl << format(R, Formatter::FMT_CSV) << endl << endl; R (csv) =91, 2, 79, 179, 52, 205 236, 8, 181, 239, 26, 248 207, 218, 45, 183, 158, 101Numpy(Numpy風格挺詳細,連數據類型都給你打印出來了)
cout << "R (numpy) = " << endl << format(R, Formatter::FMT_NUMPY ) << endl << endl; R (numpy) = array([[[ 91, 2, 79], [179, 52, 205]],[[236, 8, 181], [239, 26, 248]],[[207, 218, 45], [183, 158, 101]]], dtype='uint8')C
cout << "R (c) = " << endl << format(R, Formatter::FMT_C ) << endl << endl; R (c) = { 91, 2, 79, 179, 52, 205,236, 8, 181, 239, 26, 248,207, 218, 45, 183, 158, 101}其他常用項??目的輸出
OpenCV 也通過 << 運算符提供對其他常見 OpenCV 數據結構的輸出的支持:
二維點
Point2f P(5, 1);cout << "Point (2D) = " << P << endl << endl; Point (2D) = [5, 1]3D點
Point3f P3f(2, 6, 7);cout << "Point (3D) = " << P3f << endl << endl; Point (3D) = [2, 6, 7]std::vector via cv::Mat
vector<float> v; v.push_back((float)CV_PI); v.push_back(2); v.push_back(3.01f); cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;std::vector of points
vector<Point2f> vPoints(20); for (size_t i = 0; i < vPoints.size(); ++i)vPoints[i] = Point2f((float)(i * 5), (float)(i % 7)); cout << "A vector of 2D Points = " << vPoints << endl << endl; A vector of 2D Points = [0, 0;5, 1;10, 2;15, 3;20, 4;25, 5;30, 6;35, 0;40, 1;45, 2;50, 3;55, 4;60, 5;65, 6;70, 0;75, 1;80, 2;85, 3;90, 4;95, 5]這里的大多數示例都包含在一個小型控制臺應用程序中。您可以從這里或在 cpp 示例的核心部分下載它。
您還可以在YouTube 上找到有關此的快速視頻演示。
總結
以上是生活随笔為你收集整理的OpenCV Mat类详解和用法(官网原文)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows MSVC 符号表(.li
- 下一篇: YOLOv5 报错:“NotImplem