灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像元的个数。确定图像像元的灰度值范围,以适当的灰度间隔为单位将其划分为若干等级,以横轴表示灰度级,以纵轴表示每一灰度级具有的像元数或该像元数占总像元数的比例值,做出的条形统计图即为灰度直方图。
如下图所示,做直方图的过程:
- 直方图反映了图像中的灰度分布规律。它描述每个灰度级具有的像元个数,但不包含这些像元在图像中的位置信息。
- 任何一幅特定的图像都有唯一的直方图与之对应,但不同的图像可以有相同的直方图。
- 如果一幅图像有两个不相连的区域组成,并且每个区域的直方图已知,则整幅图像的直方图是该两个区域的直方图之和
- 对于每幅图像都可做出其灰度直方图。
- 根据直方图的形态可以大致推断图像质量的好坏。由于图像包含有大量的像元,其像元灰度值的分布应符合概率统计分布规律。假定像元的灰度值是随机分布的,那么其直方图应该是正态分布。
- 图像的灰度值是离散变量,因此直方图表示的是离散的概率分布。若以各灰度级的像元数占总像元数的比例值为纵坐标轴做出图像的直方图,将直方图中各条形的最高点连成一条外轮廓线,纵坐标的比例值即为某灰度级出现的概率密度,轮廓线可近似看成图像相应的连续函数的概率分布曲线。
直方图均衡化是将原图像的直方图通过变换函数变为均匀的直方图,然后按均匀直方图修改原图像,从而获得一幅灰度分布均匀的新图像。
计算过程如下:
- 统计原图像每一灰度级的像元数和累积像元数。
- 按下图公式计算变换后的值
- 四舍五入得到新的灰度值
- 统计像元
下图所示:
下面用Matlab实现其过程:
clc;clear;close all;
I=imread('flower.jpg'); //注意图像是灰度图像
subplot(1,2,1);
imshow(I)
title('原始图像')
subplot(1,2,2)
imhist(I);
title('直方图')
J=histeq(I);
figure;
subplot(1,2,1)
imshow(J)
title('直方图均衡化')
subplot(1,2,2)
imhist(J)
title('直方图')
结果如下图所示:很明显,直方图区域均匀分布。
用OpenCV实现过程如下:
#include "opencv2/opencv.hpp"
#include "opencv2/opencv_modules.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
using namespace cv;
using namespace std;
/*
计算直方图的函数
返回的是直方图的矩阵形式
输入是图像
*/
Mat histogramCal(const Mat& image) {
int histSize = 255; //直方图的最大像素值
float range[] = { 0,256 };
const float* histRange = { range };
vector bgr; //存储图像的矩阵
split(image, bgr); //将彩色图像分割成,b,g,r分别存储
bool uniform = true, accumulate = false;
Mat b_hist, g_hist, r_hist;
//分别计算各个波段的直方图
calcHist(&bgr[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate);
calcHist(&bgr[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate);
//绘制直方图
int hist_w = 512, hist_h = 400;
int bin_w = cvRound((double)hist_w / histSize);
Mat histImage(hist_h, hist_w, CV_8U, 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));
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));
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));
}
return histImage;
}
int main() {
Mat src;
src = imread("flower.png");
src.convertTo(src, CV_8U); //改变图像的深度
cout << src.channels() << endl;
namedWindow("origin", WINDOW_AUTOSIZE);
imshow("origin", src);
//imwrite("cute.jpg", src);
Mat histImage = histogramCal(src);
namedWindow("hist", WINDOW_AUTOSIZE);
imshow("hist", histImage);
Mat hist;
vector bgr;
split(src, bgr);
equalizeHist(bgr[0], bgr[0]);
equalizeHist(bgr[1], bgr[1]);
equalizeHist(bgr[2], bgr[2]);
//合并
Mat dst;
merge(bgr, dst);
namedWindow("equalize", WINDOW_AUTOSIZE);
imshow("equalize",dst);
Mat equalHist = histogramCal(dst);
namedWindow("equalhist", WINDOW_AUTOSIZE);
imshow("equalhist", equalHist);
waitKey();
}
结果如下图所示:
这里用的是彩色的图像,可以看到,均衡话后凸显出了彩色部分。