图像灰度图,直方图,像素通道问题
1.圖像直方圖概述
??直方圖廣泛運用于很多計算機視覺運用當中,通過標記幀與幀之間顯著的邊緣和顏色的統計變化,來檢測視頻中場景的變化。在每個興趣點設置一個有相近特征的直方圖所構成 “標簽”,用以確定圖像中的興趣點。邊緣、色彩、角度等直方圖構成了可以被傳遞給目標識別分類器的一個通用特征類型。色彩和邊緣的直方圖序列還可以用來識別網絡視頻是否被復制。
??其實,簡單來說,直方圖就是對數據進行統計的一種方法,并且將統計值組織到一系列實現定義好的 bin 當中。其中, bin 為直方圖中經常用到的一個概念,可以譯為 “直條” 或 “組距”,其數值是從數據中計算出的特征統計量,這些數據可以是諸如梯度、方向、色彩或任何其他特征。且無論如何,直方圖獲得的是數據分布的統計圖。通常直方圖的維數要低于原始數據。
??圖像直方圖(Image Histogram)是用以表示數字圖像中亮度分布的直方圖,標繪了圖像中每個亮度值的像素數。這種直方圖中,橫坐標的左側為純黑、較暗的區域,而右側為較亮、純白的區域。因此一張較暗圖片的直方圖中的數據多集中于左側和中間部分,而整體明亮、只有少量陰影的圖像則相反。CV 領域常借助圖像直方圖來實現圖像的二值化。
??直方圖的意義如下:
??● 直方圖是圖像中像素強度分布的圖形表達方式。
??● 它統計了每一個強度值所具有的像素個數。
??直方圖是對數據的統計集合,并將統計結果分布于一系列預定義的 bins 中。這里的數據不僅僅指的是灰度值,且統計數據可能是任何有效描述圖像的特征。
??假設有一個矩陣包含一張圖像的信息(灰度值 0 - 255),既然已知數字的范圍包含 256 個值,于是可以按一定規律將這個范圍分割成子區域(也就是 bins)。如:
?????????
??然后再統計每一個 bin(i) 的像素數目。采用這一方法來統計上面的數字矩陣,可以得到下圖(其中 x 軸表示 bin,y 軸表示各個 bin 中的像素個數):
????
??直方圖的一些術語和細節:
??● dims:需要統計的特征數目。在上例中,dims = 1 ,因為僅僅統計了灰度值(灰度圖像)。
??● bins:每個特征空間子區段的數目,可譯為 “直條” 或 “組距”,在上例中, bins = 16。
??● range:每個特征空間的取值范圍。在上例中,range = [0, 255]。
2.像素通道問題
用大疆Guidance獲取了視覺傳感模塊的左右圖像g_greyscale_image_left和g_greyscale_image_right,大疆官網上的解釋為:they?are?both?320?hight,?240?width,?8?bit?grayscale?images.
我現在想看看它的具體像素值,所以寫了以下代碼:
if?(!g_greyscale_image_left.empty()&&!g_greyscale_image_right.empty())?//當傳來的圖片不為空
{
imshow(string("left_")+char('0'+sensor_id),?g_greyscale_image_left);?//顯示左圖
imshow(string("right_")+char('0'+sensor_id),?g_greyscale_image_right);??//顯示右圖
int?nrl?=g_greyscale_image_left.rows;?//?number?of?rows?
int?ncl?=g_greyscale_image_left.cols;?//?number?of?columns?
int?cha=g_greyscale_image_left.channels();??
cout<<"灰度圖的通道數為:"<<cha<<endl;??//顯示通道數
for?(int?i?=?0;?i<nrl;?i++)
{
for?(int?j?=?0;?j<ncl;?j++)?{
cout<<"像素值為:"<<g_greyscale_image_left.at<Vec3b>(i,?j)<<endl;
}
}
}
結果顯示如下:通道數為1,但是像素值有三個值,如果把Vec3b改為uchar,則輸出的像素值為亂碼,有大神能解釋下為什么單通道圖像顯示的像素值為三個數值嗎?
?g_greyscale_image_left.at<Vec3b>(i,?j)
把?Vec3b?改成?BYTE?試試
改了后就成這樣了:
(int)g_greyscale_image_left.at<BYTE>(i,?j)
可以顯示正常的單通道數值了,謝謝。請問uchar,VEC3b,BYTE這幾種各有什么區別呢?我強制顯示三通道為什么也可以?
uchar,?BYTE?都是?unsigned?char?的?typedef?
VEC3b?應該是定義的一個結構體
數據本身無法表明自己是什么,?只能看什么方法來處理他
3.CV_8UC1,CV_8UC2,CV_8UC3等意思
一般的圖像文件格式使用的是 Unsigned 8bits吧,CvMat矩陣對應的參數類型就是
CV_8UC1,CV_8UC2,CV_8UC3。
(最后的1、2、3表示通道數,譬如RGB3通道就用CV_8UC3)
而float 是32位的,對應CvMat數據結構參數就是:CV_32FC1,CV_32FC2,CV_32FC3...
double是64bits,對應CvMat數據結構參數:CV_64FC1,CV_64FC2,CV_64FC3等。
變換這種矩陣單位類型,Mat里有一個函數convertTo可以辦到:
C++:void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 )
mask.convertTo(OutputArray m, CV_8UC3, -1.0, 255.0); // inverse the mask matrix ?means I_new(x,j) = -1*I(x,j) + 255.
rtype 參數就是上述單位類型。具體可以查相關的文檔。
?
當我在OpenCV文檔中找不到任何描述 - 用文字描述 - 各種類型代表什么的時候,谷歌也沒有太多幫助。我知道CV_8UC1是灰度的,但CV_8UC3代表什么?它是RGB?還是YUV?
另外,來自其他的定義呢types_c.h?命名約定是什么模式?
#define CV_8UC1 CV_MAKETYPE(CV_8U,1) #define CV_8UC2 CV_MAKETYPE(CV_8U,2) #define CV_8UC3 CV_MAKETYPE(CV_8U,3) #define CV_8UC4 CV_MAKETYPE(CV_8U,4) #define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))#define CV_8SC1 CV_MAKETYPE(CV_8S,1) #define CV_8SC2 CV_MAKETYPE(CV_8S,2) #define CV_8SC3 CV_MAKETYPE(CV_8S,3) #define CV_8SC4 CV_MAKETYPE(CV_8S,4) #define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))#define CV_16UC1 CV_MAKETYPE(CV_16U,1) #define CV_16UC2 CV_MAKETYPE(CV_16U,2) #define CV_16UC3 CV_MAKETYPE(CV_16U,3) #define CV_16UC4 CV_MAKETYPE(CV_16U,4) #define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))#define CV_16SC1 CV_MAKETYPE(CV_16S,1) #define CV_16SC2 CV_MAKETYPE(CV_16S,2) #define CV_16SC3 CV_MAKETYPE(CV_16S,3) #define CV_16SC4 CV_MAKETYPE(CV_16S,4) #define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))#define CV_32SC1 CV_MAKETYPE(CV_32S,1) #define CV_32SC2 CV_MAKETYPE(CV_32S,2) #define CV_32SC3 CV_MAKETYPE(CV_32S,3) #define CV_32SC4 CV_MAKETYPE(CV_32S,4) #define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))#define CV_32FC1 CV_MAKETYPE(CV_32F,1) #define CV_32FC2 CV_MAKETYPE(CV_32F,2) #define CV_32FC3 CV_MAKETYPE(CV_32F,3) #define CV_32FC4 CV_MAKETYPE(CV_32F,4) #define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))#define CV_64FC1 CV_MAKETYPE(CV_64F,1) #define CV_64FC2 CV_MAKETYPE(CV_64F,2) #define CV_64FC3 CV_MAKETYPE(CV_64F,3) #define CV_64FC4 CV_MAKETYPE(CV_64F,4) #define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))列表中的任何基元類型都可以通過表單中的標識符來定義?CV_<bit-depth>{U|S|F}C(<number_of_channels>)
其中U是無符號整數類型,S是有符號整數類型,F是浮點類型。
所以CV_8UC3是一個8位無符號整數矩陣/ 3通道圖像。這是最常見的RGB(或實際上是BGR)圖像。它只是意味著有三個渠道,如何使用它們取決于你和你的應用程序。
命名模式描述了數據如何實際布置在PC存儲器中,并且與圖像類型沒有直接關系。實際上,數據布局和圖像類型是分離的。
你可能已經找到了CV_XX模式的含義(即數據布局)。
對于成像數據類型,如果你要求OpenCV將圖像作為彩色圖像加載,則它會將其加載為BGR圖像并將其數據作為CV_8UC3進行布局。有幾件事要注意:
- 默認頻道順序是BGR而不是RGB。
- 可以使用模塊中的類型轉換功能imgproc,即cv::cvtColor()。所有數據布局和圖像類型都不兼容,請檢查文檔?;
- alpha通道通常被忽略。大多數處理功能要么專用于單通道圖像,要么對所有通道均勻應用相同的處理;
- 當一個函數接受一個(alpha-)掩碼時,它通常是一個可選的CV_8UC1缺省為空矩陣的類型參數。
Opencv Mat矩陣中data、size等屬性的理解
- data: ?
? ? ? ?uchar類型的指針,指向Mat數據矩陣的首地址。可以理解為標示一個房屋的門牌號;
- dims:?
? ? ? ? Mat矩陣的維度,若Mat是一個二維矩陣,則dims=2,三維則dims=3,大多數情況下處理的都是二維矩陣,是一 ? ? ? ? 個平面上的矩陣。
?
? ? ? ? 可以理解為房屋是一個一層的平房,三維或更多維的則是多層樓房;
- rows:
? ? ? ? Mat矩陣的行數。可理解為房屋內房間行數;
- cols:?
? ? ? ? Mat矩陣的列數。可理解為房屋內房間列數;
- size():
? ? ? ? 首先size是一個結構體,定義了Mat矩陣內數據的分布形式,數值上有關系式:
?
? ? ? ? ?image.size().width==image.cols; ? ? ? ?image.size().height==image.rows ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ?可以理解為房屋內房間的整體布局,這其中包括了房間分別在行列上分布的數量信息;
- channels():
? ? ? ? Mat矩陣元素擁有的通道數。例如常見的RGB彩色圖像,channels==3;而灰度圖像只有一個灰度分量信息, ? ? ? ? ? ? channels==1。
?
? ? ? ? 可以理解為每個房間內放有多少床位,3通道的放了3張床,單通道的放了1張床;
- depth:?
? ? ? ? 用來度量每一個像素中每一個通道的精度,但它本身與圖像的通道數無關!depth數值越大,精度越高。在 ? ? ? ? ? ? ? ? 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} ? ? ? ? ?
? ? ? ? 其中U是unsigned的意思,S表示signed,也就是有符號和無符號數。
? ? ? ? 可以理解為房間內每張床可以睡多少人,這個跟房間內有多少床并無關系;
- elemSize:
? ? ? ? elem是element(元素)的縮寫,表示矩陣中每一個元素的數據大小,如果Mat中的數據類型是CV_8UC1,那么 ? ? ? ? ? ? elemSize==1;如果是CV_8UC3或CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么 ? ? ? ? ? ? elemSize==6;即elemSize是以8位(一個字節)為一個單位,乘以通道數和8位的整數倍;
?
? ? ? ? 可以理解為整個房間可以睡多少人,這個時候就得累計上房間內所有床位數(通道)和每張床的容納量了;
- elemSize1:
? ? ? ? elemSize加上一個“1”構成了elemSize1這個屬性,1可以認為是元素內1個通道的意思,這樣從命名上拆分后就很 ? ? ? ? 容易解釋這個屬性了:表示Mat矩陣中每一個元素單個通道的數據大小,以字節為一個單位,所以有:?
?
? ? ? ? eleSize1==elemSize/channels;
- step:
? ? ? ? 可以理解為Mat矩陣中每一行的“步長”,以字節為基本單位,每一行中所有元素的字節總量,是累計了一行中所 ? ? ? ? ? 有元素、所有通道、所有通道的elemSize1之后的值;
- step1():?
? ? ? ?以字節為基本單位,Mat矩陣中每一個像素的大小,累計了所有通道、所有通道的elemSize1之后的值,所以有:
?
? ? ? ? step1==step/elemSize1;
- type:
? ? ? ? Mat矩陣的類型,包含有矩陣中元素的類型以及通道數信息,type的命名格式為CV_(位數)+(數據類型)+(通道 ? ? ? ? ? ? ? 數),所有取值如下:
?
?
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的每個對象具有其自己的頭,但可能他們通過讓他們矩陣指針指向同一地址的兩個實例之間共享該矩陣。此外,拷貝運算符將只能復制矩陣頭部,也還將復制指向矩陣的指針,但不復制矩陣本身。
?
?
上文中的所有對象,使用同一個數據矩陣。他們的頭不同,但是其中任何一個對矩陣進行修改,都會將影響所有其他的矩陣。在實踐中的不同對象只是提供相同的底層數據不同的訪問方法,然而,它們的頭部是不同的。真正有趣的部分是您可以創建僅指向完整數據的一小部分的頭。例如,要在圖像中創建興趣區域 ( ROI) 只需創建一個新頭設置新邊界:
?
Mat?D?(A,?Rect(10,?10,?100,?100)?);
Mat?E?=?A(Range:all(),?Range(1,3));
?
?
現在,你可能會問多個Mat對象使用同一片數據,這片數據什么時候清理。簡短的回答是:由最后一個使用它的對象清理。這里使用引用計數的機制,每當有人復制Mat對象的頭,計數器增加。每當一個頭被清除,計數器下調。當計數器變為零,矩陣也就被釋放了。(類似于動態指針)因為有時會仍然也要復制矩陣的本身,存在著 clone() 或 copyTo() 函數。
?
Mat?F?=?A.clone(); Mat?G; A.copyTo(G);?
?
現在 modifyingForGwill 不會影響由 theMatheader 指出的矩陣。你要記得從所有的是:
1、輸出圖像分配 OpenCV 功能是自動 (除非另行指定,否則)。
2、用c + + OpenCV的接口就無需考慮內存釋放。
3、賦值運算符和復制構造函數 (構造函數)只復制頭。
4、使用clone () 或copyTo () 函數將復制的圖像的基礎矩陣。
存儲方法
這是關于你是如何存儲的像素值。您可以選擇的顏色空間和使用的數據類型。色彩空間是指我們如何結合為了代碼指定的顏色的顏色分量。最簡單的是灰色的規模。在這里我們所掌握的顏色是黑色和白色。組合的這些讓我們能創造很多的灰度級。
對于彩色的方法,我們有很多方法可供選擇。不過,每一就是將他們拆解成三個或四個基本組成部分,這些部分就會組合給所有其他的方法。最受歡迎的這一個 RGB,主要是因為這也是我們的眼睛如何建立中我們的眼睛的顏色。其基準的顏色是紅、 綠、 藍。編寫代碼的一種顏色的透明度有時第四個元素: 添加 alpha (A)。但是,它們很多顏色系統每個具有自身的優勢:
1、RGB 是最常見的是我們的眼睛使用類似的事情,我們顯示系統還撰寫使用這些顏色。
· 單純皰疹和合肥分解顏色到他們的色相、 飽和度和亮度值/組件,這是我們來描述顏色更自然的方式。您使用,例如可駁回的最后一個組件,使你不那么明智的輸入圖像的光照條件的算法。
2、 YCrCb 使用流行的 JPEG 圖像格式。
3、 CIE L *b*a 是均勻顏色空間,它是非常方便的如果您需要測量給定的顏色,以另一種顏色的距離。
現在,每個建筑構件都自己有效的域。這會導致使用的數據類型。我們如何存儲組件的定義只是如何精細的控制,我們已于其域。最小的數據類型可能是 char 類型,這意味著一個字節或 8 位。這可能是有符號(值-127 到 + 127)或無符號(以便可以存儲從 0 到 255 之間的值)。雖然這三個組件的情況下已經給 16 萬可能的顏色來表示 (如 RGB 的情況下) 我們可能通過使用浮點數 (4 字節 = 32 位) 或double(8 字節 = 64 位) 數據類型的每個組件獲得甚至更精細的控制。然而,請記住增加組件的大小也會增加在內存中的整張圖片的大小。
顯式創建Mat對象
在Load, Modify and Save an Image?教程中,你已經可以看到如何使用readWriteImageVideo: 'imwrite() <imwrite>' 函數將一個矩陣寫到一個圖像文件中。然而,出于調試目的顯示的實際值就方便得多。您可以實現此通過Mat的 <<運算符。不過,請注意這僅適用于二維矩陣。
雖然Mat是一個偉大的圖像容器類,它也是一般矩陣類。因此,利用Mat創建和操作多維矩陣是可能的。您可以通過多種方式創建Mat的對象:
1、 Mat()構造函數
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
?
?
cout << "M = " << endl << " " << M << endl << endl;
對于二維的和多通道的圖像,我們首先定義它們的大小:按行和列計數。
然后我們需要指定的數據類型,用于存儲元素和每個矩陣點通道的數量。為此,我們根據以下的約定可以作出多個定義:
CV_ [每一項的位數] [有符號或無符號] [類型前綴] C [通道數]
例如,CV_8UC3 意味著我們使用那些長的 8 位無符號的 char 類型和每個像素都有三個項目的這三個通道的形成。這是預定義的四個通道數字。Scalar 是四個元素短向量。指定此和可以初始化所有矩陣點與自定義的值。但是如果你需要更多您可以創建與上部宏和頻道號碼放在括號中,您可以看到下面的類型。
使用 C\C++ 數組和通過構造函數來初始化
?
int?sz[3]?=?{2,2,2};
Mat?L(3,sz,CV_8UC(1),Scalar::all(0));(構造函數初始化)
?
上例為我們展示了如何創建一個二維以上的矩陣。首先指定其維度數,然后傳入一個包含了每個維度尺寸信息的指針,其他都保持不變。
2、為一個已經存在的IplImage創建一個頭:
IplImage* img = cvLoadImage("greatwave.png", 1);
Mat mtx(img); // 轉換 IplImage*-> Mat(創建頭)
?
3、 Create()函數:
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << " " << M << endl << endl;
?
你不能通過這個構造來初始化矩陣中的數值。它只會在新的矩陣尺寸與舊的矩陣尺寸不合時重新分配矩陣的數據空間。
?MATLAB風格的初始化函數:zeros(), ones(),
:eyes().指定使用的尺寸和數據類型
?
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;?
?
?
對于小的矩陣來說你可以使用逗號隔開的初始化函數:
Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
為一個已有的Mat對象創建一個新的頭然后clone()或者copyTo()這個頭.
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
?
?
打印格式
注意:你可以通過用randu()函數產生的隨機值來填充矩陣。你需要給定一個上限和下限來確保隨機值在你期望的范圍內:
Mat R = Mat(3, 2, CV_8UC3);
cv::randu(R, Scalar::all(0), Scalar::all(255));
randu(cv::Mat &dat ,const cv::Scalar &low,const cv::Scalar &high);
在上一個例子中你可以看到默認的格式選項。盡管如此,OpenCV允許你在符合以下規則的同時格式化你的輸出:
默認
cout << "R (default) = " << endl << R << endl << endl;
?
?
Python
cout << "R (python) = " << endl << format(R,"python") << endl << endl;
?
?
Comma separated values (CSV)
cout << "R (csv) = " << endl << format(R,"csv" ) << endl << endl;
?
?
Numpy
cout << "R (numpy) = " << endl << format(R,"numpy" ) << endl << endl;
?
?
C
cout << "R (c) = " << endl << format(R,"C" ) << endl << endl;
?
?
打印出其它常見數據項
OpenCV 通過<<操作符也為其他常用OpenCV數據結構提供打印輸出的支持,如:
2D 點
Point2f P(5, 1);
cout << "Point (2D) = " << P << endl << endl;
?
?
3D 點
Point3f P3f(2, 6, 7);
cout << "Point (3D) = " << P3f << endl << endl;
?
?
std::vector通過 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
vector<Point2f> vPoints(20);
for (size_t E = 0; E < vPoints.size(); ++E)
vPoints[E] = Point2f((float)(E*5), (float)(E % 7));
cout << "A vector of 2D Points = " << vPoints << endl << endl;
?
?
這里大多數的例程都是在一個小控制臺程序里運行。你可以在這里下載或是在cpp示例文件夾下找到。
Mat::~Mat
Mat的析構函數。
C++: Mat::~Mat()
析構函數調用Mat::release()。
Mat::operator =
提供矩陣賦值操作。
C++: Mat& Mat::operator=(const Mat& m)
C++: Mat& Mat::operator=(const MatExpr_Base& expr)
C++: Mat& Mat::operator=(const Scalar& s)
參數:
m?– 被賦值的右側的矩陣。 矩陣的賦值是一個復雜度為O(1) 的操作。 這就意味著沒有復制數據段,并且兩矩陣將使用同一引用計數器。在給矩陣賦新數據之前先由Mat::release()釋放引用。
expr?–被賦值的矩陣表達式對象。 作為第一種賦值方式的逆操作第二種形式可以被重新用到具有適當大小和尺寸的已分配空間的矩陣上以適應表達式的結果。矩陣表達式擴展得到的實函數將自動處理這個分配過程。例如:
C=A+B 擴展成add(A, B, C) , andadd() 要當心C重新分配數據的操作。.
s?– 標量賦值給每一個矩陣元,矩陣的大小和類型將不會改變。有現成的賦值運算符。由于他們各不相同請閱讀運算符參數說明。
Mat::operator MatExpr
提供一種Mat-to-MatExpr轉換運算符
C++: Mat::operator MatExpr_<Mat, Mat>() const
轉換運算符不能顯示調用而是由矩陣表達式引擎(Matrix Expression engine)內部調用The cast operator should not be called explicitly. It is used internally by the Matrix Expressions engine.
Mat::row
創建一個指定行數的矩陣頭。.
C++: Mat Mat::row(int i) const
參數:
i –?一個0基的行索引.
該方法創建一個具有指定了行數的新矩陣頭的矩陣并返回它。這是一個復雜度為O(1) 的操作,無須考慮矩陣的尺寸。新矩陣和原矩陣共享一份基礎數據。這是一個典型基本矩陣處理操作的例子, axpy是LU和許多其它算法都使用的一個函數
inline void matrix_axpy(Mat& A, int i, int j, double alpha)
{
A.row(i) += A.row(j)*alpha;
}
Note:在當前實現中,下面的代碼不會無法按預期的效果工作:
Mat A ;
...
A.row(i) = A.row(j) ;/ /不起作用
發生這種情況是因為 A.row(i) 形成臨時矩陣頭進一步分配給另一個矩陣頭。請記住,每個操作復雜度為O(1),即沒有復制任何數據。因此,如果你預期第 j行被復制到第 i行,那么上述賦值不成立。要做到這一點,應該把這種簡單的賦值轉換到表達式中或使用 Mat::copyTo() 方法:
Mat A ;
...
/ / 可行,但看上去有點目的不明確。
A.row(i) = A.row(j) + 0;
/ / 這是有點兒長,但這是推薦的方法。
A.row(j).copyTo(A.row(i)) ;
Mat::col
創建一個具有指定了矩陣頭中列數這個參數的矩陣
C++: Mat Mat::col(int j) const
參數:
j?–一個0基(從0開始)的列索引
該方法創建一個具有指定了矩陣頭中列數這個參數的新矩陣并作為函數返回值。這是一種復雜度為O(1)的操作,不用考慮矩陣的尺寸大小。新矩陣和原始矩陣共享一份基礎數據。參看Mat::row()說明信息。
Mat::rowRange
為指定的行span創建一個新的矩陣頭。
C++: Mat Mat::rowRange(int startrow, int endrow) const
C++: Mat Mat::rowRange(const Range& r) const
參數:
startrow?– 一個包容性的0基(從0開始)的行span起始索引.。
endrow?– 一個0基的獨占性的行span.終止索引。
r?– Range 結構包含著起始和終止的索引值。該方法給矩陣指定的行span創建了新的頭。 與Mat::row() 和 Mat::col()相類似這是一個復雜度為O(1)的操作。
Mat::colRange
為指定的行span創建一個矩陣頭。
C++: Mat Mat::colRange(int startcol, int endcol) const
C++: Mat Mat::colRange(const Range& r) const
參數:
startcol?– 一個包容性的0基(從0開始)的span列起始索引。
endcol?–一個0基的獨占性的列span.終止索引。
r?–Range 結構包含著起始和終止的索引值。該方法給矩陣指定的列span創建了新的頭。 與Mat::row() 和 Mat::col()相類似這是一個復雜度為O(1)的操作。
Mat::diag
提取或創建矩陣對角線。
C++: Mat Mat::diag(int d) const
C++: static Mat Mat::diag(const Mat& matD)
參數:
d?– 對角線的索引值,可以是以下的值:
– d=0 是主對角線
– d>0表示下半部的對角線。例如:d=1對角線是緊挨著住對角線并位于矩陣下方。
– d<0表示來自矩陣上半部的對角線。例如:d= 1表示對角線被設置在對角線的上方并緊挨著。
matD?– 單列用于形成矩陣對角線的列。
該方法為指定的矩陣創建一個新的頭。然后新矩陣被分割為單獨的列矩陣。類似于Mat::row() 和Mat::col() ,它是復雜度為O(1)操作。
Mat::clone
創建一個數組及其基礎數據的完整副本。
C++: Mat Mat::clone() const
該方法創建了一個完整的數組副本。原始的step[]不會被考慮在內的。因此數組的副本是一占用total()*elemSize()字節的連續陣列。
Mat::copyTo
把矩陣復制到另一個矩陣中。
C++: void Mat::copyTo(OutputArray m) const
C++: void Mat::copyTo(OutputArray m, InputArray mask) const
參數:
m?– 目標矩陣。如果它的尺寸和類型不正確,在操作之前會重新分配。
mask?– 操作掩碼。它的非零元素表示矩陣中某個要被復制。
該方法把矩陣的復制到另一個新的矩陣中在復制之前該方法會調用
m.create(this->size(), this->type);
因此,目標矩陣會在必要的情況下重新分配
盡管m.copyTo(m) works ?awlessly,該函數并不處理源矩陣和目標矩陣之間有重疊的部分的情況。當操作掩碼指定以及上述的Mat::create重新分配矩陣,新分配的矩陣在數據復制到里面之前全都被初始化為0。
Mat::convertTo
在縮放或不縮放的情況下轉換為另一種數據類型。
C++:
void Mat::convertTo(OutputArray m,int rtype,double alpha=1,double beta=0)const
參數:
m?– 目標矩陣。如果它的尺寸和類型不正確,在操作之前會重新分配。
rtype?– 要求是目標矩陣的類型,或者在當前通道數與源矩陣通道數相同的情況下的depth。如果rtype 為負,目標矩陣與源矩陣類型相同。
beta?– 可選的delta加到縮放值中去。
該方法將源像素值轉化為目標類型saturate_cast<> 要放在最后以避免溢出
m( x;y) = saturate_cast < rType > ( α*( *this)( x;y) +β)
Mat::assignTo
提供了一個convertTo的功能形式。
C++: void Mat::assignTo(Mat& m, int type=-1 ) const
Parameters
m?– 目標陣列。
type?– 要求是目標陣列depth或-1(如果陣列的類型和源矩陣類型相同)
這是一個 internally 使用的由 Matrix Expressions引擎調用的方法。
Mat::setTo
將陣列中所有的或部分的元素設置為指定的值。
C++: Mat& Mat::setTo(const Scalar& s, InputArray mask=noArray())
參數:
s?– 把標量賦給陣列并轉化到陣列的實際類型。
mask?– 與 *this尺寸相同的操作掩碼。這是Mat::operator=(const Scalar& s)運算符的一個高級變量。
Mat::reshape
在無需復制數據的前提下改變2D矩陣的形狀和通道數或其中之一。
C++: Mat Mat::reshape(int cn, int rows=0) const
參數:
cn?– 新的通道數。若cn=0,那么通道數就保持不變。
rows?–新的行數。 若rows = 0, 那么行數保持不變。
該方法為*this元素創建新的矩陣頭。這新的矩陣頭尺寸和通道數或其中之一發生改變,在以下的情況任意組合都是有可能的:
ü 新的矩陣沒有新增或減少元素。通常,rows*cols*channels()在轉換過程中保持一致。.
ü 無數據的復制。也就是說,這是一個復雜度為 O(1)的操作。通常,如果該操作改變行數或透過其他方式改變元素行索引,那么矩陣必定是連續的。參見Mat::isContinuous()。
例如,有一存儲了STL向量的三維點集,你想用3xN的矩陣來完成下面的操作:
std::vector<Point3f> vec;
...
Mat pointMat = Mat(vec). //把向量轉化成Mat, 復雜度為O(1)的運算
reshape(1). // 從Nx1的3通道矩陣得出Nx3 的單通道矩陣
//同樣是復雜度為O(1)的運算
t(); // 最后轉置Nx3 的矩陣
//這個過程要復制所有的元素
Mat::t
轉置矩陣。.
C++: MatExpr Mat::t() const
該方法通過矩陣表達式(matrix expression)實現矩陣的轉置The method performs matrix transposition by means of matrix expressions. 它并未真正完成了轉置但卻返回一個臨時的可以進一步用在更復雜的矩陣表達式中或賦給一個矩陣的轉置矩陣對象:
Mat A1 = A + Mat::eye(A.size(), A.type)*lambda;
Mat C = A1.t()*A1; //計算(A + lambda*I)^t * (A + lamda*I).
Mat::inv
反轉矩陣
C++: MatExpr Mat::inv(int method=DECOMP_LU) const
參數:
method?– 反轉矩陣的方法。有以下幾種可能的值:
– DECOMP_LU是 LU 分解一定不能是單數的。
– DECOMP_CHOLESKY 是 Cholesky LLT只適用于對稱正矩陣的分解。該類型在處理大的矩陣時的速度是LU的兩倍左右。
– DECOMP_SVD是 SVD 分解。如果矩陣是單數或甚至不是2維,函數就會計算偽反轉矩陣。
該方法執行矩陣的反轉矩陣表達。這意味著該方法返回一個臨時矩陣反轉對象并可進一步用于更復雜的矩陣表達式的中或分配給一個矩陣。
Mat::mul
執行兩個矩陣按元素相乘或這兩個矩陣的除法。
C++: MatExpr Mat::mul(InputArray m, double scale=1) const
參數:
m?– 與*this具有相同類型和大小的矩陣,或矩陣表達式。
scale?– 可選縮放系數。
該方法返回一個用可選的縮放比率編碼了每個元素的數組乘法的臨時的對象。 注意:這不是一個對應“*”運算符的簡單的矩陣乘法。.
例::
Mat C = A.mul(5/B); // 等價于divide(A, B, C, 5)
Mat::cross
計算3元素向量的一個叉乘積。
C++: Mat Mat::cross(InputArray m) const
參數:
m?–另一個叉乘操作對象。
該方法計算了兩個3元素向量的叉乘的積被操作向量必須是3元素浮點型的具有相同形狀和尺寸的向量。結果也是一語被操作對象的具有相同形狀和大小的浮點型3元素向量。
Mat::dot
計算兩向量的點乘。
C++: double Mat::dot(InputArray m) const
參數:
m?–另一個點積操作對象。
方法計算兩個矩陣的點積。如果矩陣不單列或單行的向量,用頂部到底部從左到右掃描次序將它們視為 1 D向量。這些向量必須具有相同的大小和類型。如果矩陣有多個通道,從所有通道得到的點積會被加在一起。
Mat::zeros
返回指定的大小和類型的零數組。
C++: static MatExpr Mat::zeros(int rows, int cols, int type)
C++: static MatExpr Mat::zeros(Size size, int type)
C++: static MatExpr Mat::zeros(int ndims, const int* sizes, int type)
參數
ndims?– 數組的維數。
rows–行數。
cols?–列數。
size–替代矩陣大小規格Size(cols, rows)的方法。
sizes– 指定數組的形狀的整數數組。
type– 創建的矩陣的類型。
該方法返回一個 Matlab 式的零數組初始值設定項。它可以用于快速形成一個常數數組作為函數參數,作為矩陣的表達式或矩陣初始值設定項的一部分。
Mat A;
A = Mat::zeros (3,3,CV_32F);
在上面的示例中,只要A不是 3 x 3浮點矩陣它就會被分配新的矩陣。否則為現有的
矩陣 A填充零。
Mat::ones
返回一個指定的大小和類型的全為1的數組。
C++: static MatExpr Mat::ones(int rows, int cols, int type)
C++: static MatExpr Mat::ones(Size size, int type)
C++: static MatExpr Mat::ones(int ndims, const int* sizes, int type)
參數:
ndims?–數組的維數。
rows?–行數。.
cols?–列數。
size?–替代矩陣大小規格Size(cols, rows)的方法。
sizes?–指定數組的形狀的整數數組。
type?–創建的矩陣的類型。
該方法返回一個 Matlab 樣式 1 的數組初始值設定項,類似Mat::zeros()。請注意,這種方法中你可以使用任意一個值和Matlab 語法初始化數組如下:
Mat A = Mat::ones (100,100,CV_8U) * 3 ;/ / 使100 x 100 矩陣里充滿 3。
上述操作不會形成一個 100 x 100 1 的矩陣,然后乘以 3。相反,它只是記住
縮放因子(在本例中 3)在實際調用矩陣初始值設定項時使用它。
關于圖像三通道和單通道的解釋?
(一):單通道圖,
俗稱灰度圖,每個像素點只能有有一個值表示顏色,它的像素值在0到255之間,0是黑色,255是白色,中間值是一些不同等級的灰色。(也有3通道的灰度圖,3通道灰度圖只有一個通道有值,其他兩個通道的值都是零)。
(二):三通道圖,每個像素點都有3個值表示 ,所以就是3通道。也有4通道的圖。例如RGB圖片即為三通道圖片,RGB色彩模式是工業界的一種顏色標準,是通過對紅(R)、綠(G)、藍(B)三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的,RGB即是代表紅、綠、藍三個通道的顏色,這個標準幾乎包括了人類視力所能感知的所有顏色,是目前運用最廣的顏色系統之一。總之,每一個點由三個值表示。
下面用一個簡單的例子說明三通道圖片和單通道圖片的區別
[cpp]?view plain?copy
e
- #include
- #include
- #include
- ?
- using?namespace?std;
- using?namespace?cv;
- ?
- int?main()
- {
- //載入一張彩色圖片并顯示
- Mat?srcImage=imread('G:\\Image\\lenaRGB.png',1);
- namedWindow('Image',WINDOW
_AUTOSIZE);
- imshow('Image',srcImage);
- ?
- int?nHeight=srcImage.rows;
- int?nWidth=srcImage.cols;
- ?
- //載入一張灰度圖并顯示,這里使用同一張圖片?只是imread函數的最后一個參數不一樣?效果是相同的
- Mat?grayImage=imread('G:\\Image\\lenaRGB.png',0);
- namedWindow('grayImage',WINDOW_AUTOSIZE);
- imshow('grayImage',grayImage);
- ?
- //基本信息
- cout<<'圖像的高度'<<nHeight<<endl;
- cout<<'圖像的寬度'<<nWidth<<endl;
- cout<<'Image的通道數'<<srcImage.channels()<<endl;?//彩色圖片的通道數
- cout<<'grayImage的通道數'<<grayImage.channels()<<endl;?//灰度圖片的通道數
- ?
- for(int?i=0;i
LI>
- {
- for(int?j=0;j
- {
- srcImage.at(i,j)=0;
- grayImage.at(i,j)=0;
- }
- }
- namedWindow('彩色圖片處理后對應黑色圖片',WINDOW_AUTOSIZE);
- imshow('彩色圖片處理后對應黑色圖片',srcImage);
- ?
- ?
namedWindow('灰度圖片處理后對應黑色圖片',WINDOW_AUTOSIZE);
- imshow('灰度圖片處理后對應黑色圖片',grayImage);
- ?
- cvWaitKey(0);
- cvDestroyWindow('Image');
- cvDestroyWindow('grayImage');
- cvDestroyWindow('彩色圖片處理后對應黑色圖片');
- cvDestroyWindow('灰度圖片處理后對應黑色圖片');
- ?
- return?0;
- ?
- }
程序結果:?
?
好好體會 運行結果?就知道了。?
總結
以上是生活随笔為你收集整理的图像灰度图,直方图,像素通道问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Quartz 定时任务(Schedul
- 下一篇: 理解图像傅里叶变换的频谱图