
本文代码使用OpenCV版本:2.4.13
本文代码在Win10+Visual Studio 2013 Update 3下测试通过
根据熵理论可知,直方图中,当h[0],h[1]…,h[n-1]相等时,图像信息量最大-》均衡化的目的是使每个hi都相等,即把原始图的直方图变换为均匀分布的形式,这样就增加了象素值的范围,增强了图像的对比效果。图像的直方图描述了一幅图像的概貌,在前述灰度变换的实例中(尤其是灰度拉伸),经过灰度变换之后,图像的灰度值在整个[0, 1] 灰度级上分布更加均匀,图像的显示效果明显改善。直方图均衡化的目标是期望输出图像的灰度概率密度函数均匀分布,在离 散情况下,即图像灰度的累积分布概率与灰度的取值区间长度成正比。

常 用的图像灰度变换方法主要有三种:灰度拉伸、直方图均衡化和灰度归一化。图像的直方图描述了一幅图像的概貌,在前述灰度变换的实例中(尤其是灰度拉伸),经过灰度变换之后,图像的灰度值在整个[0, 1] 灰度级上分布更加均匀,图像的显示效果明显改善。 (1)空域变换增强 常用的空域变换增强方法包括:对比度增强、直方图增强和图像算术运算等。
OpenCV提供了直方图均衡化的接口:
void equalizeHist(InputArray src, OutputArray dst);

其使用方法如下:
/////////////////////////////////////
// OpenCV实现直方图均衡化示例
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
int main()
{
// 读入图像,现在是3通道的RGB图像
cv::Mat image = cv::imread("G:/dataset/lena512.bmp");
if (image.empty())
{
return -1;
}
// 转换为单通道的灰度图像
cv::Mat grayImage;
cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
// 直方图均衡化
cv::Mat resultImage;
cv::equalizeHist(grayImage, resultImage);
// 显示原始图像及经过直方图均衡化的图像
cv::imshow("Original image", grayImage);
cv::imshow("Result image", resultImage);
cv::waitKey();
return 0;
}
原始图像及其直方图如下所示:


经过直方图均衡化的图像及其直方图如下所示:



从图像来看,对比度确实增强了;从直方图来看,确实使用了更多的灰度级,且相对来说更加平坦了。需要注意的是,由于灰度的离散性,直方图均衡化只能得到相对平坦的直方图。
直方图均衡化事实上是对灰度的重映射,将原本的灰度值映射为新的灰度值。要得到此映射函数,必须先从连续灰度说起。
考虑连续灰度值,用r代表原始图像的灰度,它的取值范围为[0, L-1],其中0代表黑白,L-1代表白色。用s代表经过直方图均衡化的图像的灰度,也就是r经过映射或变换后的灰度,则有变换函数:
从而,由该直方图得到的累积变换函数非常陡,因此才把灰度级低端过于集中的象素映射到了灰度级的高端。1.3 imadjust 函数功能:通过直方图变换调整对比度格式:j=imadjust(i,[low high],[bottom top],gamma) newmap=imadjust(map,[low high],[bottom top],gamma)说明:j=imadjust(i,[low high],[bottom top],gamma) 其中,gamma 为校正量r,[low high] 为原图像中要变换的灰度范围opencv直方图均衡化,[bottom top]。指定了变换后的灰度范围。
下面重述这两个条件:
(a) T(r)在区间[0, L-1]上为单调递增函数;如果需要得到T(r)的反函数,则要求其为严格单调递增函数。

(b) 当0≤r≤L−1时,0≤T(r)≤L−1。
为了进一步得到函数关系,我们将一幅图像的灰度级看成是区间[0, L-1]内的随机变量。描述随机变换的基本描绘子是其概率密度函数(Probability Density Function, PDF)。令pr(r)和ps(s)分别表示随机变量r和s的PDF。由基本概率论的知识可以知道,如果pr(r)和T(r)已知,且在感兴趣的区域上T(r)是连续且可微的,则变换后的变量s的PDF可由下面的简单公式得到:
由于要均衡化的直方图是均匀分布的,所以它的PDF是已知的,即
于是有:
最终得到了T(r)的变换函数,它将任意的PDF转换为均匀的PDF。如下图所示:
。
可以验证,它满足条件(a)和(b)。

得到连续灰度的变换函数后,就可以将其应用到离散的情境下。对于离散值,我们处理其概率(直方图值)与求和来替代PDF与积分。正如前面提到的那样,一幅数学图像中灰度级rk出现的概率近似为
其中,MN是图像像素的总数。
而变换T(r)的离散形式为
这样,我们就得到了将原始图像的灰度值映射为新灰度值的映射函数。它使用了累积直方图(∑kj=0nj)。
以下的C++代码描述了上述过程:
/////////////////////////////////////
// OpenCV直方图均衡化C++代码示例
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
// 计算直方图
cv::Mat calcHist(const cv::Mat& image)
{
CV_Assert(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
cv::Mat hist;
int channels[1] = { 0 };
int histSize = 256;
float range[2] = { 0, 256 };
const float* ranges[1] = { range };
cv::calcHist(&image, 1, channels, cv::Mat(), hist, 1, &histSize, ranges);
return hist;
}
// 计算累积计算图(累积分布函数)
cv::Mat calcCDF(const cv::Mat& hist)
{
CV_Assert(!hist.empty() && hist.cols == 1 && hist.depth() == CV_32F);
cv::Mat cdf(hist.size(), CV_32FC1);
cdf.at<float>(0, 0) = hist.at<float>(0, 0);
float total = cdf.at<float>(0, 0);
for (int i = 1; i < hist.rows; ++i)
{
total += hist.at<float>(i, 0);
cdf.at<float>(i, 0) = total;
}
for (int i = 0; i < cdf.rows; ++i)
{
cdf.at<float>(i, 0) = cdf.at<float>(i, 0) / total;
}
return cdf;
}
// 根据累积直方图计算查找表
cv::Mat calcLUT(const cv::Mat& cdf)
{
CV_Assert(!cdf.empty() && cdf.cols == 1);
cv::Mat lut(1, cdf.rows, CV_8UC1);
for (int i = 0; i < cdf.rows; ++i)
{
lut.at<uchar>(0, i) = static_cast<uchar>(std::round(cdf.at<float>(i, 0) * 255));
}
return lut;
}
// 直方图均衡化
cv::Mat histogramEqulization(const cv::Mat& image)
{
CV_Assert(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
cv::Mat lut = calcLUT(calcCDF(calcHist(image)));
cv::Mat eqImage;
cv::LUT(image, lut, eqImage);
return eqImage;
}
int main()
{
///////////////////////////////////////////////////////
// 直方图均衡化
// 读入图像,此时是3通道的RGB图像
cv::Mat image = cv::imread("G:/dataset/lena512.bmp");
if (image.empty())
{
return -1;
}
// 转换为单通道的灰度图像
cv::Mat grayImage;
cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);
// 直方图均衡化
cv::Mat imageEqualizedMy = histogramEqulization(grayImage);
// 显示图像
cv::imshow("result image my", imageEqualizedMy);
cv::waitKey();
return 0;
}
经对比opencv直方图均衡化,此代码的结果与OpenCV实现的函数的结果完全相同,不再展示。
《OpenCV 2 视觉编程手册》4.4节
《数字图像处理(第三版》3.3.1节
【数字图像处理】直方图均衡化详解及编程实现
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-117995-1.html
你读书读狗肚子里了