直方圖均衡的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];
int greyFreq[256] = { 0 };
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];
/* 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);
}
三. 實驗結果
實驗結果如下:
可以看到,相比原圖,直方圖均衡很好地實現了增強對比度、提高清晰度的功能。