影象處理學習筆記之直方圖的計算與繪製
阿新 • • 發佈:2019-01-06
影象直方圖包含豐富的影象細節資訊,反映了影象畫素點的概率分佈情況,它統計了每一個強度值具有的畫素個數。灰度級範圍是[0,L-1]的數字影象的直方圖是離散函式h(rk)=nk,其中是rk第k級灰度值,nk是影象中灰度為rk的畫素個數。在實踐中,經常用乘積MN表示的影象畫素總數除它的每個分量來歸一化直方圖,M、N是影象的行列數。因此歸一化後的直方圖由p(rk)=nk/MN給出。直方圖的橫座標表示灰度級,縱座標表示影象中該灰度級出現的次數(頻率)。
一般來說,在暗影象中,直方圖的分量集中在灰度級較低的一側。亮影象的直方圖分量集中在灰度級值較高的一側。低對比度的影象具有較窄的直方圖,且集中於灰度級的中部。高對比度的影象中直方圖的分量覆蓋了很寬的灰度級範圍。
圖1 亮影象及其灰度直方圖
圖2 暗影象及其灰度直方圖
圖3 高對比度影象及其灰度直方圖
圖4 低對比度影象及其灰度直方圖
opencv中提供了calchist函式用於計算影象的直方圖。其宣告如下:
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:源輸入影象陣列,可以是多幅影象,所有的影象必須有同樣的深度(CV_8U or CV_32F),同時一副影象可以有多個channes。
- narrays:源輸入陣列中的元素個數
- channels:用來計算直方圖的通道維數陣列,第一個陣列的通道由0到arrays[0].channels()-1列出,第二個陣列的通道從arrays[0].channels()到arrays[0].channels()+arrays[1].channels()-1以此類推
- mask:可選的掩膜,如果該矩陣不是空的,則必須是8位的並且與arrays[i]的大小相等,掩膜的非零值標記需要在直方圖中統計的陣列元素;
- hist:輸出直方圖,是一個稠密或者稀疏的dims維的陣列
- dims:直方圖的維數,必須為正,並且不大於CV_MAX_DIMS(當前的OpenCV版本中為32,即最大可以統計32維的直方圖);
- histSize:用於指出直方圖陣列每一維的大小的陣列,即指出每一維的bin的個數的陣列
- ranges:用於指出直方圖每一維的每個bin的上下界範圍陣列的陣列,當直方圖是均勻的(uniform =true)時,對每一維i指定直方圖的第0個bin的下界(包含即[)L0和最後一個即第histSize[i]-1個bin的上界(不包含的即))U_histSize[i]-1,也就是說對均勻直方圖來說,每一個ranges[i]都是一個兩個元素的陣列【指出該維的上下界】。當直方圖不是均勻的時,每一個ranges[i]陣列都包含histSize[i]+1個元素:L0,U0=L1,U1=L1,...,U_histSize[i]-2
= L_histSize[i]-1,U_histSize[i]-1.不在L0到U_histSize[i]-1之間的陣列元素將不會統計進直方圖中
- uniform:直方圖是否均勻的標誌;【指定直方圖每個bin統計的是否是相同數量的灰度級】
- accumulate:累加標
int main()
{
Mat src, dst;
src = imread("1.jpg");
if (!src.data)
{
return -1;
}
/// 通道分離
vector<Mat> bgr_planes;
split(src, bgr_planes);
int histSize = 256;
/// 設定範圍
float range[] = { 0, 256 };
const float* histRange = { range };
bool uniform = true; bool accumulate = false;
Mat b_hist, g_hist, r_hist;
/// 計算直方圖:
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);
// 建立畫布
int hist_w = 512; int hist_h = 400;
int bin_w = cvRound((double)hist_w / histSize);
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
/// 歸一化到 [ 0, histImage.rows ]
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
/// 畫直方圖
for (int i = 1; i < histSize; i++)
{
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);
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);
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);
}
namedWindow("calcHist Demo", CV_WINDOW_AUTOSIZE);
imshow("calcHist Demo", histImage);
waitKey(0);
return 0;
}