轉載請注明出處與作者
這裡只討論1維的直方圖
void calcHist(const Mat* arrays, int narrays, const int* channels, InputArray mask, OutputArray hist,
int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false )
這個函數可以用來計算直方圖.
arrays 源Mat數組的指針
narrays 源Mat數組中Mat的個數
channels 這是一個int型指針,它指向一個數組,為哪些channel需要被計算.當arrays 為多個Mat的時候,channel index是從0開始向下連續計算的.這個數組的大小應該與dims相同.
mask 必須是8-bit 圖像遮罩矩陣,大小與源Mat相同,非0元素對應的源Mat中的點表示為需要計算的點.如果為空則表示所有點都需要計算.
hist 輸出的直方圖矩陣.注意這只是一個數值統計矩陣,並非圖像.
dims 直方圖的維度,必須是正整數,並且不能大於CV_MAX_DIMS.
histSize 表示在計算直方圖的時候要分成多少個塊來計算.最終結果的hist矩陣的行數(rows)就是這個histSize.也就是將要統計的值分成histSize 個區域,像素點落在其中哪個區域,哪個區域的值就增加.
ranges 這是一個指針數組,數組中的每一個元素都是一個指針,這個指針指向一個多元素的數組.這個參數數用來確定histSize 個塊中的每一個塊的數值範圍.
下面詳細說明一下histSize和ranges的關係:
可以這樣認為:histSize和ranges都是數組,histSize數組中存放的是整數,ranges數組中存放的是數組型指針. histSize和ranges這兩個數組的維數應該是一樣的,都等於直方圖的維度dims .也就是說每一個histSize元素都對應一個ranges元素.
當uniform=true的時候,ranges[i]中 只要有2個值(也就是一個區間)就可以了,函數會根據histSize[i] 的數量對這個區間進行等分.
當uniform=false的時候,ranges[i]中要有histSize[i] +1個元素.這histSize[i] +1個元素正好分割成histSize[i]個區間.
當源矩陣中的元素值落在對應區間中,則對應區間的值增加.
accumulate為累加設置,當為true的時候不會清空hist中已經存在的數據,而是在其基礎上進行累加.當為false的時候則從0開始計算.
最後得到一個dims列,每列histSize[i] 行的矩陣.這個矩陣就是我們要得到的直方圖hist.
如何將直方圖矩陣中的數據以圖表的方式顯示出來呢?
方法就是事先準備一個Mat并對其進行初始化,初始化包括設定寬度,高度,type和背景圖像(也就是要填充一個背景,可以用圖片或純色).然後使用OpenCV的劃綫函數在其上進行劃綫或寫文字...然後顯示出來即可.
劃綫的時候一定要先將直方圖中的數值縮放到圖像大小再劃綫.(可以畫線也可以畫矩形)
//!得到一個channel的一維直方圖.
//!channelIndex 為channel的序號,從0開始,blockcount為需要將range中的值分成多少個區塊來統計,最終得出的直方圖會有blockcount個點.
//!range_low為圖像數據範圍的最小值,range_up為圖像數據範圍的最大值,這個範圍之外的數據將不被統計.
static bool GetHistogram_SingleChannel(const cv::Mat &src,cv::Mat &hist,int channelIndex,int blockcount,float range_low,float range_up);
//將直方圖矩陣中的數據轉換為圖像.
//type 圖像類型 0=線型 1=矩形
//imgsize 圖像大小,如果為空則根據直方圖矩陣中的行數來確定(線型為行數,矩形為行數*2),高度為256.color為線或矩形的顏色(默認為黑色),backcolor為背景填充的顏色(默認為白色).
static bool GetHistogramImage(const cv::Mat& hist, cv::Mat &image,int type,cv::Size imgsize,cv::Scalar *color=NULL,cv::Scalar *backcolor=NULL);
//!統計源Mat中的直方圖并將其轉換為圖形.
//!channelIndex 為channel的序號 ,type 圖像類型 0=線型 1=矩形
//!color為線或矩形的顏色(默認為黑色),backcolor為背景填充的顏色(默認為白色)
bool GetHistogram(int channelIndex=0,int type=0,RGBQUAD *color=NULL,RGBQUAD *backcolor=NULL);
bool COpenCVImage::GetHistogram_SingleChannel(const cv::Mat &src,cv::Mat &hist,int channelIndex,int blockcount,float range_low,float range_up)
{
if(src.channels()<=channelIndex || src.depth()!=CV_8U) return false;
int channels[]={channelIndex};
int histSize[]={blockcount};
float hrange[]={range_low,range_up};
const float *ranges[]={hrange};
cv::calcHist(&src,1,channels,cv::Mat(),hist,1,histSize,ranges);
return true;
}
bool COpenCVImage::GetHistogramImage(const cv::Mat& hist, cv::Mat &image,int type,cv::Size imgsize,cv::Scalar *color,cv::Scalar *backcolor)
{
double maxVal = 0;
double minVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
if(imgsize==cv::Size())
{
switch(type)
{
case 1://矩形
{
imgsize.width=2*hist.rows;
imgsize.height=256;
break;
}
case 0://連線
default:
{
imgsize.width=hist.rows;
imgsize.height=256;
}
}
}
cv::Mat histImg(imgsize, CV_8UC3,(backcolor==NULL? cv::Scalar(255,255,255,0):*backcolor));
//int hpt = static_cast<int>(0.9*imgsize.height);
float tmp;
for(int h=0; h<hist.rows; h++)
{
switch(type)
{
case 1://矩形
{
float binVal = hist.at<float>(h);
int rectangle_w= cvRound((imgsize.width/hist.rows+0.5)/2);
int Point2 = cvRound(binVal*0.9*imgsize.height/maxVal);
cv::rectangle(histImg,cv::Rect(h*(imgsize.width/hist.rows),imgsize.height-Point2,rectangle_w,Point2),(color==NULL?cvScalar(0,0,0,0):*color));
//tmp=binVal;
break;
}
case 0: //連線
default:
{
if(h==0)
{
tmp=hist.at<float>(h);
continue;
}
float binVal = hist.at<float>(h);
int Point1 = static_cast<int>(tmp*0.9*imgsize.height/maxVal);
int Point2 = static_cast<int>(binVal*0.9*imgsize.height/maxVal);
cv::line(histImg, cv::Point(h-1,imgsize.height-Point1),cv::Point(h,imgsize.height-Point2), (color==NULL?cvScalar(0,0,0,0):*color));
tmp=binVal;
}
}
}
image=histImg;
return true;
}
bool COpenCVImage::GetHistogram(int channelIndex,int type,RGBQUAD *color,RGBQUAD *backcolor)
{
cv::Mat hist;
if(!GetHistogram_SingleChannel(m_src(r_roi),hist,channelIndex,256,0.0f,255.0f))
{
return false;
}
GetHistogramImage(hist,m_src,type,cv::Size(),
(color==NULL?&cv::Scalar(0,0,0,0):&cv::Scalar(color->rgbBlue,color->rgbGreen,color->rgbRed,color->rgbReserved)),
(backcolor==NULL?&cv::Scalar(255,255,255,0):&cv::Scalar(backcolor->rgbBlue,backcolor->rgbGreen,backcolor->rgbRed,backcolor->rgbReserved))
);
m_modifyed_for_draw=true;
return true;
}
OpenCV還提供了一個直方圖均衡化的函數
void equalizeHist(InputArray src, OutputArray dst)
可以進行直方圖均衡化.
bool COpenCVImage::equalizeHist()
{
if(m_src.channels()!=1 || m_src.depth()!=CV_8U) return false;
cv::equalizeHist(m_src(r_roi),m_src(r_roi));
m_modifyed_for_draw=true;
return true;
}