直方图均衡化
微信公众号:幼儿园的学霸
直方图均衡化(Histogram Equalization) 又称直方图平坦化,实质上是对图像进行非线性拉伸,重新分配图像象元值,使一定灰度范围内象元值的数量大致相等。这样,原来直方图中间的峰顶部分对比度得到增强,而两侧的谷底部分对比度降低,输出图像的直方图是一个较平的分段直方图:如果输出数据分段值较小的话,会产生粗略分类的视觉效果。
目录
前言
图像增强处理技术是数字图像处理的基本内容之一。图像增强是按特定的需要突出一幅图像中的某些信息,同时,削弱或去除某些不需要的信息的处理方法。
利用直方图统计的结果,通过使图像的直方图均衡的方法称为直方图均衡化,直方图均衡化可以达到增强图像显示效果的作用。其基本思想是把原始图像的直方图变换成均匀分布的形式,这样就增加了像素灰度值的动态范围,从而达到增强图像整体对比度的效果。
通过直方图统计,可以观察出,图像中各种亮度所占的比例大都分布不均匀,设法增加在直方图统计中所占比例高的像素和其他占的比例少的像素之间的亮度差,可以提高图像的显示效果。简单来说,直方图增强的方法就是压缩直方图中比例少的像素所占用的灰度范围,多出来的灰度空间按照统计比例分配给直方图中比例高的像素使用。这种方法主要是针对人眼对灰度差别越大的图像更容易分辨的特点而坐的增强。
直方图均衡化过程(算法):
(1) 列出原始图灰度级rk;
(2) 统计原始直方图各灰度级像素数nk;
(3) 计算原始直方图各概率:pk=nk/N;
(4) 计算累计直方图:sk=Σpk;
(5) 取整Sk=int{(L-1)sk+0.5};
(6) 确定映射对应关系:rk---->sk;
(7) 统计新直方图各灰度级像素nk’;
(8) 用pk (sk) =nk’/N计算新直方图。
其中L是灰度层次数, N是图幅总像素数。
直方图均衡化实现代码
根据上述流程可以实现图像的直方图均衡化,在OpenCV中已经封装了函数equalizeHist()可以直接使用,该函数的使用比较简单,输入参数是灰度图像,输出为直方图均衡化后的图像。此外,也可以根据上述流程自定义实现直方图均衡化,演示代码如下:
//====================================================================//
// Created by liheng on 19-3-12.
//Program:直方图均衡化Demo,演示采用自定义的函数实现均衡化和采用OpenCV函数实现均衡化
//Data:2019.3.12
//Author:liheng
//Version:V1.0
//====================================================================//
#include <opencv2/opencv.hpp>
//===================================================================//
//Histogram1D 计算一幅灰度图像的直方图,该类是对OpenCV的简单封装,参考网上资料
//Histogram1D提供了两个方法:
// getHistogram返回统计直方图的数组,默认计算的灰度范围是[0,255];
// getHistogramImage将图像的直方图以线条的形式画出来,并返回包含直方图的图像
//应用示例:
// Histogram1D hist;
// Mat histImg;
// histImg = hist.getHistogramImage(image);
//
// imshow("Image", image);
// imshow("Histogram", histImg);
//===================================================================//
class Histogram1D
{
private:
int histSize[1]; // 项的数量
float hranges[2]; // 统计像素的最大值和最小值
const float* ranges[1];
int channels[1]; // 仅计算一个通道
public:
Histogram1D()
{
// 准备1D直方图的参数
histSize[0] = 256;
hranges[0] = 0.0f;
hranges[1] = 255.0f;
ranges[0] = hranges;
channels[0] = 0;
}
cv::MatND getHistogram(const cv::Mat &image)
{
cv::MatND hist;
// 计算直方图
cv::calcHist(&image ,// 要计算图像的
1, // 只计算一幅图像的直方图
channels, // 通道数量
cv::Mat(), // 不使用掩码
hist, // 存放直方图
1, // 1D直方图
histSize, // 统计的灰度的个数
ranges); // 灰度值的范围
return hist;
}
cv::Mat getHistogramImage(const cv::Mat &image)
{
cv::MatND hist = getHistogram(image);
// 最大值,最小值
double maxVal = 0.0f;
double minVal = 0.0f;
cv::minMaxLoc(hist, &minVal, &maxVal);
//显示直方图的图像
cv::Mat histImg(histSize[0], histSize[0], CV_8UC1, cv::Scalar(255));
// 设置最高点为nbins的90%
int hpt = static_cast<int>(0.9 * histSize[0]);
//每个条目绘制一条垂直线
for (int h = 0; h < histSize[0]; h++)
{
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal * hpt / maxVal);
// 两点之间绘制一条直线
cv::line(histImg, cv::Point(h, histSize[0]), cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));
}
return histImg;
}
};
//===================================================================//
//自定义直方图均衡化函数,作用和equalizeHist()函数一致
//===================================================================//
void equalization_self(const cv::Mat &src, cv::Mat &dst)
{
Histogram1D hist1D;
cv::MatND hist = hist1D.getHistogram(src);
hist /= (src.rows * src.cols); // 对得到的灰度直方图进行归一化
float cdf[256] = { 0 }; // 灰度的累积概率
cv::Mat lut(1, 256, CV_8U); // 灰度变换的查找表
for (int i = 0; i < 256; i++)
{
// 计算灰度级的累积概率
if (i == 0)
cdf[i] = hist.at<float>(i);
else
cdf[i] = cdf[i - 1] + hist.at<float>(i);
lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 创建灰度的查找表
}
cv::LUT(src, lut, dst); // 应用查找表,进行灰度变化,得到均衡化后的图像
}
int main()
{
//cv::Mat src = cv::imread("../pictures/lena.jpg",0);
cv::Mat src = cv::imread("../pictures/000177.png",0);
cv::Mat dst_equalize,dst_self;
cv::equalizeHist(src,dst_equalize);
equalization_self(src,dst_self);
cv::imshow("srcImage",src);
cv::cvtColor(dst_equalize,dst_equalize,cv::COLOR_GRAY2BGR);
cv::cvtColor(dst_self,dst_self,cv::COLOR_GRAY2BGR);
cv::putText(dst_equalize,"opencv_equalize",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
cv::putText(dst_self,"dst_self",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
cv::hconcat(dst_equalize,dst_self,dst_equalize);
cv::resize(dst_equalize,dst_equalize,cv::Size(),0.5,0.5);
cv::imshow("dst",dst_equalize);
cv::waitKey(0);
return 0;
}
运行结果如图:
说明
直方图均衡化的优点是能自动地增强整个图像的对比度,但它的具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。实际中有时需要变换直方图使之成为某个特定的形状,从而有选择地增强某个灰度值范围内的对比度。这时候可以使用直方图规定化。