直方圖均衡的C++實現方法

直方圖均衡的C++實現方法

一. 原理

灰度直方圖是灰度級的函數,描述的是圖像中具有該灰度級的像素的個數,即h(k)=nk(k=0,1,,L1)h(k)=n_k\quad (k=0,1,\cdots,L-1),其中LL爲圖像的灰度級數。

直方圖均衡的目的是將圖像的直方圖修正爲均勻分佈形式(離散分佈做不到完全均勻,但可以接近),增加像素灰度值的動態範圍,從而達到增強圖像對比度,提高清晰度的效果。

因而我們需要找到一種變換t=EH(s)t=E_{\rm H}(s)使得直方圖變平直,且要使變換後的灰度仍保持從黑到白的單一變化順序、變換範圍與原先一致。同時規定:

  • 0s10\le s \le 1EH(s)E_{\rm H}(s)單調遞增,且0EH(s)10 \le E_{\rm H}(s) \le 1
  • 0t10\le t \le 1,其反變換s=EH1(t)s = E^{-1}_{\rm H}(t)單調遞增。

具體方法如下:

  1. 設一幅圖像的像素總數爲nn,共有LL個灰度級,nkn_k爲第kk個灰度級出現的頻率。則第kk個灰度級直方圖均衡出現的概率爲
    p(sk)=nkn p(s_k) = \dfrac {n_k} n

  2. 計算累計概率
    tk=EH(sk)=i=0kp(sk),0tk1 t_{k}=E_{\mathrm{H}}\left(s_{k}\right)=\sum_{i=0}^{k} p\left(s_{k}\right), \quad 0 \leq t_{k} \leq 1
    ss的分佈轉換爲tt的均勻分佈;

  3. 將歸一化的tkt_k,映射回灰度值區間[0,L1][0,L-1]
    tk=int[(L1)tk+0.5] t_k = {\rm int}\left[ (L-1)t_k+0.5 \right]
    其中加0.5再取整即爲在程序中取四捨五入的算法;

  4. 對圖像中每一個像素的灰度值進行映射,得到均衡後的圖像。

下面將對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);
}

三. 實驗結果

實驗結果如下:


進行了直方圖均衡後的圖像

可以看到,相比原圖,直方圖均衡很好地實現了增強對比度、提高清晰度的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章