引言
前面我們說了影像的本質是一堆資料,計算機可不認識影像中的各種人物、景色,只認識0,1,而所有這些都是人為打的標簽,計算機看到的都是一個個像素點的像素值,而這些像素值又有一定的取值范圍,對于RGB來說就是0-255的取值范圍,我們就可以利用直方圖統計在這區間內哪一個出現的頻率低,哪一個頻率高,
一、簡述影像直方圖
- 優勢:影像直方圖是影像像素值的統計學特征,表示了影像的各個像素在0-255出現的頻率,它的計算代價較小,具有影像旋轉、縮放、平移不變性等很多優點
- 缺點:不能表征一張影像,
- 應用場景:灰度影像的閾值分割、基于顏色的影像檢索以及影像分類、反向投影跟蹤等,
- Bins是指直方圖的大小范圍, 對于像素值取值在0~255之間的,最少有256個bin,此外還可以有16、32、48、128等,256除以bin的大小應該是整數倍,
- 直方圖資料反映的是0-255總共256個灰度等級,每一個灰度等級在這張影像上,這么多像素點出現的次數是多少(x軸為灰度等級,y軸為出現次數),每一個直方圖都是對影像的像素點在像素值的取值范圍空間出現頻次的統計,
二、函式原型
calcHist函式
作用:計算一維或多維影像直方圖
c++原型:
引數:
- images:輸入影像的指標,一定是同樣的深度(CV_8U or CV_32F),且一個影像可以有多個channes,
- nimages:輸入影像的個數
- channels:計算直方圖的channes的陣列,如果輸入的影像的個數為2,第一張影像有0,1,2共三個channel,第二張影像只有0一個channel,那么輸入就一共有4個通道,如果int channels[3] = {3, 0, 1},那么就表示是使用第二張影像的第一個通道和第一張影像的第0和第1個通道來算
- mask:掩碼,跟前面說的mask一致,非0區域才會用來做直方圖的計算,如果不用就表示完整計算整張影像,這時Mat()引數為空,如果要用mask,那么它必須是一個8位(CV_8U)的陣列,并且它的大小的和images[i]的大小相同,值為1的點用來計算
- hist:計算出來的直方圖
- dims:計算出來的直方圖的維數,這里是一維的,如果想要二維可以定義一個陣列,陣列里有兩個值
- histSize:在每一維上直方圖的個數,簡單把直方圖看作一個一個的豎條的話,就是每一維上豎條的個數,
- ranges:用來進行統計的范圍 例如:float scope[] = {100, 255};const float *rangs[1] = { scope };那么就是對100-255的范圍的值進行統計,多個范圍可再定義陣列
- uniform:每一個豎條的寬度是否相等
- accumulate:是否累加,如果為true,在下次計算的時候不會首先清空直方圖
三、實作思路及效果
1、思路
- 定義引數變數并分離三通道
- calcHist函式計算B\G\R三個通道中各個通道的直方圖
- 創建畫布用來顯示直方圖
- normalize函式歸一化直方圖資料(歸一化到資料大小一致的范圍,為了不超出畫布允許的范圍)
- 繪制直方圖曲線(影像上的坐標轉換成螢屏中的坐標,即實際高度等于整個影像的高度減去像素點的高度,如圖所示)
- 顯示直方圖

2、效果

從直方圖可以看出,直方圖最左邊的一塊應該對應頭發那塊黑色,直方圖中最高的那一塊應該對應灰色的背景,臉部的亮度主要對應在直方圖右邊區域
四、代碼
void test1::hist_drawing(Mat &image)
{
// 分離三通道
vector<Mat> bgr_passage;
split(image, bgr_passage);
// 定義引數變數
const int channels[1] = { 0 };
const int bins[1] = { 256 };
float scope[2] = { 0,255 };
const float* ranges[1] = { scope };
Mat b_hist;
Mat g_hist;
Mat r_hist;
// 計算B, G, R通道的直方圖
calcHist(&bgr_passage[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
calcHist(&bgr_passage[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
calcHist(&bgr_passage[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
// 顯示直方圖
int hist_w = 512;
int hist_h = 400;
int bin_w = cvRound((double)hist_w / bins[0]);
Mat histImg = Mat::zeros(hist_h, hist_w, CV_8UC3);
// 歸一化直方圖資料
normalize(b_hist, b_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, histImg.rows, NORM_MINMAX, -1, Mat());
// 繪制直方圖曲線
for (int i = 1; i < bins[0]; i++)
{
line(histImg, 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);
line(histImg, 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);
line(histImg, 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);
}
// 顯示直方圖
// namedWindow("input", WINDOW_FREERATIO);
imshow("hist_drawing", histImg);
}
原創不易,轉載請注明出處:
Qt+OpenCV聯合開發(二十三)--影像直方圖
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/423417.html
標籤:AI


