直方图均衡化本质就是找到一个映射函数T,将图像的每个灰度值映射到新的灰度值空间。
对T的要求是单调递增,映射后值域一般在[0, 255],或根据实际图像的值域确定映射后图像的值域。
实际操作中选用原图的直方图的概率密度函数累积函数作为T。
直方图规定化本质是得到直方图均衡化的图像之后,再利用目标图像的概率密度函数累积函数作为映射函数对原图均衡化后的图像进行映射。
#include <opencv2/highgui.hpp>
#include <map>
int main()
{
const int kGrayLevel = 256; /* 灰度阶数目,图像灰度值取值范围[0, 256-1];
该值可根据实际情况灵活选用,可选用常用的灰度图范围[0,255],
也可根据实际情况选择所处理图像的实际灰度值范围 */
cv::Mat img = cv::imread("src.jpg", 0); // 原图
int img_width = img.cols;
int img_height = img.rows;
cv::Mat img_equalization(img_height, img_width, CV_8UC1); // 直方图均衡化后的图
// 原图灰度值统计,计算出每个灰度阶像素个数,即直方图
std::map<int, int> hist;
for (int i = 0; i < kGrayLevel; ++i)
{
hist[i] = 0;
}
int min_val = INT_MAX;
int max_val = -INT_MAX;
for (int i = 0; i < img_height; ++i)
{
for (int j = 0; j < img_width; ++j)
{
int val = img.at<uchar>(i, j);
hist[val]++;
if (min_val > val)
{
min_val = val;
}
if (max_val < val)
{
max_val = val;
}
}
}
// 求对应概率,即直方图的概率表示,亦即输入图像的概率密度函数
int area = img_width * img_height;
std::map<int, float> hist_prob;
for (auto it : hist)
{
hist_prob[it.first] = it.second * 1.0f / area;
}
std::map<int, float> sum_hist_prob; // 直方图的概率密度函数累积函数
for (int i = 0; i < kGrayLevel; ++i)
{
float sum = 0.0f;
for (int j = 0; j <= i; ++j)
{
sum += hist_prob[j];
}
sum_hist_prob[i] = sum;
}
/* 使用原图的概率密度函数累积函数对原图进行映射得到直方图均衡化的图像,
其中(max_val - min_val) + min_val也可以直接改为只乘以一般灰度图的灰度值的最大值256,需根据实际情况选择 */
for (int i = 0; i < img_height; ++i)
{
for (int j = 0; j < img_width; ++j)
{
img_equalization.at<uchar>(i, j) =
sum_hist_prob[img.at<uchar>(i, j)] * (max_val - min_val) + min_val;
}
}
cv::imwrite("img_equalization.jpg", img_equalization);
/************************************************************************/
/* 该分界线以上是直方图均衡化部分,以下是直方图规定化部分 */
/************************************************************************/
cv::Mat img_dst = cv::imread("dst.jpg", 0);
cv::Mat img_specification(img_height, img_width, CV_8UC1);
// 求目标图像的灰度值统计,直方图的像素个数表示
std::map<int, int> hist_dst;
for (int i = 0; i < kGrayLevel; ++i)
{
hist_dst[i] = 0;
}
min_val = INT_MAX;
max_val = -INT_MAX;
for (int i = 0; i < img_dst.rows; ++i)
{
for (int j = 0; j < img_dst.cols; ++j)
{
int val = img_dst.at<uchar>(i, j);
hist_dst[val]++;
if (min_val > val)
{
min_val = val;
}
if (max_val < val)
{
max_val = val;
}
}
}
// 求对应直方图的概率表示亦即概率密度函数
std::map<int, float> hist_prob_dst;
for (auto it : hist_dst)
{
hist_prob_dst[it.first] = it.second * 1.0f / area;
}
// 求目标图像的概率密度函数累积函数
std::map<int, float> sum_hist_prob_dst;
for (int i = 0; i < kGrayLevel; ++i)
{
float sum = 0.0f;
for (int j = 0; j <= i; ++j)
{
sum += hist_prob_dst[j];
}
sum_hist_prob_dst[i] = sum;
}
/* 使用目标图像的概率密度函数累积函数对原图的均衡化后的图像进行映射得到直方图规定化的图像,
其中(max_val - min_val) + min_val也可以直接改为只乘以一般灰度图的灰度值的最大值256,需根据实际情况选择 */
for (int i = 0; i < img_height; ++i)
{
for (int j = 0; j < img_width; ++j)
{
img_specification.at<uchar>(i, j) =
sum_hist_prob_dst[img_equalization.at<uchar>(i, j)] * (max_val - min_val) + min_val;
}
}
cv::imwrite("img_specification.jpg", img_specification);
return 0;
}
原图:
原图直方图均衡化后的图:
目标图:
原图向目标图直方图均衡化后的图: