📄 图像处理.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 + -