图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分;而整体明亮、只有少量阴影的图像则相反,数据多集中在右侧和中间部分。很多数码相机提供图像直方图功能,拍摄者可以通过观察图像直方图了解到当前图像是否过分曝光或者曝光不足。计算机视觉领域常借助图像直方图来实现图像的二值化,其是图像像素值的统计学特征、计算代价较小,具有图像平移、旋转、缩放不变性等众多优点,广泛地应用于图像处理的各个领域,特别是灰度图像的阈值分割、基于颜色的图像检索以及图像分类、反向投影跟踪。常见的分为“灰度直方图”和“颜色直方图”这两种。
下图是OpenCV中的图像直方图测试结果:
直方图中横坐标为灰度值级数,以8位单通道的灰度图像为例,其横坐标范围为【0~255】的闭区间;纵坐标为图像在每个灰度级的像素数,一般要做归一化处理。
CV_EXPORTS void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform = true, bool accumulate = false );
每个参数具体含义如下:
图像直方图均衡化可以用于图像增强、对输入图像进行直方图均衡化处理,提升后续对象检测的准确率,在OpenCV人脸检测的代码演示中已经很常见。此外对医学影像图像与卫星遥感图像也经常通过直方图均衡化来提升图像质量。简单来说,就是通过调整图像的灰阶分布,使得像素点在0~255灰阶上的分布更加均衡,提高了图像的对比度,达到改善图像主观视觉效果的目的。对比度较低的图像适合使用直方图均衡化方法来增强图像细节。
下图是OpenCV中的图像直方图均衡化测试结果:
可以看出,上图中左侧对比度较低的图像经过直方图均衡化处理后就变为了右侧对比度比较强的图像。
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
使用方法比较简单,每个参数具体含义如下:
// HistogramEqualization.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat srcImage, GrayImage, GrayHisEquImg, GrayHisImg, GrayEquImg, Histogram1, Histogram2;
srcImage = imread("star.jpg");
if (nullptr == srcImage.data)
{
cout << "图片加载失败!" << endl;
return 0;
}
cvtColor(srcImage, GrayImage, COLOR_BGR2GRAY); //转换灰度图
/*直方图均衡化*/
GrayHisEquImg.create(GrayImage.rows, GrayImage.cols, GrayImage.type());
GrayHisEquImg.setTo(0);
equalizeHist(GrayImage, GrayHisEquImg);
/** 创建直方图,一维, 每个维度上均分 */
int HisImgWidth = 512;
int HisImgHeight = 400;
const int bins = 256;
float range[] = { 0, 255 };
const float *ranges[] = { range };
const int channel = 0;
GrayHisImg.create(HisImgHeight, HisImgWidth, CV_8UC1);
GrayHisImg.setTo(255);
GrayEquImg.create(HisImgHeight, HisImgWidth, CV_8UC1);
GrayEquImg.setTo(255);
calcHist(&GrayImage, 1, &channel, Mat(), Histogram1, 1, &bins, ranges, true, false);
calcHist(&GrayHisEquImg, 1, &channel, Mat(), Histogram2, 1, &bins, ranges, true, false);
// 归一化直方图数据
normalize(Histogram1, Histogram1, 0, GrayHisImg.rows, NORM_MINMAX, -1, Mat());
normalize(Histogram2, Histogram2, 0, GrayEquImg.rows, NORM_MINMAX, -1, Mat());
// 绘制柱状直方图
for (int i = 1; i < bins; i++)
{
rectangle(GrayHisImg, Point(2*(i-1), HisImgHeight - 1), Point(2*i, HisImgHeight - cvRound(Histogram1.at<float>(i-1))), Scalar(0, 0, 0), 2, 8, 0);
rectangle(GrayEquImg, Point(2*(i-1), HisImgHeight - 1), Point(2*i, HisImgHeight - cvRound(Histogram2.at<float>(i-1))), Scalar(0, 0, 0), 2, 8, 0);
}
namedWindow("src");
namedWindow("GrayImage");
namedWindow("GrayHisEquImg");
namedWindow("GrayHisImg");
namedWindow("GrayEquImg");
imshow("src", srcImage);
imshow("GrayImage", GrayImage);
imshow("GrayHisEquImg", GrayHisEquImg);
imshow("GrayHisImg", GrayHisImg);
imshow("GrayEquImg", GrayEquImg);
while (true)
{
if (waitKey(10) == 27)
{
break;
}
}
destroyAllWindows();
return 1;
}