图像处理之直方图均衡化及C源码实现

时间:2023-03-09 00:02:28
图像处理之直方图均衡化及C源码实现

1 直方图均衡化(Histogram Equalization)简介

  图像对比度增强的方法可以分成两类:一类是直接对比度增强方法;另一类是间接对比度增强方法。直方图拉伸和直方图均衡化是两种最常见的间接对比度增强方法。直方图拉伸是通过对比度拉伸对直方图进行调整,从而“扩大”前景和背景灰度的差别,以达到增强对比度的目的,这种方法可以利用线性或非线性的方法来实现;直方图均衡化则通过使用累积函数对灰度值进行“调整”以实现对比度的增强。

  如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像原取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。

2 直方图均衡化原理

  设变量r代表图像中像素的灰度级,直方图变换就是假定一个变换式:

                             图像处理之直方图均衡化及C源码实现

  也就是,通过上述变换,每个原始图像的像素灰度级r都会产生一个s值。变换函数T(r)应满足以下条件:

  (1) T(r)在区间图像处理之直方图均衡化及C源码实现中为单值且单调递增;

  (2) 当 图像处理之直方图均衡化及C源码实现时,图像处理之直方图均衡化及C源码实现即T(r)的取值范围与r相同。

  直方图均衡化:对于离散值,我们处理其概率与求和,而不是概率密度函数与积分。一幅图像中灰度级rk出现的概率近似为

                          图像处理之直方图均衡化及C源码实现

  其中,n是图像中像素的总和, 是灰度级 的像素个数,L为图像中可能的灰度级总数。

                            图像处理之直方图均衡化及C源码实现

上式中变换函数的离散形式为:

                    图像处理之直方图均衡化及C源码实现

  式给出的变换(映射)称为直方图均衡化或直方图线性化。

   根据上面公式推导,直方图均衡化步骤如下:

  (1) 统计原图每灰度级像素个数

  (2) 统计原图像每灰度级像素的累积个数

  (3) 建立灰度级的映射规则

  (4) 将原图每个像素点的灰度映射到新图

3 直方图均衡化优缺点

  这种方法对于背景和前景都太亮或者太暗的图像非常有用,这种方法尤其是可以带来X光图像中更好的骨骼结构显示以及曝光过度或者曝光不足照片中更好的细节。这种方法的一个主要优势是它是一个相当直观的技术并且是可逆操作,如果已知均衡化函数,那么就可以恢复原始的直方图,并且计算量也不大。

  这种方法的一个缺点是它对处理的数据不加选择,它可能会增加背景杂讯的对比度并且降低有用信号的对比度;变换后图像的灰度级减少,某些细节消失;某些图像,如直方图有高峰,经处理后对比度不自然的过分增强。

4 直方图均衡化源码实现

 #include <windows.h>
