直方图均衡的C++实现方法

直方图均衡的C++实现方法

一. 原理

灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数,即 h ( k ) = n k ( k = 0 , 1 , ⋯   , L − 1 ) h(k)=n_k\quad (k=0,1,\cdots,L-1) h(k)=nk(k=0,1,,L1),其中 L L L为图像的灰度级数。

直方图均衡的目的是将图像的直方图修正为均匀分布形式(离散分布做不到完全均匀,但可以接近),增加像素灰度值的动态范围,从而达到增强图像对比度,提高清晰度的效果。

因而我们需要找到一种变换 t = E H ( s ) t=E_{\rm H}(s) t=EH(s)使得直方图变平直,且要使变换后的灰度仍保持从黑到白的单一变化顺序、变换范围与原先一致。同时规定:

  • 0 ≤ s ≤ 1 0\le s \le 1 0s1 E H ( s ) E_{\rm H}(s) EH(s)单调递增,且 0 ≤ E H ( s ) ≤ 1 0 \le E_{\rm H}(s) \le 1 0EH(s)1
  • 0 ≤ t ≤ 1 0\le t \le 1 0t1,其反变换 s = E H − 1 ( t ) s = E^{-1}_{\rm H}(t) s=EH1(t)单调递增。

具体方法如下:

  1. 设一幅图像的像素总数为 n n n,共有 L L L个灰度级, n k n_k nk为第 k k k个灰度级出现的频率。则第 k k k个灰度级直方图均衡出现的概率为
    p ( s k ) = n k n p(s_k) = \dfrac {n_k} n p(sk)=nnk

  2. 计算累计概率
    t k = E H ( s k ) = ∑ i = 0 k p ( s k ) , 0 ≤ t k ≤ 1 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 tk=EH(sk)=i=0kp(sk),0tk1
    s s s的分布转换为 t t t的均匀分布;

  3. 将归一化的 t k t_k tk,映射回灰度值区间 [ 0 , L − 1 ] [0,L-1] [0,L1]
    t k = i n t [ ( L − 1 ) t k + 0.5 ] t_k = {\rm int}\left[ (L-1)t_k+0.5 \right] tk=int[(L1)tk+0.5]
    其中加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);
}

三. 实验结果

实验结果如下:


进行了直方图均衡后的图像

可以看到,相比原图,直方图均衡很好地实现了增强对比度、提高清晰度的功能。

#include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX_PIXEL 255 #define MIN_PIXEL 0 int main(int argc, char *argv[]) { if (argc != 3) { printf("Usage: %s input_image output_image\n", argv[0]); return 1; } FILE *input_file = fopen(argv[1], "rb"); FILE *output_file = fopen(argv[2], "wb"); if (input_file == NULL || output_file == NULL) { printf("Error: Could not open file\n"); return 1; } //读取位图文件头,位图信息头,颜色表 unsigned char bmp_header[54]; fread(bmp_header, sizeof(unsigned char), 54, input_file); //获取位图宽度,高度,每个像素的颜色位数 int width = *(int*)&bmp_header[18]; int height = *(int*)&bmp_header[22]; int color_depth = *(int*)&bmp_header[28]; //计算每行像素的字节数 int row_size = width * color_depth / 8; if (row_size % 4 != 0) { row_size += 4 - (row_size % 4); } //读取像素数据 unsigned char *image_data = (unsigned char*)malloc(row_size * height); fread(image_data, sizeof(unsigned char), row_size * height, input_file); //计算直方图 int histogram[MAX_PIXEL + 1] = {0}; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int index = i * row_size + j * color_depth / 8; int pixel = image_data[index]; histogram[pixel]++; } } //计算累积分布函数 int cdf[MAX_PIXEL + 1] = {0}; cdf[0] = histogram[0]; for (int i = 1; i <= MAX_PIXEL; i++) { cdf[i] = cdf[i - 1] + histogram[i]; } //计算像素值映射表 int map[MAX_PIXEL + 1] = {0}; float scale_factor = (float)(MAX_PIXEL - MIN_PIXEL) / (width * height); for (int i = 0; i <= MAX_PIXEL; i++) { map[i] = round((cdf[i] - cdf[0]) * scale_factor); } //应用像素值映射表 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int index = i * row_size + j * color_depth / 8; int pixel = image_data[index]; int new_pixel = map[pixel]; image_data[index] = new_pixel; } } //写入位图文件头,位图信息头,颜色表 fwrite(bmp_header, sizeof(unsigned char), 54, output_file); //写入像素数据 fwrite(image_data, sizeof(unsigned char), row_size * height, output_file); //释放内存 free(image_data); //关闭文件 fclose(input_file); fclose(output_file); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值