直方图均衡的C++实现方法
一. 原理
灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数,即 ,其中 为图像的灰度级数。
直方图均衡的目的是将图像的直方图修正为均匀分布形式(离散分布做不到完全均匀,但可以接近),增加像素灰度值的动态范围,从而达到增强图像对比度,提高清晰度的效果。
因而我们需要找到一种变换 使得直方图变平直,且要使变换后的灰度仍保持从黑到白的单一变化顺序、变换范围与原先一致。同时规定:
- 在 , 单调递增,且 ;
- 在 ,其反变换 单调递增。
具体方法如下:
-
设一幅图像的像素总数为 ,共有 个灰度级, 为第 个灰度级出现的频率。则第 个灰度级直方图均衡出现的概率为
-
计算累计概率
将 的分布转换为 的均匀分布; -
将归一化的 ,映射回灰度值区间 :
其中加0.5再取整即为在程序中取四舍五入的算法; -
对图像中每一个像素的灰度值进行映射,得到均衡后的图像。
下面将对seed.yuv进行直方图均衡。
原始图像
二. 实验代码
declarations.h
#pragma once
extern int w;
extern int h;
void Freq(unsigned char* yBuff, double freq[]);
void CumulativeFreq(double prob[], double cumProb[]);
void Mapping(double cumProb[], unsigned char* yBuffOri, unsigned char* yBuffEqu);
global.cpp
#include "declarations.h"
#include <iostream>
int w = 500;
int h = 500;
void Freq(unsigned char* yBuff, double prob[])
{
double count[256] = { 0 };
for (int i = 0; i < w * h; i++)
{
int greyIndex = (int)yBuff[i];
count[greyIndex]++;
}
for (int i = 0; i < 256; i++)
{
prob[i] = count[i] / (w * h);
//printf("%-5d%lf\n", i, prob[i]);
}
}
void CumulativeFreq(double prob[], double cumProb[])
{
cumProb[0] = 0;
//printf("%-5d%lf\n", 0, cumProb[0]);
for (int i = 1; i < 256; i++)
{
cumProb[i] = cumProb[i - 1] + prob[i - 1];
//printf("%-5d%lf\n", i, cumProb[i]);
}
}
void Mapping(double cumProb[], unsigned char* yBuffOri, unsigned char* yBuffEqu)
{
for (int i = 0; i < 256; i++)
{
cumProb[i] = floor(255 * cumProb[i] + 0.5);
}
for (int i = 0; i < w * h; i++)
{
int greyIndex = (int)yBuffOri[i];
yBuffEqu[i] = cumProb[greyIndex];
}
}
main.cpp
#include <iostream>
#include "declarations.h"
using namespace std;
int main(int argc, char* argv[])
{
FILE* oriImgPtr;
FILE* equImgPtr;
const char* oriImgName = argv[1];
const char* equImgName = argv[2];
double greyProb[256] = { 0 };
double greyCumProb[256] = { 0 };
/* Open the files */
if (fopen_s(&oriImgPtr, oriImgName, "rb") == 0)
{
cout << "Successfully opened \"" << oriImgName << "\"." << endl;
}
else
{
cout << "Failed to open \"" << oriImgName << "\"." << endl;
exit(-1);
}
if (fopen_s(&equImgPtr, equImgName, "wb") == 0)
{
cout << "Successfully opened \"" << equImgName << "\"." << endl;
}
else
{
cout << "Failed to open \"" << equImgName << "\"." << endl;
exit(-1);
}
/* Space allocation */
unsigned char* oriYBuff = new unsigned char[w * h];
unsigned char* equYBuff = new unsigned char[w * h];
unsigned char* equUBuff = new unsigned char[w * h / 4];
unsigned char* equVBuff = new unsigned char[w * h / 4];
int* greyFreq = new int[256];
/* Initialisation of U & V component (greyscale image) */
memset(equUBuff, 128, w * h / 4);
memset(equVBuff, 128, w * h / 4);
/* Read Y component into the buffer */
fread(oriYBuff, sizeof(unsigned char), w * h, oriImgPtr);
/* Calculate probabilities of each grey value */
Freq(oriYBuff, greyProb);
/* Calculate cumulative probabilites of each grey value */
CumulativeFreq(greyProb, greyCumProb);
/* Mapping */
Mapping(greyCumProb, oriYBuff, equYBuff);
/* Write histogram-equalised data into the new file */
fwrite(equYBuff, sizeof(unsigned char), w * h, equImgPtr);
fwrite(equUBuff, sizeof(unsigned char), w * h / 4, equImgPtr);
fwrite(equVBuff, sizeof(unsigned char), w * h / 4, equImgPtr);
delete[]oriYBuff;
delete[]equYBuff;
delete[]equUBuff;
delete[]equVBuff;
fclose(oriImgPtr);
fclose(equImgPtr);
}
三. 实验结果
实验结果如下:
进行了直方图均衡后的图像
可以看到,相比原图,直方图均衡很好地实现了增强对比度、提高清晰度的功能。