OpenCV RGB直方圖計算與繪制----calcHist()函數、normalize()函數


1-BGR直方圖
在OpenCV中,彩色圖像存儲是通過多通道的數組來實現的,對CV_8UF3來言,其每個數組通道中的元素可取值為0到255。
顏色分布直方圖描述的是不同色彩在整幅圖像中所占的比例,而並不關心每種色彩所處的空間位置。
因此,對彩色圖像求其直方圖,可先提取彩色圖像的各個通道,然后對每個通道進行直方圖計算,最后利用圖像融合技術合並通道信息,求解出圖像顏色分布直方圖。
===========================分割線=========================
2-換個角度認識圖像(直方圖)
第一個就是當我們面對圖像的時候,我們面對的是抽象的矩陣,如下圖,下面是0-255的灰度圖像的表示,密密麻麻的。

那么我們做的直方圖,其實就是對這些像素值的統計。例如:首先,我們需要把0-255分成 17 個 區域(bin),如下圖所示:

我們對每個范圍中的灰度值進行統計排序,做出如下的表格:

我們是以圖像的灰度為例子說明這個直方圖,當然直方圖不僅僅用於灰度特種統計排序,還可以用於圖像的梯度、方向等特征。

灰度直方圖是灰度級的函數,描述圖像中該灰度級的像素個數(或該灰度級像素出現的頻率):其橫坐標是灰度級,縱坐標表示圖像中該灰度級出現的個數(頻率)。

在以上的過程中,我們使用到一些重要的參數,理解這些參數幫助我們更好的使用API函數。


dims:需要統計的特征的數目,我們上面只統計了 灰度值這個特征,所以, dims =1。
bins:一般翻譯為箱子,看上圖,一共有16個bins,其實就和我們平時見得簡單函數差不多。在圖像直方圖中,你可以把一個灰度值設置為一個bins,0~255強度的灰度值一共就需要256個bins,是不是很簡單。 
Range:就是范圍啦,規定一個bins能夠達到的最大和最小的范圍。比如一張圖片10*10,那么就有100個像素。然后前面已經說過,直方圖是按照亮度統計像素數量,那么范圍就是0~100啦。這里有一個地方要說一下,剛剛0~100還是對於比較小的圖像,那么對於比較大的圖像1000*1000,那么范圍太大了。我們統計像素數量的時候肯定沒有問題,但是要畫直方圖的時候,難道有一個包含100000個像素,豈不是要化的很長?所以,一般在畫直方圖的時候,會有一個比例縮放的過程,比如我提前定好我直方圖最大的高度只能夠是256,那么你就可以用(最大的高度/最大的像素量)統計到的像素量來進行縮放。這樣就簡單多了。我這里提到的縮放方式只是一種,你可以隨便定義喜歡的縮放方式。

一維直方圖的結構表示為:

 

 

再結合上面畫的示意圖,應該就很好理解了。

 

基本的概念其實很簡答,想的時候要不要想復雜了,那么基本概念就到這里了。

===============分割線===============

3-相關函數說明

當然了,首先介紹的是計算直方圖的函數了。

直方圖計算:calcHist()函數

1 void calcHist( const Mat* images, int nimages, 2                           const int* channels, InputArray mask, 3                           OutputArray hist, int dims, const int* histSize, 4                           const float** ranges, bool uniform=true, bool accumulate=false );

參數解釋:
參數1:輸入源圖像。注意這里的格式是const Mat*,也就是說,你要傳入一個地址,輸入的數組(圖片)或者數組集(一堆圖片)需要為相同的深度(CV_8U或CV_32F)和相同的尺寸。
參數2:int類型的nimages,輸入數組的個數,也就是第一個參數中存放了多少張“圖像”,有幾個原數組。
參數3:const int*類型的channels,用來計算直方圖的channes的數組,需要統計的通道(dim)索引。第一個數組通道從0到images[0].channels()-1,而第二個數組通道從images[0]計算到images[0].channels()+images[1].channels()-1,以此類推。比如輸入是2副圖像,第一副圖像有0,1,2共三個channel,第二幅圖像只有0一個channel,那么輸入就一共有4個channes,如果int channels[3] = {3, 2, 0},那么就表示是使用第二副圖像的第一個通道和第一副圖像的第2和第0個通道來計算直方圖。(這句表示沒看懂)。
參數4:InputArray類型的mask,可選的操作掩碼。如果此掩碼不為空,那么它必須為8位(CV_8U)的數組,並且與images[i]有同樣大小的尺寸,值為1的點將用來計算直方圖。這里的非零掩碼元素用於標記出統計直方圖的數組元素數據。
參數5:OutputArray類型的hist,輸出的計算出來的直方圖,一個二維數組。
參數6:int類型dims,需要計算的直方圖的維度,必須是正數,且不大於CV_MAX_DIMS。(32)
參數7:const int*類型的histSize,存放每個維度的直方圖尺寸的數組。簡單把直方圖看作一個一個的豎條的話,就是每一維上豎條的個數。
參數8:const float**類型的ranges,表示每一個維度數組(第6個參數dims)的每一維的邊界陣列,可以理解為每一維數值的取值范圍。比如 float rang1[] = {0, 20};float rang2[] = {30, 40};  const float*rangs[] = {rang1, rang2};那么就是對0,20和30,40范圍的值進行統計。
參數9:bool類型的uniform,表示直方圖是否均勻的標識符,即每一個豎條的寬度是否相等。有默認值true。
參數10:bool類型的accumulate,累計標識符,有默認值false。若其為true,直方圖在配置階段不會被清零。此功能主要是允許從多個陣列中計算單個直方圖,或者用於在特定的時間更新直方圖。
=====================間隔線======================
歸一化:normalize()函數
功能:縮放和移位數組元素,以便指定的標准(alpha)或最小(alpha)和最大(beta)數組值獲得指定的值。

1 void normalize( InputArray src, OutputArray dst, double alpha=1, double beta=0, 2                              int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray());

參數解釋:
參數1:InputArray類型的src,輸入數組(圖像)。
參數2:OutputArray類似的dst,輸出數組(圖像),與輸入圖像類型尺寸一樣。
參數3:alpha,表示range normalization模式的最小值。有默認值為1。
參數4:beta,表示range normalization模式的最大值,不用於norm normalization(范數歸一化)模式。有默認值為0。
參數5:normType,表示歸一化的類型,可以有以下的取值:
---------------NORM_MINMAX:數組的數值被平移或縮放到一個指定的范圍,線性歸一化,一般較常用。
---------------NORM_INF:此類型的定義沒有查到,根據OpenCV 1的對應項,可能是歸一化數組的C-范數(絕對值的最大值)。
---------------NORM_L1:歸一化數組的L1-范數(絕對值的和)。
---------------NORM_L2:歸一化數組的(歐幾里德)L2-范數。
參數6:有默認值為-1。dtype為負數時,輸出數組的type與輸入數組的type相同;否則,輸出數組與輸入數組只是通道數相同,而tpye=CV_MAT_DEPTH(dtype)。
參數7:操作掩膜,用於指示函數是否僅僅對指定的元素進行操作。
=====================間隔線======================
繪圖方法詳情請看官方文檔:  基本繪圖
=======================分割線=====================
4-代碼演示

 1 //----------------------------------------------------------  2 //功能:BGR三通道直方圖實現  3 //----------------------------------------------------------
 4  
 5 #include <opencv2/core/core.hpp>                        
 6 #include <opencv2/highgui/highgui.hpp>                        
 7 #include <opencv2/imgproc/imgproc.hpp>                       
 8 #include <iostream>     
 9 using namespace std; 10 using namespace cv; 11  
12 int main() 13 { 14     //------------【1】讀取源圖像並檢查圖像是否讀取成功-------------------------------- 
15     Mat srcImage = imread("D:/OutPutResult/ImageTest/aurora.jpg"); 16     if (!srcImage.data) 17  { 18         cout << "讀取圖片錯誤,請重新輸入正確路徑!\n"; 19         system("pause"); 20         return -1; 21  } 22     namedWindow("【源圖像-RGB顏色空間】"); 23     imshow("【源圖像-RGB顏色空間】", srcImage); 24     //-------------【2】圖像通道的分離,3個通道B、G、R------------------------
25     vector<Mat> rgb_channel; 26  split(srcImage, rgb_channel); 27     //-------------【3】初始化直方圖計算參數---------------------------------------
28     int bins = 256; 29     int histsize[] = { bins }; 30     float range[] = { 0, 256 }; 31     const float* histRange = { range }; 32  Mat b_Hist, g_Hist, r_Hist; 33     //-------------【4】計算各個通道的直方圖--------------------------------------
34     calcHist(&rgb_channel[0], 1, 0, Mat(), b_Hist, 1, histsize, &histRange, true, false); //B-通道
35     calcHist(&rgb_channel[1], 1, 0, Mat(), g_Hist, 1, histsize, &histRange, true, false); //G-通道
36     calcHist(&rgb_channel[2], 1, 0, Mat(), r_Hist, 1, histsize, &histRange, true, false); //R-通道 37     //-------------【5】設置直方圖繪圖參數----------------------------------------------------
38     int hist_h = 360; 39     int hist_w = bins * 3; 40     int bin_w = cvRound((double)hist_w / bins); 41     Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//創建一個黑底的圖像,為了可以顯示彩色,所以該繪制圖像是一個8位的3通道圖像 42     //-------------【6】將直方圖歸一化到[0,histImage.rows] ------------------------------------------------------------------------------
43     normalize(b_Hist, b_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());  //B-通道
44     normalize(g_Hist, g_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());  //G-通道
45     normalize(r_Hist, r_Hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());  //R-通道 46     //--------------【7】繪制直方圖 ----------------------------------------------------------------
47     for (int i = 1; i < bins; i++) 48  { 49         //繪制B通道的直方圖信息
50         line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(b_Hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(b_Hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0); 51         //繪制G通道的直方圖信息
52         line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(g_Hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(g_Hist.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0); 53         //繪制R通道的直方圖信息
54         line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(r_Hist.at<float>(i - 1))), Point(bin_w*(i), hist_h - cvRound(r_Hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0); 55  } 56     namedWindow("【RGB直方圖】"); 57     imshow("【RGB直方圖】", histImage); 58     waitKey(0); 59     return 0; 60 }

=================================分割線======================

5-顯示結果

==============================分割線================================

6-程序說明

先把源圖像的通道進行分離,0-通道為B;1-通道為G;2-通道為R。然后計算每個通道的直方圖並繪制顯示處理。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM