1. 程式人生 > >影象處理學習筆記之直方圖的計算與繪製

影象處理學習筆記之直方圖的計算與繪製

影象直方圖包含豐富的影象細節資訊,反映了影象畫素點的概率分佈情況,它統計了每一個強度值具有的畫素個數。灰度級範圍是[0,L-1]的數字影象的直方圖是離散函式h(rk)=nk,其中是rkk級灰度值,nk是影象中灰度為rk的畫素個數。在實踐中,經常用乘積MN表示的影象畫素總數除它的每個分量來歸一化直方圖,MN是影象的行列數。因此歸一化後的直方圖由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;
}