?? 圖像處理.txt
字號:
}
}
MaxGrayNum=0;
MinGrayNum=65535;
for(grayindex=0;grayindex<256;grayindex++){
temp=GrayTable[grayindex];
if(temp>MaxGrayNum)
MaxGrayNum=temp; //找到更大的了
if( (temp<MinGrayNum) && (temp>0) )
MinGrayNum=temp; //找 到更小的了
}
GlobalUnlock(hImgData);
//產生新的窗口顯示結果
hPopupWnd = CreateWindow ("PopupWindowClass",
"Histogram Statistic Window",WS_OVERLAPPEDWINDOW,50,80,550,350, hWnd,NULL,ghInst,NULL);
if (hPopupWnd){
ShowWindow (hPopupWnd, SW_SHOW);
UpdateWindow (hPopupWnd);
}
return TRUE;
}
下面是新窗口的消息處理函數
long FAR PASCAL PopupWndProc (HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
DWORD i;
int xstart;
static LOGPEN blp={PS_SOLID,1,1,RGB(0,0,255)}; //藍色畫筆
HPEN bhp; //畫筆句柄
float a,b,temp;
char str[10];
//計算上面所說的a,b的值
a=(float)(265.0 /( MaxGrayNum - MinGrayNum) );
b=(float) (270.0-a* MaxGrayNum);
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
bhp = CreatePenIndirect(&blp);
SelectObject(hdc,bhp);
MoveToEx(hdc,2,270,NULL);
LineTo(hdc,518,270); //先畫一條水平線
xstart=2;
for(i=0;i<256;i++){
MoveToEx(hdc,xstart,270,NULL);
if (GrayTable[i]!=0)
temp=(float)(a*GrayTable[i]+b);
else temp=0.0f; //如果灰度出現的次數是零,則不畫線
LineTo(hdc,xstart,270-(int)temp); //畫出該灰度的計數值
if (i%16 ==0){ //畫出標尺,每16個一格
MoveToEx(hdc,xstart,270,NULL);
LineTo(hdc,xstart,280);
_itoa(i,str,10);
TextOut(hdc,xstart,285,str,strlen(str));
}
xstart+=2;
}
MoveToEx(hdc,xstart,270,NULL);
LineTo(hdc,xstart,280);
TextOut(hdc,xstart,285,"256",strlen("256"));
EndPaint(hWnd,&ps);
DeleteObject(bhp);
break;
default:
break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
9.灰度直方圖均衡化(histogram equalization)
在介紹灰度直方圖均衡化之前,先講講直方圖修正。所謂直方圖修正,就是通過一個灰度映射函數Gnew=F(Gold),將原灰度直方圖改造成你所希望的直方圖。所以,直方圖修正的關鍵就是灰度映射函數。我們剛才介紹的閾值化,削波,灰度窗口變換等等,都是灰度映射函數。
直方圖均衡化是一種最常用的直方圖修正。它是把給定圖象的直方圖分布改造成均勻直方圖分布。由信息學的理論來解釋,具有最大熵(信息量)的圖象為均衡化圖象。直觀地講,直方圖均衡化導致圖象的對比度增加。
由于直方圖均衡化涉及到很多概率和數學的知識,具體的細節這里就不介紹了,只給出算法。通過下面的例題,就很容易明白了。
有一幅圖象,共有16級灰度,其直方圖分布為Pi, i=0,1,…15,求經直方圖均衡化后,量化級別為10級的灰度圖象的直方圖分布Qi ,其中Pi和Qi為分布的概率,即灰度i出現的次數與總的點數之比。
Pi:0.03,0, 0.06,0.10,0.20, 0.11,0,0,0,0.03,0,0.06,0.10,0.20,0.11,0
步驟1:用一個數組s記錄Pi,即s[0]=0.03;s[1]=0;s[2]=0.06…s[14]=0.11;s[15]=0
步驟2:i從1開始,令s[i]=s[i]+s[i-1] , 得到的結果是
s: 0.03,0.03,0.09,0.19,0.39,0.50,0.50,0.50,0.50,0.53,0.53,0.59,0.69,
0.89,1.0,1.0
步驟3:用一個數組L記錄新的調色板索引值,即令L[i]=s[i]*(10-1) ,得到的結果是L:0,0,1,2,4,5,5,5,5,5,5,5,6,8,9,9
這樣就找到了原來的調色板索引值和新的調色板索引值之間的對應關系,即0->0;1->0;2->1;3->2;4->4;5->5;6->5;7->5;8->5;9->5;10->5;11->5;12->6;13->8;14->9;15->9
步驟4:將老的索引值對應的概率合并,作為對應的新的索引值的概率。例如,原來的索引值0,1都對應了新的索引值0,則灰度索引值為0的概率為P0+P1=0.03;新的索引值3和7找不到老的索引值與之對應,所以令Q3和Q7為0。最后得到的結果是
Qi:0.03,0.06,0.10,0,0.20,0.20,0.10,0,0.20,0.11
圖17為Pi的分布,圖18為Qi的分布,對照一下,不難發現圖18的分布比圖17要均勻一些。
圖17. Pi的分布 圖18. Qi的分布
要注意的是,均衡化處理后的圖象只能是近似均勻分布。均衡化圖象的動態范圍擴大了,但其本質是擴大了量化間隔,而量化級別反而減少了,因此,原來灰度不同的像素經處理后可能變的相同,形成了一片的相同灰度的區域,各區域之間有明顯的邊界,從而出現了偽輪廓。
圖19為圖13經直方圖均衡化處理后,量化為128級灰度的結果,圖20為它的直方圖分布。為什么天亮了起來呢?分析一下就明白了:因為原圖中低灰度的點太多了,所以s數組前面的元素很大,經過L[i]=s[i]*(128-1)的處理后,原圖中低灰度的點的灰度值提高了不少,所以那片暗區變亮了。同時可以看出,天空中出現了偽輪廓。
圖19. 圖13經直方圖均衡化處理后的結果
圖20. 它的灰度直方圖
圖21為圖1直方圖均衡化后的結果(128級灰度),暗的區域(手)變亮了,看起來更清楚一些。
圖21. 圖1直方圖均衡化后的結果
下面給出直方圖均衡化的源程序
int EquaScale; //為新的灰度級別
BOOL HistogramEqua(HWND hWnd)
{
DLGPROC dlgInputBox = NULL;
DWORD BufSize,OffBits;
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
LPSTR lpTempPtr;
HDC hDc;
HFILE hf;
LONG x,y;
LOGPALETTE *pPal;
HPALETTE hPrevPalette;
HLOCAL hPal;
WORD i;
int Gray;
DWORD GrayHits[256];
int GrayIndex[256];
float s[256];
if( NumColors!=256){ //必須是256級灰度圖
MessageBox(hWnd,"Must be a 256 grayscale bitmap!","Error Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
//出現對話框,輸入新的灰度級別
dlgInputBox = (DLGPROC) MakeProcInstance ( (FARPROC)InputBox, ghInst );
DialogBox (ghInst, "INPUTBOX", hWnd, dlgInputBox);
FreeProcInstance ( (FARPROC) dlgInputBox );
if( EquaScale >=255){ //量化級別不能大于255
MessageBox(hWnd,"The new scale can not be larger than 255","Error Message",
MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
//OffBits為到實際位圖數據的偏移值
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//BufSize為緩沖區的大小
BufSize=bf.bfSize-sizeof(BITMAPFILEHEADER);
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{
MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK|
MB_ICONEXCLAMATION);
return FALSE;
}
//lpImgData指向原圖,//lpTempImgData指向新開的緩沖區
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
//拷貝頭信息
memcpy(lpTempImgData,lpImgData,OffBits);
//ColorHits為記錄顏色使用頻率的數組,ColorIndex為記錄顏色索引值的數組
//先清零
memset(GrayHits,0,256*sizeof(DWORD));
memset(GrayIndex,0,256*sizeof(WORD));
for(y=0;y<bi.biHeight;y++){
lpPtr=(unsigned char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
Gray=(unsigned char )*(lpPtr++);
GrayHits[Gray]++; //統計該顏色用到的次數
}
}
for(i=0;i<256;i++)
//次數除以總點數得到頻率
s[i]=(float)GrayHits[i]/((float)bi.biWidth*(float)bi.biHeight);
for(i=1;i<256;i++)
s[i]+=s[i-1]; //每一項都是前面所有項的累加
for(i=0;i<256;i++)
GrayIndex[i]=(int)(s[i]*(EquaScale-1)); //根據新的量化級別,計算新的灰//度索引值
//為新的調色板分配內存
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE) + 256* sizeof(PALETTEENTRY));
pPal =(LOGPALETTE *)LocalLock(hPal);
//先將調色板內存全部清零
memset(pPal,0,sizeof(LOGPALETTE) + 256* sizeof(PALETTEENTRY));
pPal->palNumEntries =(WORD) 256;
pPal->palVersion = 0x300;
lpTempPtr=(char *)lpTempImgData+sizeof(BITMAPINFOHEADER);
for (i = 0; i < EquaScale; i++) {
Gray=(int)(i*255.0/(EquaScale-1)); //根據新的量化級別,計算新的灰度值
pPal->palPalEntry[i].peRed=(BYTE)Gray;
pPal->palPalEntry[i].peGreen=(BYTE)Gray;
pPal->palPalEntry[i].peBlue=(BYTE)Gray;
pPal->palPalEntry[i].peFlags=(BYTE)0;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=(unsigned char)Gray;
*(lpTempPtr++)=0;
}
if(hPalette!=NULL)
DeleteObject(hPalette);
//產生新的邏輯調色板
hPalette=CreatePalette(pPal);
LocalUnlock(hPal);
LocalFree(hPal);
hDc=GetDC(hWnd);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
for(y=0;y<bi.biHeight;y++){
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
Gray=(unsigned char )*(lpPtr++); //原灰度索引值
Gray=GrayIndex[Gray]; //對應的新的灰度索引值
*(lpTempPtr++)=(unsigned char)Gray;
}
}
if(hBitmap!=NULL)
DeleteObject(hBitmap);
//產生新的位圖
hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpTempImgData, (LONG)CBM_INIT,(LPSTR)lpTempImgData+sizeof(BITMAPINFOHEADER)
+256*sizeof(RGBQUAD),(LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS);
if(hPalette && hPrevPalette){
SelectPalette(hDc,hPrevPalette,FALSE);
RealizePalette(hDc);
}
hf=_lcreat("c:\\equa.bmp",0);
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
_lclose(hf);
//釋放內存和資源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
return TRUE;
}
注意事項:
source5目錄下為本章介紹的源程序,功能是直方圖修正和彩色變換.
運行時,文件c:\test.bmp必須存在
命令行編譯過程如下
vcvars32
rc bmp.rc
cl colorope.c bmp.res user32.lib gdi32.lib
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -