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

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

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

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

直方图均衡化原理

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

                              

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

  (1) T(r)在区间中为单值且单调递增;

  (2) 当 时,T(r)的取值范围与r相同。

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

                           

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

                             

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

                     

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

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

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

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

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

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

直方图均衡化优缺点

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

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

直方图均衡化源码实现

 

  1 #include <windows.h> 
  2 #include <stdafx.h>
  3 #include <stdlib.h> 
  4 #include <string.h>
  5 #include <stdio.h>
  6 #include<conio.h>
  7 #pragma pack(1)
  8  
  9 typedef  unsigned char BYTE;
 10 typedef  short unsigned int WORD;
 11 typedef  unsigned long DWORD;
 12 
 13 
 14 
 15 
 16 typedef struct BMP_FILEHEADER
 17 {//定义bmp文件头
 18 
 19     WORD bmpType;/*位图标识*/
 20     DWORD bmpSize;/*说明文件的大小,用字节为单位*/
 21     DWORD bmpReserved;/*保留,必须设置为0*/
 22     DWORD bmpOffset;/*从文件头开始到实际的图象数据之间的字节的偏移量*/
 23     DWORD bmpHeaderSize;/*说明BITMAP_INFOHEADER结构所需要的字数*/
 24     DWORD bmpWidth;/*说明图象的宽度,以象素为单位*/
 25     DWORD bmpHeight;/*说明图象的高度,以象素为单位.大多数的BMP文件都是倒向的位图*/
 26     WORD bmpPlanes;/*为目标设备说明位面数,其值将总是被设为1*/
 27 
 28 } BMP_FILEHEADER;
 29 
 30 typedef struct BMP_INFOHEADER
 31 {//定义bmp信息头
 32 
 33     WORD bitsPerPixel;/*说明比特数/象素*/
 34     DWORD bmpCompression;/*说明图象数据压缩的类型*/
 35     DWORD bmpDataSize;   /*说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0*/
 36     DWORD bmpHResolution;/*说明水平分辨率,用象素/米表示*/
 37     DWORD bmpVResolution;/*说明垂直分辨率,用象素/米表示*/
 38     DWORD bmpColors;/*实际使用的颜色数,0说明使用所有调色板项*/
 39     DWORD bmpImportantColors;/*重要影响的颜色索引的数目,如果是0,表示都重要*/
 40 
 41 } BMP_INFOHEADER;
 42 
 43 typedef struct RGBPALETTE
 44 {//定义8位颜色表
 45 
 46     BYTE rgbBlue;/*指定蓝色强度*/
 47     BYTE rgbGreen;/*指定绿色强度*/
 48     BYTE rgbRed;/*指定红色强度*/
 49     BYTE rgbReserved;/*保留,设为0*/
 50 
 51 } RGBPALETTE;
 52 
 53 typedef struct RGBPALETTE_24
 54 {//定义24位颜色表
 55      BYTE rgbBlue;/*指定蓝色强度*/
 56      BYTE rgbGreen;/*指定绿色强度*/
 57      BYTE rgbRed;/*指定红色强度*/
 58 }RGBPALETTE_24;
 59 
 60 typedef struct BMPFILEPTR
 61 {//定义bmp文件指针类型
 62     BMP_FILEHEADER *bmpFileHeader;//bmp文件头指针
 63     BMP_INFOHEADER *bmpInfoHeader;//bmp信息头指针
 64     RGBPALETTE  *bmpColorTable;//颜色表指针
 65     unsigned char *bmpDataPtr;//bmp数据指针
 66 }BMPFILEPTR;
 67 
 68 typedef struct HE
 69 {//定义直方图均衡化结构
 70     float ohs[256];//原始灰度级概率
 71     float integs[256];//原始灰度概率累计值
 72 }HE;
 73 
 74 long FileLength(FILE *fp);//返回文件长度
 75 void WriteData(unsigned char *pBmpData,FILE *fp);//写数据,将文件数据写入pBmpData所指内存单元
 76 unsigned char* MemoryAlloc(int length);//分配length长度的内存单元
 77 void MemoryFree(unsigned char *pBmpData);//释放内存单元
 78 int GetPixelColor(BMPFILEPTR bmpFile,unsigned int index,int bitsperpixel);//获得颜色值
 79 void InputPicture(BMPFILEPTR *bmpFile,unsigned char *pBmpData,FILE *fp);//输入图片,文件指针初始化
 80 void DisplayPicture(HDC hdc,BMPFILEPTR bmpFile,POINT spoint);//显示图片,将图片显示在画板上
 81 void ComputeOriginalProbabilty(BMPFILEPTR bmpFile,HE *hequal);//计算原始图片灰度级概率
 82 void ComputeEqualizedProbabilty(BMPFILEPTR bmpFile,HE *hequal);//计算均衡化后的灰度级概率
 83 void GenerateEqualizedBmpFile(BMPFILEPTR bmpFile,FILE *fp1,HE hequal);//生成均衡化之后的bmp文件
 84 
 85 long WINAPI WndProc(HWND,UINT,UINT,LONG); //处理消息响应的函数
 86 BOOL InitWindowsClass(HINSTANCE); //初始化窗口类
 87 BOOL InitWindows(HINSTANCE,int); //初始化窗口函数
 88 HWND hWndMain;//窗口句柄
 89  
 90 long FileLength(FILE *fp)
 91 {//返回文件长度
 92     long curpos, length;
 93 
 94     curpos = ftell(fp);
 95     fseek(fp, 0L, SEEK_END);
 96     length = ftell(fp);
 97     fseek(fp, curpos, SEEK_SET);
 98     return length;
 99 }
100 
101 unsigned char* MemoryAlloc(int length)
102 {//按照length长度分配内存单元,返回内存首地址
103     unsigned char *pBmpData;
104 
105     if(NULL==(pBmpData=(unsigned char*)malloc(length*sizeof(unsigned char))))
106     {
107         MessageBox(NULL,(LPCWSTR )"memory allocation failure,error!",NULL,NULL);
108         exit(1);
109     }
110     return pBmpData;
111 }
112 
113 void MemoryFree(unsigned char *pBmpData)
114 {//释放内存单元
115     free(pBmpData);
116 }
117 
118 void WriteData(unsigned char *pBmpData,FILE *fp)
119 {//写数据,将文件数据写入字节流中
120     int ind=0;
121     fseek(fp, 0, SEEK_SET);
122     while(!feof(fp))
123     {
124         pBmpData[ind++]=fgetc(fp);
125     }
126 }
127 
128 int GetPixelColor(BMPFILEPTR bmpFile,unsigned int index,int bitsperpixel)
129 {//取像素点的颜色值,在VC++中颜色值是0x00bbggrr,返回颜色值
130      unsigned int pixelcolor=0;
131     switch(bitsperpixel)
132     {
133     case 8: pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbBlue;
134             pixelcolor<<=8;
135             pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbGreen;
136             pixelcolor<<=8;
137             pixelcolor+=(bmpFile.bmpColorTable+*(bmpFile.bmpDataPtr+index))->rgbRed;
138             break;
139     case 16:
140             break;
141     case 24:pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbBlue;
142             pixelcolor<<=8;
143             pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbGreen;
144             pixelcolor<<=8;
145             pixelcolor+=((RGBPALETTE_24*)bmpFile.bmpDataPtr+index)->rgbRed;
146             break;
147     case 32:
148             break;
149     default:
150             ;
151     }
152      return pixelcolor;
153 
154 }
155 void InputPicture(BMPFILEPTR *bmpFile,unsigned char *pBmpData,FILE *fp)
156 {//将bmp文件输入,存到pBmpData指针所指存储空间中,初始化bmpFile变量
157         WriteData(pBmpData,fp);
158         bmpFile->bmpFileHeader=(BMP_FILEHEADER*)(pBmpData);
159         bmpFile->bmpInfoHeader=(BMP_INFOHEADER*)((char*)bmpFile->bmpFileHeader+sizeof(BMP_FILEHEADER));
160         bmpFile->bmpColorTable=(RGBPALETTE*)((char*)bmpFile->bmpInfoHeader+sizeof(BMP_INFOHEADER));
161         bmpFile->bmpDataPtr=pBmpData+bmpFile->bmpFileHeader->bmpOffset;
162 
163 }
164 void DisplayPicture(HDC hdc,BMPFILEPTR bmpFile,POINT spoint)
165 {//显示图片,spoint为图片起始点,左上角
166      static int bmpHeight,bmpWidth;
167      int h,w;
168      unsigned int index=0;
169      static int bitsperpixel;
170      static int pixelcolor;
171      bmpHeight=bmpFile.bmpFileHeader->bmpHeight;
172      bmpWidth=bmpFile.bmpFileHeader->bmpWidth;
173      bitsperpixel=bmpFile.bmpInfoHeader->bitsPerPixel;
174      if(8==bmpFile.bmpInfoHeader->bitsPerPixel)
175          bmpWidth=((bmpWidth*bitsperpixel+31)>>5)<<2;//若宽度不是4的整数倍则对齐
176 
177      Rectangle(hdc,spoint.x,spoint.y,bmpWidth,bmpHeight);//画图片外框架 
178      for(h=spoint.y+bmpHeight;h>spoint.y;h--)//从图片左下角开始逐点填充
179         for(w=spoint.x;w<spoint.x+bmpWidth;w++)
180         {
181             pixelcolor=GetPixelColor(bmpFile,index++,bitsperpixel);
182             SetPixel(hdc,w,h,pixelcolor);
183         }
184 }
185 void ComputeOriginalProbabilty(BMPFILEPTR bmpFile,HE *hequal)
186 {//计算原始灰度级概率
187     int i;
188     int pixels=(bmpFile.bmpFileHeader->bmpHeight)*(bmpFile.bmpFileHeader->bmpWidth);
189 
190     for(i=0;i<pixels;i++)
191         hequal->ohs[bmpFile.bmpDataPtr[i]]++;//累计相同灰度级点个数
192 
193 
194     FILE *p1; 
195     p1=fopen("p1.txt","w");
196    int p[256];
197       for(i=0;i<256;i++)
198       {
199            p[i]=hequal->ohs[i];
200             
201               fprintf(p1,"p[%d]=%d\n ",i,p[i]);
202       }
203 
204 
205 
206     for(i=0;i<256;i++)
207         hequal->ohs[i]=hequal->ohs[i]/pixels;//计算每个灰度级概率
208 }
209 void ComputeEqualizedProbabilty(BMPFILEPTR bmpFile,HE *hequal)
210 {// 计算原始灰度级概率的累计概率,为生成均衡化之后概率
211     int i;
212         FILE *p2; 
213     p2=fopen("p2.txt","w");
214     int a[256];
215 
216     hequal->integs[0]=hequal->ohs[0];
217     for(i=1;i<256;i++)
218     {
219         hequal->integs[i]=hequal->integs[i-1]+hequal->ohs[i];
220 
221         a[i]=(hequal->integs[i])*256;
222         fprintf(p2,"a[%d]=%d\n ",i,a[i]);
223     }
224 
225 
226 }
227 void GenerateEqualizedBmpFile(BMPFILEPTR bmpFile,FILE *fp1,HE hequal)
228 {//生成均衡化后的bmp文件
229      unsigned long i,j;
230      unsigned char c;
231      fwrite(bmpFile.bmpFileHeader,sizeof(BMP_FILEHEADER),1,fp1);//将bmp文件头结构数据赋给新文件相应部分
232      fwrite(bmpFile.bmpInfoHeader,sizeof(BMP_INFOHEADER),1,fp1);
233      fwrite(bmpFile.bmpColorTable,256*sizeof(RGBPALETTE),1,fp1);
234      for(i=0;i<bmpFile.bmpFileHeader->bmpSize-bmpFile.bmpFileHeader->bmpOffset;i++)
235      {//将灰度级数据赋给新文件数据部分
236          for(j=0;j<256;j++)
237          {
238              if(*(bmpFile.bmpDataPtr+i)==j)
239              {
240                  c=hequal.integs[j]*255;
241                  break;
242              }
243               
244          }
245          fwrite(&c,sizeof(unsigned char),1,fp1);
246      }
247 }
248 BMPFILEPTR bmpFile,newbmpFile;
249 HE hequal={0,0},newhequal={0,0};//初始化直方图结构体变量
250 
251 
252 
253 
254 
255 int WINAPI WinMain( 
256                     HINSTANCE hInstance,      
257                     HINSTANCE hPrevInstance,  
258                     LPSTR lpCmdLine,        
259                     int nCmdShow   
260                    ) 
261 { //windows API 主函数
262     MSG Message; 
263 
264     static FILE *fp = NULL,*newfp=NULL;//定义两个文件指针,分别指向两个bmp文件
265     static unsigned char *pBmpData,*newpBmpData;//定义两个数据指针,分别指向两处内存首地址
266     static int filesize=0;
267     if(NULL==(fp=fopen("Fig4.bmp","rb+")))
268      {
269          MessageBox(NULL,(LPCWSTR )"file open failure,error!",NULL,NULL);
270          exit(1);
271      }
272     if(NULL==(newfp=fopen("newFig.bmp","wb+")))
273      {
274          MessageBox(NULL,(LPCWSTR )"file open failure,error!",NULL,NULL);
275          exit(1);
276      }    
277     filesize=FileLength(fp);
278     pBmpData=MemoryAlloc(filesize);
279     newpBmpData=MemoryAlloc(filesize);
280     InputPicture(&bmpFile,pBmpData,fp);
281     ComputeOriginalProbabilty(bmpFile,&hequal);
282     ComputeEqualizedProbabilty(bmpFile,&hequal);
283     GenerateEqualizedBmpFile(bmpFile,newfp,hequal);
284     InputPicture(&newbmpFile,newpBmpData,newfp);
285     ComputeOriginalProbabilty(newbmpFile,&newhequal);
286     fclose(fp);
287     fclose(newfp);
288 
289     if(!InitWindowsClass(hInstance)) 
290          return FALSE; 
291     if(!InitWindows(hInstance,nCmdShow)) 
292          return FALSE; 
293     while(GetMessage(&Message,NULL,0,0)) 
294     { 
295          TranslateMessage(&Message); 
296          DispatchMessage(&Message); 
297     }
298     return Message.wParam;
299 }
300 
301 
302 
303 int InitWindows(HINSTANCE hInstance,int nCmdShow) 
304 { 
305      hWndMain=CreateWindow( 
306                            (LPCWSTR )"WinFill",  // registered class name 
307                            (LPCWSTR )"直方图均衡化", // window name 
308                            WS_OVERLAPPEDWINDOW,        // window style 
309                            0,                // horizontal position of window 
310                            0,                // vertical position of window 
311                            1100,           // window width 
312                            550,          // window height 
313                            NULL,      // handle to parent or owner window 
314                            NULL,          // menu handle or child identifier 
315                            hInstance,  // handle to application instance 
316                            NULL        // window-creation data 
317                            ); 
318      if(!hWndMain) 
319          return FALSE; 
320      ShowWindow(hWndMain,nCmdShow); 
321      UpdateWindow(hWndMain); 
322      return TRUE; 
323 }
324  
325 int InitWindowsClass(HINSTANCE hInstance) 
326 { //初始化窗口类,对窗口类的对象赋初始值
327      WNDCLASS wndclass; 
328      wndclass.style=0; 
329      wndclass.cbClsExtra=0; 
330      wndclass.cbWndExtra=0; 
331      wndclass.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); 
332      wndclass.hCursor=LoadCursor( 
333                                  hInstance,  
334                                  IDC_ARROW  
335                                  ); 
336      wndclass.hIcon=LoadIcon( 
337                              hInstance, 
338                              IDI_APPLICATION   
339                              ); 
340      wndclass.hInstance=hInstance; 
341      wndclass.lpfnWndProc=WndProc; 
342      wndclass.lpszClassName=(LPCWSTR )"WinFill"; 
343      wndclass.lpszMenuName=NULL; 
344      return RegisterClass(&wndclass); 
345 } 
346 
347 long WINAPI WndProc(HWND hWnd,UINT iMessage,UINT wParam,LONG lParam) 
348 { 
349      HDC hdc;  
350      HPEN hpen; 
351      PAINTSTRUCT ps; 
352      POINT originpoint,pstartpoint;
353      
354      switch(iMessage) 
355      { 
356        case WM_PAINT: hdc=BeginPaint(hWnd,&ps);//绘图             
357 
358                       hpen=CreatePen(PS_SOLID,1,RGB(0,255,0)); 
359                       SelectObject(hdc,hpen);
360                     ////////////////画原始的bmp图//////////////////
361                       pstartpoint.x=0;
362                       pstartpoint.y=0;
363                       DisplayPicture(hdc,bmpFile,pstartpoint);
364                      
365 
366                     ///////////画均衡化后的bmp图/////////////////
367                       pstartpoint.x=550;
368                       pstartpoint.y=0;
369                       DisplayPicture(hdc,newbmpFile,pstartpoint);
370                     
371                       return 0; 
372       case WM_DESTROY:PostQuitMessage(0); 
373                       return 0; 
374       default:  return (DefWindowProc(hWnd,iMessage,wParam,lParam)); 
375      } 
376 } 

 

posted @ 2016-03-25 23:08  淇淇宝贝  阅读(9487)  评论(1编辑  收藏  举报