#include <stdafx.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include<conio.h>
#pragma pack(1) typedef unsigned char BYTE;
typedef short unsigned int WORD;
typedef unsigned long DWORD; typedef struct BMP_FILEHEADER
{//定义bmp文件头 WORD bmpType;/*位图标识*/
DWORD bmpSize;/*说明文件的大小,用字节为单位*/
DWORD bmpReserved;/*保留,必须设置为0*/
DWORD bmpOffset;/*从文件头开始到实际的图象数据之间的字节的偏移量*/
DWORD bmpHeaderSize;/*说明BITMAP_INFOHEADER结构所需要的字数*/
DWORD bmpWidth;/*说明图象的宽度,以象素为单位*/
DWORD bmpHeight;/*说明图象的高度,以象素为单位.大多数的BMP文件都是倒向的位图*/
WORD bmpPlanes;/*为目标设备说明位面数,其值将总是被设为1*/ } BMP_FILEHEADER; typedef struct BMP_INFOHEADER
{//定义bmp信息头 WORD bitsPerPixel;/*说明比特数/象素*/
DWORD bmpCompression;/*说明图象数据压缩的类型*/
DWORD bmpDataSize; /*说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0*/
DWORD bmpHResolution;/*说明水平分辨率,用象素/米表示*/
DWORD bmpVResolution;/*说明垂直分辨率,用象素/米表示*/
DWORD bmpColors;/*实际使用的颜色数,0说明使用所有调色板项*/
DWORD bmpImportantColors;/*重要影响的颜色索引的数目,如果是0,表示都重要*/ } BMP_INFOHEADER; typedef struct RGBPALETTE
{//定义8位颜色表 BYTE rgbBlue;/*指定蓝色强度*/
BYTE rgbGreen;/*指定绿色强度*/
BYTE rgbRed;/*指定红色强度*/
BYTE rgbReserved;/*保留,设为0*/ } RGBPALETTE; typedef struct RGBPALETTE_24
{//定义24位颜色表
BYTE rgbBlue;/*指定蓝色强度*/
BYTE rgbGreen;/*指定绿色强度*/
BYTE rgbRed;/*指定红色强度*/
}RGBPALETTE_24; typedef struct BMPFILEPTR
{//定义bmp文件指针类型
BMP_FILEHEADER *bmpFileHeader;//bmp文件头指针
BMP_INFOHEADER *bmpInfoHeader;//bmp信息头指针
RGBPALETTE *bmpColorTable;//颜色表指针
unsigned char *bmpDataPtr;//bmp数据指针
}BMPFILEPTR; typedef struct HE
{//定义直方图均衡化结构
float ohs[];//原始灰度级概率
float integs[];//原始灰度概率累计值
}HE; long FileLength(FILE *fp);//返回文件长度
void WriteData(unsigned char *pBmpData,FILE *fp);//写数据,将文件数据写入pBmpData所指内存单元
unsigned char* MemoryAlloc(int length);//分配length长度的内存单元
void MemoryFree(unsigned char *pBmpData);//释放内存单元
int GetPixelColor(BMPFILEPTR bmpFile,unsigned int index,int bitsperpixel);//获得颜色值
void InputPicture(BMPFILEPTR *bmpFile,unsigned char *pBmpData,FILE *fp);//输入图片,文件指针初始化
void DisplayPicture(HDC hdc,BMPFILEPTR bmpFile,POINT spoint);//显示图片,将图片显示在画板上
void ComputeOriginalProbabilty(BMPFILEPTR bmpFile,HE *hequal);//计算原始图片灰度级概率
void ComputeEqualizedProbabilty(BMPFILEPTR bmpFile,HE *hequal);//计算均衡化后的灰度级概率
void GenerateEqualizedBmpFile(BMPFILEPTR bmpFile,FILE *fp1,HE hequal);//生成均衡化之后的bmp文件 long WINAPI WndProc(HWND,UINT,UINT,LONG); //处理消息响应的函数
BOOL InitWindowsClass(HINSTANCE); //初始化窗口类
BOOL InitWindows(HINSTANCE,int); //初始化窗口函数
HWND hWndMain;//窗口句柄 long FileLength(FILE *fp)
{//返回文件长度
long curpos, length; curpos = ftell(fp);
fseek(fp, 0L, SEEK_END);
length = ftell(fp);
fseek(fp, curpos, SEEK_SET);
return length;
} unsigned char* MemoryAlloc(int length)
{//按照length长度分配内存单元,返回内存首地址
unsigned char *pBmpData; if(NULL==(pBmpData=(unsigned char*)malloc(length*sizeof(unsigned char))))
{
MessageBox(NULL,(LPCWSTR )"memory allocation failure,error!",NULL,NULL);
exit();
}
return pBmpData;
} void MemoryFree(unsigned char *pBmpData)
{//释放内存单元
free(pBmpData);
} void WriteData(unsigned char *pBmpData,FILE *fp)
{//写数据,将文件数据写入字节流中
int ind=;
fseek(fp, , SEEK_SET);
while(!feof(fp))
{
pBmpData[ind++]=fgetc(fp);
}
} int GetPixelColor(BMPFILEPTR bmpFile,unsigned int index,int bitsperpixel)
{//取像素点的颜色值,在VC++中颜色值是0x00bbggrr,返回颜色值
unsigned int pixelcolor=;
switch(bitsperpixel)
{
case : pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbBlue;
pixelcolor<<=;
pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbGreen;
pixelcolor<<=;
pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbRed;
break;
case :
break;
case :pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbBlue;
pixelcolor<<=;
pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbGreen;
pixelcolor<<=;
pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbRed;
break;
case :
break;
default:
;
}
return pixelcolor; }
void InputPicture(BMPFILEPTR *bmpFile,unsigned char *pBmpData,FILE *fp)
{//将bmp文件输入,存到pBmpData指针所指存储空间中,初始化bmpFile变量
WriteData(pBmpData,fp);
bmpFile->bmpFileHeader=(BMP_FILEHEADER*)(pBmpData);
bmpFile->bmpInfoHeader=(BMP_INFOHEADER*)((char*)bmpFile->bmpFileHeader+sizeof(BMP_FILEHEADER));
bmpFile->bmpColorTable=(RGBPALETTE*)((char*)bmpFile->bmpInfoHeader+sizeof(BMP_INFOHEADER));
bmpFile->bmpDataPtr=pBmpData+bmpFile->bmpFileHeader->bmpOffset; }
void DisplayPicture(HDC hdc,BMPFILEPTR bmpFile,POINT spoint)
{//显示图片,spoint为图片起始点,左上角
static int bmpHeight,bmpWidth;
int h,w;
unsigned int index=;
static int bitsperpixel;
static int pixelcolor;
bmpHeight=bmpFile.bmpFileHeader->bmpHeight;
bmpWidth=bmpFile.bmpFileHeader->bmpWidth;
bitsperpixel=bmpFile.bmpInfoHeader->bitsPerPixel;
if(==bmpFile.bmpInfoHeader->bitsPerPixel)
bmpWidth=((bmpWidth*bitsperpixel+)>>)<<;//若宽度不是4的整数倍则对齐 Rectangle(hdc,spoint.x,spoint.y,bmpWidth,bmpHeight);//画图片外框架
for(h=spoint.y+bmpHeight;h>spoint.y;h--)//从图片左下角开始逐点填充
for(w=spoint.x;w<spoint.x+bmpWidth;w++)
{
pixelcolor=GetPixelColor(bmpFile,index++,bitsperpixel);
SetPixel(hdc,w,h,pixelcolor);
}
}
void ComputeOriginalProbabilty(BMPFILEPTR bmpFile,HE *hequal)
{//计算原始灰度级概率
int i;
int pixels=(bmpFile.bmpFileHeader->bmpHeight)*(bmpFile.bmpFileHeader->bmpWidth); for(i=;i<pixels;i++)
hequal->ohs[bmpFile.bmpDataPtr[i]]++;//累计相同灰度级点个数 FILE *p1;
p1=fopen("p1.txt","w");
int p[];
for(i=;i<;i++)
{
p[i]=hequal->ohs[i]; fprintf(p1,"p[%d]=%d\n ",i,p[i]);
} for(i=;i<;i++)
hequal->ohs[i]=hequal->ohs[i]/pixels;//计算每个灰度级概率
}
void ComputeEqualizedProbabilty(BMPFILEPTR bmpFile,HE *hequal)
{// 计算原始灰度级概率的累计概率,为生成均衡化之后概率
int i;
FILE *p2;
p2=fopen("p2.txt","w");
int a[]; hequal->integs[]=hequal->ohs[];
for(i=;i<;i++)
{
hequal->integs[i]=hequal->integs[i-]+hequal->ohs[i]; a[i]=(hequal->integs[i])*;
fprintf(p2,"a[%d]=%d\n ",i,a[i]);
} }
void GenerateEqualizedBmpFile(BMPFILEPTR bmpFile,FILE *fp1,HE hequal)
{//生成均衡化后的bmp文件
unsigned long i,j;
unsigned char c;
fwrite(bmpFile.bmpFileHeader,sizeof(BMP_FILEHEADER),,fp1);//将bmp文件头结构数据赋给新文件相应部分
fwrite(bmpFile.bmpInfoHeader,sizeof(BMP_INFOHEADER),,fp1);
fwrite(bmpFile.bmpColorTable,*sizeof(RGBPALETTE),,fp1);
for(i=;i<bmpFile.bmpFileHeader->bmpSize-bmpFile.bmpFileHeader->bmpOffset;i++)
{//将灰度级数据赋给新文件数据部分
for(j=;j<;j++)
{
if(*(bmpFile.bmpDataPtr+i)==j)
{
c=hequal.integs[j]*;
break;
} }
fwrite(&c,sizeof(unsigned char),,fp1);
}
}
BMPFILEPTR bmpFile,newbmpFile;
HE hequal={,},newhequal={,};//初始化直方图结构体变量 int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{ //windows API 主函数
MSG Message; static FILE *fp = NULL,*newfp=NULL;//定义两个文件指针,分别指向两个bmp文件
static unsigned char *pBmpData,*newpBmpData;//定义两个数据指针,分别指向两处内存首地址
static int filesize=;
if(NULL==(fp=fopen("Fig4.bmp","rb+")))
{
MessageBox(NULL,(LPCWSTR )"file open failure,error!",NULL,NULL);
exit();
}
if(NULL==(newfp=fopen("newFig.bmp","wb+")))
{
MessageBox(NULL,(LPCWSTR )"file open failure,error!",NULL,NULL);
exit();
}
filesize=FileLength(fp);
pBmpData=MemoryAlloc(filesize);
newpBmpData=MemoryAlloc(filesize);
InputPicture(&bmpFile,pBmpData,fp);
ComputeOriginalProbabilty(bmpFile,&hequal);
ComputeEqualizedProbabilty(bmpFile,&hequal);
GenerateEqualizedBmpFile(bmpFile,newfp,hequal);
InputPicture(&newbmpFile,newpBmpData,newfp);
ComputeOriginalProbabilty(newbmpFile,&newhequal);
fclose(fp);
fclose(newfp); if(!InitWindowsClass(hInstance))
return FALSE;
if(!InitWindows(hInstance,nCmdShow))
return FALSE;
while(GetMessage(&Message,NULL,,))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
} int InitWindows(HINSTANCE hInstance,int nCmdShow)
{
hWndMain=CreateWindow(
(LPCWSTR )"WinFill", // registered class name
(LPCWSTR )"直方图均衡化", // window name
WS_OVERLAPPEDWINDOW, // window style
, // horizontal position of window
, // vertical position of window
, // window width
, // window height
NULL, // handle to parent or owner window
NULL, // menu handle or child identifier
hInstance, // handle to application instance
NULL // window-creation data
);
if(!hWndMain)
return FALSE;
ShowWindow(hWndMain,nCmdShow);
UpdateWindow(hWndMain);
return TRUE;
} int InitWindowsClass(HINSTANCE hInstance)
{ //初始化窗口类,对窗口类的对象赋初始值
WNDCLASS wndclass;
wndclass.style=;
wndclass.cbClsExtra=;
wndclass.cbWndExtra=;
wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndclass.hCursor=LoadCursor(
hInstance,
IDC_ARROW
);
wndclass.hIcon=LoadIcon(
hInstance,
IDI_APPLICATION
);
wndclass.hInstance=hInstance;
wndclass.lpfnWndProc=WndProc;
wndclass.lpszClassName=(LPCWSTR )"WinFill";
wndclass.lpszMenuName=NULL;
return RegisterClass(&wndclass);
} long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam)
{
HDC hdc;
HPEN hpen;
PAINTSTRUCT ps;
POINT originpoint,pstartpoint; switch(iMessage)
{
case WM_PAINT: hdc=BeginPaint(hWnd,&ps);//绘图 hpen=CreatePen(PS_SOLID,,RGB(,,));
SelectObject(hdc,hpen);
////////////////画原始的bmp图//////////////////
pstartpoint.x=;
pstartpoint.y=;
DisplayPicture(hdc,bmpFile,pstartpoint); ///////////画均衡化后的bmp图/////////////////
pstartpoint.x=;
pstartpoint.y=;
DisplayPicture(hdc,newbmpFile,pstartpoint); return ;
case WM_DESTROY:PostQuitMessage();
return ;
default: return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}
}