第5章直方图修正和彩色变换 这一章,我们主要和调色板打交道。先从最简单的反色讲起。 51反色 反色( (invert)就是形成底片效果。例如,图52为图51反色后的结果。 图51原图 图52图5.1反色后的结果 反色有时是很有用的,比如,图5.1中黑色区域占绝大多数,这样打印起来很费墨,我们可 以先进行反色处理后再打印 反色的实际含义是将R、G、B值反转。若颜色的量化级别是256,则新图的R、G、B值为 255减去原图的R、G、B值。这里针对的是所有图,包括真彩图、带调色板的彩色图(又称 为伪彩色图)、和灰度图。针对不同种类有不同的处理 先看看真彩图。我们知道真彩图不带调色板,每个象素用3个字节,表示R、G、B三个分 量。所以处理很简单,把反转后的R、G、B值写入新图即可。 再来看看带调色板的彩色图,我们知道位图中的数据只是对应调色板中的一个索引值,我们 只需要将调色板中的颜色反转,形成新调色板,而位图数据不用动,就能够实现反转。 灰度图是一种特殊的伪彩色图,只不过调色板中的R、G、B值都是一样的而已。所以反 转的处理和上面讲的一样。 这里,我想澄清一个概念。过去我们讲二值图时,一直都说成黑白图。二值位图一定是黑白 的吗?答案是不一定。我们安装 Windows95时看到的那幅 setup.bmp是由蓝色和黑色组成的, 但它实际上是二值图。原来,它的调色板中的两种颜色是黑与蓝,而不是黑与白。所以说二 值图也可以是彩色的,只不过一般情况下是黑白图而已 下面的程序实现了反色,注意其中真彩图和调色板位图处理时的差别。 BOOL Invert(HWND hwnd) DWORD OffBits Bufsize
第 5 章 直方图修正和彩色变换 这一章,我们主要和调色板打交道。先从最简单的反色讲起。 5.1 反色 反色(invert)就是形成底片效果。例如,图 5.2 为图 5.1 反色后的结果。 图 5.1 原图 图 5.2 图 5.1 反色后的结果 反色有时是很有用的,比如,图 5.1 中黑色区域占绝大多数,这样打印起来很费墨,我们可 以先进行反色处理后再打印。 反色的实际含义是将 R、G、B 值反转。若颜色的量化级别是 256,则新图的 R、G、B 值为 255 减去原图的 R、G、B 值。这里针对的是所有图,包括真彩图、带调色板的彩色图(又称 为伪彩色图)、和灰度图。针对不同种类有不同的处理。 先看看真彩图。我们知道真彩图不带调色板,每个象素用 3 个字节,表示 R、G、B 三个分 量。所以处理很简单,把反转后的 R、G、B 值写入新图即可。 再来看看带调色板的彩色图,我们知道位图中的数据只是对应调色板中的一个索引值,我们 只需要将调色板中的颜色反转,形成新调色板,而位图数据不用动,就能够实现反转。 灰度图是一种特殊的伪彩色图,只不过调色板中的 R、G、B 值 都是一样的而已。所以反 转的处理和上面讲的一样。 这里,我想澄清一个概念。过去我们讲二值图时,一直都说成黑白图。二值位图一定是黑白 的吗?答案是不一定。我们安装Windows95时看到的那幅setup.bmp是由蓝色和黑色组成的, 但它实际上是二值图。原来,它的调色板中的两种颜色是黑与蓝,而不是黑与白。所以说二 值图也可以是彩色的,只不过一般情况下是黑白图而已。 下面的程序实现了反色,注意其中真彩图和调色板位图处理时的差别。 BOOL Invert(HWND hWnd) { DWORD OffBits,BufSize;
LPBITMAPINFOHEADER IplmgData LPSTR HLOCAL hTemplmg Data; LPBITMAPINFOHEADER Ip TemplmgData; LPSTR IpTempPtr hDc E LONG LOGPALETTE HPALETTE hPrev Palette=NULL; HLOCAL hPal unsigned char Red, Green Blue OffBits-bf. bfOffBits-sizeof( BITMAPFILEHEADER) BufSize= OrbIts+bi. biHeightLine Bytes,∥新开缓冲区的大小 f((hTemplmg Data=LocalAlloc(LHND, BufSize))==NULL) Message Box(hWnd, "Error alloc memory! " "Error Message", MB OK MB ICONEXCLAMATION return False lplmg Data=(LPBITMAPINFOHEADER)GlobalLock(hlmgData) lpTemplmg Data=(LPBITMAPINFOHEADER)LocalLock(hTemplmg Data ∥拷贝头信息 memcpy(pTemplmg Data, Iplmg Data, BufSize); hDc=GetDC(hWnd) if( Num Colors!!=0){/ Num Colors不为0说明是带调色板的 lpPtr(char *)lplmg Data+sizeof( BITMAPINFOHEADER) ∥指向原图数据 IpTempPtr=(char *)lpTemplmg Data+sizeof( BITMAPINFOHEADER) ∥指向新图数据 ∥.新调色板分配内存 hPal=Local Alloc(LHND, sizeof(LOGPALETTE)+ NumColors*sizeof( PALETTEENTRY)) pPal =(LOGPALETTE ")LocalLock(hPal); pPal-pal NumEntries =(WORD) Num Colors; pPal->pal Version =0x300 for(i=0; 1< Num Colors; i++)i
LPBITMAPINFOHEADER lpImgData; LPSTR lpPtr; HLOCAL hTempImgData; LPBITMAPINFOHEADER lpTempImgData; LPSTR lpTempPtr; HDC hDc; HFILE hf; LONG x,y; LOGPALETTE *pPal; HPALETTE hPrevPalette=NULL; HLOCAL hPal; DWORD i; unsigned char Red,Green,Blue; OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); BufSize=OffBits+bi.biHeight*LineBytes; //新开缓冲区的大小 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) { MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK| MB_ICONEXCLAMATION); return FALSE; } lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); //拷贝头信息 memcpy(lpTempImgData,lpImgData,BufSize); hDc=GetDC(hWnd); if(NumColors!=0){ //NumColors 不为 0 说明是带调色板的 lpPtr=(char *)lpImgData+sizeof(BITMAPINFOHEADER); //指向原图数据 lpTempPtr=(char *)lpTempImgData+sizeof(BITMAPINFOHEADER); //指向新图数据 //为新调色板分配内存 hPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+ NumColors*sizeof(PALETTEENTRY)); pPal =(LOGPALETTE *)LocalLock(hPal); pPal->palNumEntries =(WORD) NumColors; pPal->palVersion = 0x300; for (i = 0; i < NumColors; i++) {
Blue=(unsigned char )("lpPtr++ Green=(unsigned char )('lpPtr++) Red=(unsigned char )(*lpPtr++); IpPr+ ∥反转调色板中的颜色,存入新的调色板 oPal->palPal Entry[i] peRed=(BYTE)(255-Red); pPal->palPalEntry[i]-pe Green=(BYTE)(255-Green) Pal->palPal Entry i]- peBlue=(BYTE)(255-Blue); pPal->palPal Entry[i]-peFlags=0 *(pTempPtr++ =(unsigned char)(255-Green) *(IpTempPtr++ (unsigned char)(255-Red) if(pAlette!=NULL) palette= CreatePalettel(pPal),∥/产生新的调色板 LocalUnlock(hPal) ocal Free(hPal) if(pAlette)' hPrevPalette=SelectPalette(hDc, hPalette, FALSE) RealizePalette(hDc else{∥不带调色板,说明是真彩色图 for(y=0; y<bi. biHeight; y++)1 lpPtr=(char *)lplmg Data+( BufSize-LineBytes-y*Line Bytes); lpTempPtr=(char *)lp Templmg Data+( BufSize-LineBytes-y* LineBytes) for(x=0 X<bi bi Width; x++) Blue=(unsigned char (lpPtr++) Green=(unsigned char )("lpPtr++); Red=(unsigned char )(*lpPtr++) ∥反转位图数据中的颜色,存入新的位图数据中 *(pTempPtr++ =(unsigned char )(255-Blue) *(Ip TempPtr++ =(unsigned char)(255-Green *(IpTempPtr++ =(unsigned char )(255-Red)
Blue=(unsigned char )(*lpPtr++); Green=(unsigned char )(*lpPtr++); Red=(unsigned char )(*lpPtr++); lpPtr++; //反转调色板中的颜色,存入新的调色板 pPal->palPalEntry[i].peRed=(BYTE)(255-Red); pPal->palPalEntry[i].peGreen=(BYTE)(255-Green); pPal->palPalEntry[i].peBlue=(BYTE)(255-Blue); pPal->palPalEntry[i].peFlags=0; *(lpTempPtr++)=(unsigned char)(255-Blue); *(lpTempPtr++)=(unsigned char)(255-Green); *(lpTempPtr++)=(unsigned char)(255-Red); *(lpTempPtr++)=0; } if(hPalette!=NULL) DeleteObject(hPalette); hPalette=CreatePalette(pPal); //产生新的调色板 LocalUnlock(hPal); LocalFree(hPal); if(hPalette){ hPrevPalette=SelectPalette(hDc,hPalette,FALSE); RealizePalette(hDc); } } else{ //不带调色板,说明是真彩色图 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++){ Blue=(unsigned char )(*lpPtr++); Green=(unsigned char )(*lpPtr++); Red=(unsigned char )(*lpPtr++); //反转位图数据中的颜色,存入新的位图数据中 *(lpTempPtr++)=(unsigned char)(255-Blue); *(lpTempPtr++)=(unsigned char)(255-Green); *(lpTempPtr++)=(unsigned char)(255-Red); } }
if(hBitmap =NUL DeleteObject(hBitmap); hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lpTemplmg Data (LONG CBM INIT (LPSTR)lp Templmg Data+ sizeof( BITMAPINFOHEADER NumColors*sizeof( RGBQUAD). ( LPBITMAPINFO)Ip Templmg Data DIB RGB COLORS) if(pAlette & hPrev Palette)( SelectPalette(hDc, hPrevPalette, FALSE) RealizePalette(hDc) hf- Creat("c: invert. bmp,0) I write(hf, (LPSTR)&bf, sizeof( BITMAPFILEHEADER)) I write(hf, (LPSTR)IpTemplmg Data, BufSize) Iclose(hf) 释放内存和资源 Released(hWnd, hDc) Local Unlock(hTemplmg Data) al Free(hTemplmg Data Global Unlock(hmg Data) return tRUe. 52彩色图转灰度图 第2章中提到了YUV的颜色表示方法,在这种表示方法中,Y分量的物理含义就是亮度 它含了灰度图( grayscale的所有信息,只用Y分量就完全能够表示出一幅灰度图来。YUV 和RGB之间有着如下的对应关系: 0.299-0.1480.615 Iy u vI-IR G Blo.587-0289.5151 0.1140437-0.100 我们利用上式,根据R、G、B的值求出Y值后,将R、G、B值都赋值成Y,就能表示出 灰度图来,这就是彩色图转灰度图的原理
} if(hBitmap!=NULL) DeleteObject(hBitmap); hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, (LONG)CBM_INIT, (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ NumColors*sizeof(RGBQUAD), (LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS); if(hPalette && hPrevPalette){ SelectPalette(hDc,hPrevPalette,FALSE); RealizePalette(hDc); } hf=_lcreat("c:\\invert.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; } 5.2 彩色图转灰度图 第 2 章中提到了 YUV 的颜色表示方法,在这种表示方法中,Y 分量的物理含义就是亮度, 它含了灰度图(grayscale)的所有信息,只用 Y 分量就完全能够表示出一幅灰度图来。YUV 和 RGB 之间有着如下的对应关系: 我们利用上式,根据 R、G、B 的值求出 Y 值后,将 R、G、B 值都赋值成 Y,就能表示出 灰度图来,这就是彩色图转灰度图的原理
先看看真彩图。我们知道真彩图不带调色板,每个象素用3个字节,表示R、G、B三个分 量。所以处理很简单,根据R、G、B的值求出Y值后,将R、G、B值都赋值成Y,写入 新图即可。 再来看看带调色板的彩色图,我们知道位图中的数据只是对应调色板中的一个索引值,我们 只需要将调色板中的彩色变成灰度,形成新调色板,而位图数据不用动,就可以了 下面的程序实现了彩色图到灰度图的转换,注意其中真彩图和调色板位图处理时的差别 BOOL Colorto Gray Scale(HWND hWnd) SrcOffBits, SrcBufSize, Dst BufSize, DstLine Byte LPBITMAPINFOHEADER Iplmg Data LPSTR IpPt HLOCAL hTemplmg Data LPBITMAPINFOHEADER lp TemplmgData; LPSTR HDC hDc HFILE LONG BITMAPFILEHEADER Dst: BITMAPINFOHEADER DstB LOGPALETTE HPALETTE hPrey Palette HLOCAL DWORD NewNum Colors WORD NewBitCount. DWORD Red, Green, Blue, Gray NewNum Colors= Num Colors;/ NewNum Colors为新图的颜色数 New count== bi bi Bitcount;/ NewBitCount为新图的颜色位数 if( NumColors==0)∥真彩图 NewNum Colors=256. Newbie 由于颜色位数有可能发生了改变,所以要重新计算每行占用的字节数以及 ∥新图的缓冲区大小 DstLine Bytes=(DWORD)WIDTHBYTES(bibi Width*New Count) DstBufSsize=(DWORD)(Sizeof(BITMAPINFOHEADER)+NewNum Colors*
先看看真彩图。我们知道真彩图不带调色板,每个象素用 3 个字节,表示 R、G、B 三个分 量。所以处理很简单,根据 R、G、B 的值求出 Y 值后,将 R、G、B 值都赋值成 Y,写入 新图即可。 再来看看带调色板的彩色图,我们知道位图中的数据只是对应调色板中的一个索引值,我们 只需要将调色板中的彩色变成灰度,形成新调色板,而位图数据不用动,就可以了。 下面的程序实现了彩色图到灰度图的转换,注意其中真彩图和调色板位图处理时的差别。 BOOL ColortoGrayScale(HWND hWnd) { DWORD SrcOffBits,SrcBufSize,DstBufSize,DstLineBytes; LPBITMAPINFOHEADER lpImgData; LPSTR lpPtr; HLOCAL hTempImgData; LPBITMAPINFOHEADER lpTempImgData; LPSTR lpTempPtr; HDC hDc; HFILE hf; LONG x,y; BITMAPFILEHEADER DstBf; BITMAPINFOHEADER DstBi; LOGPALETTE *pPal; HPALETTE hPrevPalette; HLOCAL hPal; DWORD NewNumColors; WORD NewBitCount; float Y; DWORD i; unsigned char Red,Green,Blue,Gray; NewNumColors=NumColors; //NewNumColors 为新图的颜色数 NewBitCount=bi.biBitCount; //NewBitCount 为新图的颜色位数 if(NumColors==0) //真彩图 { NewNumColors=256; NewBitCount=8; } //由于颜色位数有可能发生了改变,所以要重新计算每行占用的字节数以及 //新图的缓冲区大小 DstLineBytes=(DWORD)WIDTHBYTES(bi.biWidth*NewBitCount); DstBufSize=(DWORD)(sizeof(BITMAPINFOHEADER)+NewNumColors*