第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*
sizeof(RGBQUAD)+(DWORD)DstLineBytes"bi. biHeight) SteF和 DstBi为新的 BITMAPFILEHEADER和 BITMAPINFOHEADER ∥拷贝原来的头信息 memcpy((char *)&DstBf, (char *)&bf, sizeof( BI TMAPFILEHEADER): memcpy((char *)&Dst Bi, (char *)&bi, sizeof( BITMAPINFOHEADER)) ∥(必要的改变 DstBf. bfSize=Dst BufSize+sizeof(BITMAPFILEHEADER) DstBf. bfoffBits=(DWORD)(NewNum Colors*sizeof(RGBQUAD)+ sizeof( BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER) DstBi biclrUsed=o Dstbi bibitcountNewBitcount ∥原图的缓冲区的大小 SrcOffBits-bf. bfoffBits-sizeof(BITMAPFILEHEADER) SrcBufSize=SrcOffBits+bi bi Height *Line Bytes, if((hTemplmg Data=LocalAlloc(LHND, Dst BufSize))==NULL) Message Box(hWnd, Error alloc memory! " Error Message", MB OK MB ICONEXCLAMATION return False lplmg Data=(LPBITMAPINFOHEADER)GlobalLock(hlmg Data) IpTemplmg Data=(LPBITMAPINFOHEADER)LocalLock(hTemplmg Data); ∥拷贝头信息和位图数据 memcpy(lp Templmg Data, lplmg Data, DstBufSize); ∥)新的 BITMAPINFOHEADER替换原来的头信息 emcpy(lp Templmg Data, (char *)&DstBi, sizeof( BITMAPINFOHEADER)) IpPt指向原图的数据 lpPtr=(char *)lplmg Data+sizeof( BITMAPINFOHEADER) IpTempPtr指向新图的数据 lpTempPtr=(char *)lp Templmg Datatsizeof( BITMAPINFOHEADER) ∥新的调色板分配内存 hPal=Local Alloc( LHND, sizeof(LOGPALETTE)+ NewNum Colors s sizeof(PALETTEENTRY)) pPal=(LOGPALETTE *)LocalLock(hPal) pPal->pal NumEntries=( WORD) NewNumColors pPal->pal Version =0X300 f( Num Colors==0)∥真彩色
sizeof(RGBQUAD)+(DWORD)DstLineBytes*bi.biHeight); //DstBf 和 DstBi 为新的 BITMAPFILEHEADER 和 BITMAPINFOHEADER //拷贝原来的头信息 memcpy((char *)&DstBf,(char *)&bf,sizeof(BITMAPFILEHEADER)); memcpy((char *)&DstBi,(char *)&bi,sizeof(BITMAPINFOHEADER)); //做必要的改变 DstBf.bfSize=DstBufSize+sizeof(BITMAPFILEHEADER); DstBf.bfOffBits=(DWORD)(NewNumColors*sizeof(RGBQUAD)+ sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER)); DstBi.biClrUsed=0; DstBi.biBitCount=NewBitCount; //原图的缓冲区的大小 SrcOffBits=bf.bfOffBits- sizeof(BITMAPFILEHEADER); SrcBufSize=SrcOffBits+bi.biHeight*LineBytes; if((hTempImgData=LocalAlloc(LHND,DstBufSize))==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,DstBufSize); //用新的 BITMAPINFOHEADER 替换原来的头信息 memcpy(lpTempImgData,(char *)&DstBi,sizeof(BITMAPINFOHEADER)); //lpPtr 指向原图的数据 lpPtr=(char *)lpImgData+sizeof(BITMAPINFOHEADER); //lpTempPtr 指向新图的数据 lpTempPtr=(char *)lpTempImgData+sizeof(BITMAPINFOHEADER); //为新的调色板分配内存 hPal=LocalAlloc(LHND,sizeof(LOGPALETTE) + NewNumColors * sizeof(PALETTEENTRY)); pPal =(LOGPALETTE *)LocalLock(hPal); pPal->palNumEntries =(WORD) NewNumColors; pPal->palVersion = 0x300; if(NumColors==0) //真彩色
for(i=0,ipalPal Entry[i]- peRed=(BYTE)i pPal->palPal Entry[i]. peGreen=(BYTE)i pPal->palPal[i]-peBlue=(BYTE)i; )0, *(pTempPtr++) =(unsigned char ) (Ip *(IpTempPtr++) =(unsigned char )1; *(Ip else for(i=0,ipalPalEntryli]- peRed=Gray oPal->palPalEntryli].-pe Green=Gray; Pal->palPalEntry[i]-peFlags=0 *(IpTempPtr+)=(unsigned char )Gray (pTempPtr++ =(unsigned char)Gray (IpTempPtr++ =(unsigned char )Gray; "(pTempPtr++)=( if(hPalet DeleteObject(hPalette) ∥生成新的逻辑调色板 pAlette Local Unlock (hPal) Dc=GetDC (hWnd) if(pAlette)i hPrev Palette=SelectPalette(h alette, FALSE) RealizePalette(hDc)
for (i = 0; i palPalEntry[i].peRed=(BYTE)i; pPal->palPalEntry[i].peGreen=(BYTE)i; pPal->palPalEntry[i].peBlue=(BYTE)i; pPal->palPalEntry[i].peFlags=(BYTE)0; *(lpTempPtr++)=(unsigned char)i; *(lpTempPtr++)=(unsigned char)i; *(lpTempPtr++)=(unsigned char)i; *(lpTempPtr++)=0; } else for (i = 0; i palPalEntry[i].peRed=Gray; pPal->palPalEntry[i].peGreen=Gray; pPal->palPalEntry[i].peBlue=Gray; pPal->palPalEntry[i].peFlags=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);
if(NumColors=0)∥真彩色图才需要处理位图数据 for(y=0; y<bi. biHeight; y++)4 lpPtr=(char *)lplmg Data+(SrcBufSize-LineBytes-y*Line Bytes); ( DstBufSize-DstLine Bytes-y* DstLine Bytes) for(x=0 X<bi bi Width, x++) Blue=(unsigned char )(lpPtr++) Green=(unsigned char )(*lpPtr++); nar )("lpPtr++) Y=( float)(Red*0.299Gren*0.587+Bue*0.114) ∥从位图数据计算得到Y值,写入新图中 Gray=(BYTE)Y lpTempPtr++)=(unsigned char )Gray; DeleteObject(h Bitmap) 广产生新的位图 hBitmap=CreateDIBitmap(hDc, (LPBITMAPINFOHEADER)lp Templmg Data (LONG)CBM INIT. (LPSTR)lp Templmg Data+ sizeof( BITMAPINFOHEADER)+ NewNum Colors*sizeof(RGBQUAD) (LPBITMAPINFO)IpTemplmgD DIB RGB COLORS) if(pAlette & hPrevPalette)4 SelectPalette(hDc, hPrevPalette, FALSE) RealizePalette(hDc) hf- Creat("c: gray. bmp",0) Iwrite(hf, (LPSTR)&DstBf, sizeof( BITMAPFILEHEADER)) Iwrite(hf, (LPSTR)IpTemplmg Data, Dst BufSize); Iclose(hf) ∥释放内存和资源 Released(hWnd, hDc) LocalUnlock(hTemplmg Data) Local Free(hTemplmg Data);
} if(NumColors==0) //真彩色图才需要处理位图数据 for(y=0;y<bi.biHeight;y++){ lpPtr=(char *)lpImgData+(SrcBufSize-LineBytes-y*LineBytes); lpTempPtr=(char*)lpTempImgData+ (DstBufSize-DstLineBytes-y*DstLineBytes); for(x=0;x<bi.biWidth;x++){ Blue=(unsigned char )(*lpPtr++); Green=(unsigned char )(*lpPtr++); Red=(unsigned char )(*lpPtr++); Y=(float)(Red*0.299+Green*0.587+Blue*0.114); //从位图数据计算得到 Y 值,写入新图中 Gray=(BYTE)Y; *(lpTempPtr++)=(unsigned char)Gray; } } if(hBitmap!=NULL) DeleteObject(hBitmap); //产生新的位图 hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, (LONG)CBM_INIT, (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ NewNumColors*sizeof(RGBQUAD), (LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS); if(hPalette && hPrevPalette){ SelectPalette(hDc,hPrevPalette,FALSE); RealizePalette(hDc); } hf=_lcreat("c:\\gray.bmp",0); _lwrite(hf,(LPSTR)&DstBf,sizeof(BITMAPFILEHEADER)); _lwrite(hf,(LPSTR)lpTempImgData,DstBufSize); _lclose(hf); //释放内存和资源 ReleaseDC(hWnd,hDc); LocalUnlock(hTempImgData); LocalFree(hTempImgData);
Global Unlock(hmg Data) return tRUe 53真彩图转256色图 我们知道,真彩图中包含最多达24种颜色,怎样从中选出256种颜色,又要使颜色的失真 比较小,这是一个比较复杂的问题。一种简单的做法是将R:G:B以3:3:2表示,即取 R,G的高3位,B的高两位,组成一个字节,这样就可以表示256种颜色了,但不难想象, 这种方法的失真肯定很严重 我们下面介绍的算法能够比较好地实现真彩图到256色图的转换。它的思想是:准备一个长 度为4096的数组,代表40%6种颜色。对图中的每一个象素,取R、G、B的最高四位,拼 成一个12位的整数,对应的数组元素加1。全部统计完后,就得到了这4096种颜色的使用 频率。其中,可能有一些颜色一次也没用到,即对应的数组元素为零(假设不为零的数组元 素共有 PalCounts个)。将这些为零的数组元素清除出去,使得前 PalCounts个元素都不为零。 将这 PalCounts个数按从大到小的顺序排列(这里我们使用起泡排序)。这样,前256种颜色 就是用的最多的颜色,它们将作为调色板上的256种颜色。对于剩下的 PalCounts256种颜 色并不是简单地丢弃,而是用前256种颜色中的一种来代替,代替的原则是找有最小平方误 差的那个。再次对图中的每一个象素,取R、G、B的最高四位,拼成一个12位的整数 如果对应值在前256种颜色中,则直接将该索引值填入位图数据中,如果是在后 AcoUnts-256种颜色中,则用代替色的索引值填入位图数据中 下面的两幅图中,图5.3是原真彩图,图54是用上面的算法转换成的256色图,可以看出, 效果还不错。 图53原真彩图 图54转换后的256色图 下面是上述算法的源程序。 BOOL Trueto256(HWND hwnd) DWORD SrcBufSize, OffBits, Dst BufSize, DstLineBy LPBITMAPINFOHEADER IplmgData
GlobalUnlock(hImgData); return TRUE; } 5.3 真彩图转 256 色图 我们知道,真彩图中包含最多达 2 24 种颜色,怎样从中选出 256 种颜色,又要使颜色的失真 比较小,这是一个比较复杂的问题。一种简单的做法是将 R:G:B 以 3:3:2 表示,即取 R,G 的高 3 位,B 的高两位,组成一个字节,这样就可以表示 256 种颜色了,但不难想象, 这种方法的失真肯定很严重。 我们下面介绍的算法能够比较好地实现真彩图到 256 色图的转换。它的思想是:准备一个长 度为 4096 的数组,代表 4096 种颜色。对图中的每一个象素,取 R、G、B 的最高四位,拼 成一个 12 位的整数,对应的数组元素加 1。全部统计完后,就得到了这 4096 种颜色的使用 频率。其中,可能有一些颜色一次也没用到,即对应的数组元素为零(假设不为零的数组元 素共有 PalCounts 个)。将这些为零的数组元素清除出去,使得前 PalCounts 个元素都不为零。 将这 PalCounts 个数按从大到小的顺序排列(这里我们使用起泡排序)。这样,前 256 种颜色 就是用的最多的颜色,它们将作为调色板上的 256 种颜色。对于剩下的 PalCounts-256 种颜 色并不是简单地丢弃,而是用前 256 种颜色中的一种来代替,代替的原则是找有最小平方误 差的那个。再次对图中的每一个象素,取 R、G、B 的最高四位,拼成一个 12 位的整数, 如果对应值在前 256 种颜色中,则直接将该索引值填入位图数据中,如果是在后 PalCounts-256 种颜色中,则用代替色的索引值填入位图数据中。 下面的两幅图中,图 5.3 是原真彩图,图.54 是用上面的算法转换成的 256 色图,可以看出, 效果还不错。 图 5.3 原真彩图 图 5.4 转换后的 256 色图 下面是上述算法的源程序。 BOOL Trueto256(HWND hWnd) { DWORD SrcBufSize,OffBits,DstBufSize,DstLineBytes; LPBITMAPINFOHEADER lpImgData;
LPSTR IpPr HLOCAL tEmpl LPBITMAPINFOHEADER lp TemplmgData; LPSTR lpTempPtr HFILE LONG x,y, BITMAPFILEHEADER DstBf BITMAPINFOHEADER Dst Bi LOGPALETTE HPALETTE hPrev Palette HLOCAL WORD Red green Blue. Clrlnde DWORD Color Hits( 4096 ColorIndex[4096 Pal Counts, temp Color Error l Color Error2 if( Num Colors!=0){/ Num Colors不为零,所以不是真彩图 Message Box(hWnd, Must be a true color bitmap ! " "Error Message" MB OK MB ICONEXCLAMATION) return falses ∥于颜色位数有可能发生了改变,所以要重新计算每行占用的字节数以及 ∥新图的缓冲区大小 DstLineBytes=(DWORD)WIDTHBYTES(bibi Width*8); Dst BufSize=(DWORD)(sizeof( BITMAPINFOHEADER)+ 256*sizeof(RGBQUAD)+ (DWORD)DstLine Bytes*bi. biHeight); SteF和 Dstbi为新的 BITMAPFILEHEADER和 BITMAPINFOHEADER ∥拷贝原来的头信息 memcpy((char *)&DstBf, (char *)&bf, sizeof( BITMAPFILEHEADER)); memcpy((char *)&Dst Bi, (char *)&bi, sizeof(BITMAPINFOHEADER)) ∥做必要的改变 DstBf. bfSize=Dst BufSize+sizeof( BITMAPFILEHEADER) DstBf. bfoffBits=(DWORD)(256*sizeof(RGBQUAD)+ sizeof( BITMAPFILEHEADEl +sizeof(BITMAPINFOHEADER))
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; WORD i,j; int Red,Green,Blue,ClrIndex; DWORD ColorHits[4096]; WORD ColorIndex[4096]; DWORD PalCounts,temp; long ColorError1,ColorError2; if(NumColors!=0){ //NumColors 不为零,所以不是真彩图 MessageBox(hWnd,"Must be a true color bitmap!","Error Message", MB_OK|MB_ICONEXCLAMATION); return FALSE; } //由于颜色位数有可能发生了改变,所以要重新计算每行占用的字节数以及 //新图的缓冲区大小 DstLineBytes=(DWORD)WIDTHBYTES(bi.biWidth*8); DstBufSize=(DWORD)(sizeof(BITMAPINFOHEADER)+ 256*sizeof(RGBQUAD)+ (DWORD)DstLineBytes*bi.biHeight); //DstBf 和 DstBi 为新的 BITMAPFILEHEADER 和 BITMAPINFOHEADER //拷贝原来的头信息 memcpy((char *)&DstBf,(char *)&bf,sizeof(BITMAPFILEHEADER)); memcpy((char *)&DstBi,(char *)&bi,sizeof(BITMAPINFOHEADER)); //做必要的改变 DstBf.bfSize=DstBufSize+sizeof(BITMAPFILEHEADER); DstBf.bfOffBits=(DWORD)(256*sizeof(RGBQUAD)+ sizeof(BITMAPFILEHEADER) +sizeof(BITMAPINFOHEADER));