




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
/第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是由蓝色和黑色组成的,但它实际上是二值图。原来,它的调色板中的两种颜色是黑与蓝,而不是黑与白.所以说二值图也可以是彩色的,只不过一般情况下是黑白图而已。下面的程序实现了反色,注意其中真彩图和调色板位图处理时的差别.BOOLInvert(HWNDhWnd){
DWORD
OffBits,BufSize;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;
unsignedchar
Red,Green,Blue;
OffBits=bf.bfOffBits-sizeof(BITMAP);
BufSize=OffBits+bi.biHeight*LineBytes;//新开缓冲区的大小
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL){MessageBox(hWnd,"Errorallocmemory!",”ErrorMessage”,MB_OK|MB_ICONEXCLAMATION);returnFALSE;}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=(unsignedchar)(*lpPtr++);
Green=(unsignedchar)(*lpPtr++);
Red=(unsignedchar)(*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++)=(unsignedchar)(255—Blue);
*(lpTempPtr++)=(unsignedchar)(255-Green);
*(lpTempPtr++)=(unsignedchar)(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=(unsignedchar)(*lpPtr++);
Green=(unsignedchar)(*lpPtr++);
Red=(unsignedchar)(*lpPtr++);
//反转位图数据中的颜色,存入新的位图数据中
*(lpTempPtr++)=(unsignedchar)(255-Blue);
*(lpTempPtr++)=(unsignedchar)(255—Green);
*(lpTempPtr++)=(unsignedchar)(255-Red);
}
}
}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(BITMAP));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
_lclose(hf);
//释放内存和资源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
returnTRUE;}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,写入新图即可。再来看看带调色板的彩色图,我们知道位图中的数据只是对应调色板中的一个索引值,我们只需要将调色板中的彩色变成灰度,形成新调色板,而位图数据不用动,就可以了。下面的程序实现了彩色图到灰度图的转换,注意其中真彩图和调色板位图处理时的差别。BOOLColortoGrayScale(HWNDhWnd){DWORD
SrcOffBits,SrcBufSize,DstBufSize,DstLineBytes;LPBITMAPINFOHEADER
lpImgData;
LPSTR
lpPtr;
HLOCAL
hTempImgData;
LPBITMAPINFOHEADER
lpTempImgData;
LPSTR
lpTempPtr;
HDC
hDc;
HFILE
hf;
LONG
x,y;
BITMAP
DstBf;
BITMAPINFOHEADER
DstBi;LOGPALETTE
*pPal;HPALETTE
hPrevPalette;
HLOCAL
hPal;
DWORD
NewNumColors;
WORD
NewBitCount;
float
Y;
DWORD
i;
unsignedchar
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);
//DstBf和DstBi为新的BITMAP和BITMAPINFOHEADER
//拷贝原来的头信息
memcpy((char*)&DstBf,(char*)&bf,sizeof(BITMAP));
memcpy((char*)&DstBi,(char*)&bi,sizeof(BITMAPINFOHEADER));
//做必要的改变
DstBf。bfSize=DstBufSize+sizeof(BITMAP);
DstBf.bfOffBits=(DWORD)(NewNumColors*sizeof(RGBQUAD)+sizeof(BITMAP)+sizeof(BITMAPINFOHEADER));
DstBi.biClrUsed=0;
DstBi。biBitCount=NewBitCount;
//原图的缓冲区的大小
SrcOffBits=bf.bfOffBits-sizeof(BITMAP);SrcBufSize=SrcOffBits+bi。biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,DstBufSize))==NULL){MessageBox(hWnd,"Errorallocmemory!”,”ErrorMessage”,MB_OK|MB_ICONEXCLAMATION);returnFALSE;
}
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;i<256;i++){//灰度从(0,0,0)到(255,255,255)
pPal—>palPalEntry[i].peRed=(BYTE)i;
pPal->palPalEntry[i]。peGreen=(BYTE)i;
pPal->palPalEntry[i].peBlue=(BYTE)i;
pPal->palPalEntry[i].peFlags=(BYTE)0;
*(lpTempPtr++)=(unsignedchar)i;
*(lpTempPtr++)=(unsignedchar)i;
*(lpTempPtr++)=(unsignedchar)i;
*(lpTempPtr++)=0;
}
else
for(i=0;i<NewNumColors;i++){//带调色板的彩色图
Blue=(unsignedchar)(*lpPtr++);
Green=(unsignedchar)(*lpPtr++);
Red=(unsignedchar)(*lpPtr++);
Y=(float)(Red*0.299+Green*0.587+Blue*0.114);
Gray=(BYTE)Y;
lpPtr++;
//从原来的调色板中的颜色计算得到Y值,写入新的调色板
pPal->palPalEntry[i].peRed=Gray;
pPal—>palPalEntry[i]。peGreen=Gray;
pPal—〉palPalEntry[i].peBlue=Gray;
pPal-〉palPalEntry[i].peFlags=0;
*(lpTempPtr++)=(unsignedchar)Gray;
*(lpTempPtr++)=(unsignedchar)Gray;
*(lpTempPtr++)=(unsignedchar)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++){
lpPtr=(char*)lpImgData+(SrcBufSize—LineBytes-y*LineBytes);
lpTempPtr=(char*)lpTempImgData+(DstBufSize-DstLineBytes-y*DstLineBytes);
for(x=0;x<bi.biWidth;x++){
Blue=(unsignedchar)(*lpPtr++);
Green=(unsignedchar)(*lpPtr++);
Red=(unsignedchar)(*lpPtr++);
Y=(float)(Red*0。299+Green*0.587+Blue*0。114);
//从位图数据计算得到Y值,写入新图中
Gray=(BYTE)Y;
*(lpTempPtr++)=(unsignedchar)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(BITMAP));
_lwrite(hf,(LPSTR)lpTempImgData,DstBufSize);
_lclose(hf);
//释放内存和资源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
returnTRUE;}5.3真彩图转256色图我们知道,真彩图中包含最多达224种颜色,怎样从中选出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色图下面是上述算法的源程序。BOOLTrueto256(HWNDhWnd){DWORD
SrcBufSize,OffBits,DstBufSize,DstLineBytes;LPBITMAPINFOHEADER
lpImgData;
LPSTR
lpPtr;
HLOCAL
hTempImgData;
LPBITMAPINFOHEADER
lpTempImgData;
LPSTR
lpTempPtr;
HDC
hDc;
HFILE
hf;
LONG
x,y;
BITMAP
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,"Mustbeatruecolorbitmap!","ErrorMessage",MB_OK|MB_ICONEXCLAMATION);returnFALSE;}//由于颜色位数有可能发生了改变,所以要重新计算每行占用的字节数以及//新图的缓冲区大小
DstLineBytes=(DWORD)WIDTHBYTES(bi。biWidth*8);
DstBufSize=(DWORD)(sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD)+(DWORD)DstLineBytes*bi.biHeight);
//DstBf和DstBi为新的BITMAP和BITMAPINFOHEADER
//拷贝原来的头信息
memcpy((char*)&DstBf,(char*)&bf,sizeof(BITMAP));
memcpy((char*)&DstBi,(char*)&bi,sizeof(BITMAPINFOHEADER));//做必要的改变
DstBf.bfSize=DstBufSize+sizeof(BITMAP);
DstBf。bfOffBits=(DWORD)(256*sizeof(RGBQUAD)+sizeof(BITMAP)
+sizeof(BITMAPINFOHEADER));
DstBi.biClrUsed=0;
DstBi.biBitCount=8;
//OffBits为到实际位图数据的偏移值
OffBits=bf。bfOffBits-sizeof(BITMAP);
//SrcBufSize为原图缓冲区的大小SrcBufSize=OffBits+bi.biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,DstBufSize))==NULL){MessageBox(hWnd,"Errorallocmemory!",”ErrorMessage",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;}
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
//拷贝位图数据
memcpy(lpTempImgData,lpImgData,OffBits);//用新的头信息取代旧的头信息
memcpy(lpTempImgData,(char*)&DstBi,sizeof(BITMAPINFOHEADER));//ColorHits为记录颜色使用频率的数组,ColorIndex为记录颜色索引值的//数组//先全部清零
memset(ColorHits,0,4096*sizeof(DWORD));
memset(ColorIndex,0,4096*sizeof(WORD));
for(y=0;y<bi。biHeight;y++){
lpPtr=(unsignedchar*)lpImgData+(SrcBufSize-LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
//R,G,B各取4位
Blue=(int)(*(lpPtr++)&0xf0);
Green=(int)(*(lpPtr++)&0xf0);
Red=(int)(*(lpPtr++)&0xf0);
//拼成一个12位整数
ClrIndex=(Blue<<4)+Green+(Red〉>4);
//相应的数组元素加1
ColorHits[ClrIndex]++;
}
}PalCounts=0;//将为零的元素清除出去
for(ClrIndex=0;ClrIndex<4096;ClrIndex++)
{
if(ColorHits[ClrIndex]!=0){
ColorHits[PalCounts]=ColorHits[ClrIndex];
//注意调整相应的索引值
ColorIndex[PalCounts]=ClrIndex;
PalCounts++;//颜色数加1
}
}//用起泡排序将PalCounts种颜色按从大到小的顺序排列
for(i=0;i<PalCounts-1;i++)
for(j=i+1;j<PalCounts;j++){
if(ColorHits[j]〉ColorHits[i]){
temp=ColorHits[i];
ColorHits[i]=ColorHits[j];
ColorHits[j]=temp;
//注意调整相应的索引值
temp=ColorIndex[i];
ColorIndex[i]=ColorIndex[j];
ColorIndex[j]=(WORD)temp;
}
}//为新的调色板分配内存
hPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY));
pPal=(LOGPALETTE*)LocalLock(hPal);
pPal—>palNumEntries=(WORD)256;
pPal—>palVersion
=0x300;
lpTempPtr=(char*)lpTempImgData+sizeof(BITMAPINFOHEADER);
for(i=0;i〈256;i++){//由12位索引值得到R,G,B的最高4位值
pPal—〉palPalEntry[i]。peRed=(BYTE)((ColorIndex[i]&0x00f)<〈4);
pPal-〉palPalEntry[i].peGreen=(BYTE)((ColorIndex[i]&0x0f0));
pPal—>palPalEntry[i]。peBlue=(BYTE)((ColorIndex[i]&0xf00)〉>4);
pPal—>palPalEntry[i].peFlags=(BYTE)0;
*(lpTempPtr++)=(unsignedchar)((ColorIndex[i]&0xf00)>>4);
*(lpTempPtr++)=(unsignedchar)((ColorIndex[i]&0x0f0));
*(lpTempPtr++)=(unsignedchar)((ColorIndex[i]&0x00f)<<4);
*(lpTempPtr++)=0;//ColorHits作为颜色记数的作用已经完成了,下面的作用是记录12位索//引值对应的调色板//中的索引值
ColorHits[i]=i;
}//其余的颜色依据最小平方误差近似为前256中最接近的一种
if(PalCounts〉256){
for(i=256;i〈PalCounts;i++){
//ColorError1记录最小平方误差,一开始赋一个很大的值
ColorError1=1000000000;//由12位索引值得到R,G,B的最高4位值
Blue=(long)((ColorIndex[i]&0xf00)>>4);
Green=(long)((ColorIndex[i]&0x0f0));
Red=(long)((ColorIndex[i]&0x00f)<〈4);
ClrIndex=0;
for(j=0;j〈256;j++){
//ColorError2计算当前的平方误差
ColorError2=(long)(Blue-pPal-〉palPalEntry[j].peBlue)*(Blue-pPal->palPalEntry[j].peBlue)+(long)(Green-pPal—>palPalEntry[j]。peGreen)*(Green-pPal-〉palPalEntry[j].peGreen)+
(long)(Red-pPal—>palPalEntry[j].peRed)*(Red-pPal->palPalEntry[j]。peRed);if(ColorError2<ColorError1){//找到更小的了
ColorError1=ColorError2;
ClrIndex=j;//记录对应的调色板的索引值
}
}//ColorHits记录12位索引值对应的调色板中的索引值
ColorHits[i]=ClrIndex;
}}
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+(SrcBufSize—LineBytes—y*LineBytes);
lpTempPtr=(char*)lpTempImgData+(DstBufSize-DstLineBytes-y*DstLineBytes);
for(x=0;x〈bi.biWidth;x++){
//R,G,B各取4位
Blue=(int)(*(lpPtr++)&0xf0);
Green=(int)(*(lpPtr++)&0xf0);
Red=(int)(*(lpPtr++)&0xf0);
//拼成一个12位整数
ClrIndex=(Blue〈〈4)+Green+(Red>>4);
for(i=0;i<PalCounts;i++)
if(ClrIndex==ColorIndex[i]){//根据12索引值取得对应的调色板中的索引值*(lpTempPtr++)=(unsignedchar)ColorHits[i];
break;
}
}
}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:\\256.bmp",0);
_lwrite(hf,(LPSTR)&DstBf,sizeof(BITMAP));
_lwrite(hf,(LPSTR)lpTempImgData,DstBufSize);
_lclose(hf);
//释放内存和资源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
returnTRUE;}以下我们将要介绍灰度变换,针对的都是256级灰度图。5。4对比度扩展假设有一幅图,由于成象时光照不足,使得整幅图偏暗(例如,灰度范围从0到63);或者成象时光照过强,使得整幅图偏亮(例如,灰度范围从200到255),我们称这些情况为低对比度,即灰度都挤在一起,没有拉开.灰度扩展的意思就是把你所感性趣的灰度范围拉开,使得该范围内的象素,亮的越亮,暗的越暗,从而达到了增强对比度的目的。我们可以用图5.5来说明对比度扩展(contraststretching)的原理。图5.5
对比度扩展的原理图5。5中的横坐标gold表示原图的灰度值,纵坐标gnew表示gold经过对比度扩展后得到了新的灰度值。a,b,c为三段直线的斜率,因为是对比度扩展,所以斜率b>1.g1old和g2old表示原图中要进行对比度扩展的范围,g1new和g2new表示对应的新值。用公式表示为
显然要得到对比度扩展后的灰度,我们需要知道a,b,c,g1old,g2old五个参数。由于有新图的灰度级别也是255这个约束,所以满足ag1old+b(gold—g1old)+c(255-g2old)=255这个方程。这样,我们只需给出四个参数,而另一个可以代入方程求得。我们假设a=c,这样,我们只要给出b,g1old和g2old,就可以求出a=(255-b(g2old—g1old))/(255—(g2old-g1old))要注意的是,给出的三个参数必须满:(1)b*(g2old—g1old)〈=255;(2)(g2old—g1old)〈=255。下图为图5.1取g1old=100,g2old=150,b=3.0进行对比度扩展的结果。可以看出亮的区域(雕塑)变得更亮,暗的区域(手)变得更暗.图5.6
图5。1对比度扩展后的结果下面的这段程序实现了对比度扩展。首先出现对话框,输入b,g1old,g2old的三个参数(在程序中分别是StretchRatio,SecondPoint,FirstPoint),然后对调色板做响应的处理,而实际的位图数据不用改动。BOOLContrastStretch(HWNDhWnd){DLGPROC
dlgInputBox=NULL;
DWORD
OffBits,BufSize;LPBITMAPINFOHEADER
lpImgData;
LPSTR
lpPtr;
HLOCAL
hTempImgData;
LPBITMAPINFOHEADER
lpTempImgData;
LPSTR
lpTempPtr;
HDC
hDc;
HFILE
hf;LOGPALETTE
*pPal;
HPALETTE
hPrevPalette=NULL;
HLOCAL
hPal;
DWORD
i;
unsignedchar
Gray;
float
a,g1,g2,g;
if(NumColors!=256){//必须是256级灰度图MessageBox(hWnd,”Mustbea256grayscalebitmap!","ErrorMessage”,MB_OK|MB_ICONEXCLAMATION);
returnFALSE;}//出现对话框,输入三个参数
dlgInputBox=(DLGPROC)MakeProcInstance((FARPROC)InputBox,ghInst);
DialogBox(ghInst,"INPUTBOX",hWnd,dlgInputBox);
FreeProcInstance((FARPROC)dlgInputBox);
if(StretchRatio*(SecondPoint-FirstPoint)>255.0){//参数不合法MessageBox(hWnd,"StretchRatio*(SecondPoint-FirstPoint)cannotbelargerthan255!",ErrorMessage",MB_OK|MB_ICONEXCLAMATION);returnFALSE;
}
if((SecondPoint-FirstPoint)>=255){//参数不合法
MessageBox(hWnd,"Theareayouselectedcannotbethewholescale!",”ErrorMessage”,MB_OK|MB_ICONEXCLAMATION);returnFALSE;}//计算出第一和第三段的斜率a
a=(float)((255.0—StretchRatio*(SecondPoint-FirstPoint))/(255.0—(SecondPoint—FirstPoint)));
//对比度扩展范围的边界点所对应的新的灰度
g1=a*FirstPoint;
g2=StretchRatio*(SecondPoint-FirstPoint)+g1;
//新开的缓冲区的大小
OffBits=bf。bfOffBits—sizeof(BITMAP);BufSize=OffBits+bi.biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{
MessageBox(hWnd,"Errorallocmemory!",”ErrorMessage",MB_OK|MB_ICONEXCLAMATION);returnFALSE;
}
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);//拷贝头信息和实际位图数据
memcpy(lpTempImgData,lpImgData,BufSize);
hDc=GetDC(hWnd);
//lpPtr指向原图数据缓冲区,lpTempPtr指向新图数据缓冲区
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<256;i++){
Gray=(unsignedchar)*lpPtr;
lpPtr+=4;
//进行对比度扩展
if(Gray〈FirstPoint)g=(float)(a*Gray);
elseif(Gray<SecondPoint)g=g1+StretchRatio*(Gray—FirstPoint);
elseg=g2+a*(Gray-SecondPoint);
pPal-〉palPalEntry[i]。peRed=(BYTE)g;
pPal-〉palPalEntry[i].peGreen=(BYTE)g;
pPal—>palPalEntry[i]。peBlue=(BYTE)g;
pPal-〉palPalEntry[i]。peFlags=0;
*(lpTempPtr++)=(unsignedchar)g;
*(lpTempPtr++)=(unsignedchar)g;
*(lpTempPtr++)=(unsignedchar)g;
*(lpTempPtr++)=0;
}
if(hPalette!=NULL)
DeleteObject(hPalette);
//产生新的逻辑调色板
hPalette=CreatePalette(pPal);
LocalUnlock(hPal);
LocalFree(hPal);
if(hPalette){
hPrevPalette=SelectPalette(hDc,hPalette,FALSE);
RealizePalette(hDc);
}
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:\\stretch.bmp",0);
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAP));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
_lclose(hf);
//释放内存和资源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
returnTRUE;}5。5削波削波(cliping)可以看作是对比度扩展的一个特例,我们用图5。7说明削波的原理。图5.7
削波的原理不难看出,只要令对比度扩展中的a=c=0就实现了削波.我们只要给出范围的两个端点,斜率b就可以用方程b(g2old-g1old)=255求出。图5.8为图5.1取g1old=150,g2old=200进行削波的结果。把亮的区域(雕塑)提取了出来。图5.8
图5。1削波处理后的结果削波的程序和对比度扩展的程序很类似,就不再给出了。5。6阈值化阈值化(thresholding)可以看作是削波的一个特例,我们用图5.9说明阈值化的原理。图5。9
阈值化的原理不难看出,只要令削波中的g1old=g2old就实现了阈值化.阈值就象个门槛,比它大就是白,比它小就是黑。经过阈值化处理后的图象变成了黑白二值图,所以说阈值化是灰度图转二值图的一种常用方法(我们以前介绍过图案化和抖动的方法).进行阈值化只需给出阈值点g1old即可.图5.10为图5。1阈值取128,阈值化处理后的结果,是一幅黑白图。图5.10
图5。1阈值化处理后的结果阈值化的程序和对比度扩展的程序很类似,就不再给出了.5.7灰度窗口变换灰度窗口变换(slicing)是将某一区间的灰度级和其它部分(背景)分开。我们用图5.11和图5。12说明灰度窗口变换的原理。其中[g1old,g2old]称为灰度窗口。图5.11
清除背景的灰度窗口变换的原理图5。12
保留背景的灰度窗口变换的原理灰度窗口变换有两种,一种是清除背景的,一种是保留背景的。前者把不在灰度窗口范围内的象素都赋值为0,在灰度窗口范围内的象素都赋值为255,这也能实现灰度图的二值化;后者是把不在灰度窗口范围内的象素保留原灰度值,在灰度窗口范围内的象素都赋值为255。灰度窗口变换可以检测出在某一灰度窗口范围内的所有象素,是图象灰度分析中的一个有力工具。下面有三幅图,图5.13为原图;图5.14是经过清除背景的灰度窗口变换处理后的图(灰度窗口取[200—255]),将夜景中大厦里的灯光提取了出来;图5.15是经过保留背景的灰度窗口变换处理后的图(灰度窗口取[200-255]),将夜景中大厦里的灯光提取了出来,同时保留了大厦的背景,可以看出它们的差别还是很明显的。图5。13原图图5。14图5.13经过清除背景的灰度窗口变换处理后的图图5。15
图5。13经过保留背景的灰度窗口变换处理后的图灰度窗口变换的程序和对比度扩展的程序很类似,就不再给出了.不久前在一本科学杂志上看到一篇文章,非常有趣,是介绍电影“阿甘正传"的特技制作的。其中有一项就用到了类似灰度窗口变换的思想。相信看过这部电影的读者都会对那个断腿的丹尼上校有深刻的印象。他的断腿是怎么拍出来的呢?其实方法很简单,先拍一幅没有演员出现的背景画面,然后拍一幅有演员出现,其它不变的画面.要注意的是,此时演员的腿用蓝布包裹.把前后两幅图输入计算机进行处理。第二幅图中凡是遇到蓝色的象素,就用第一幅图中对应位置的背景象素代替.这样,一位断腿的上校就逼真的出现在屏幕上了。这就是电影特技中经常用到的“蓝幕"技术。说点题外话。其实现代电影,特别是好莱坞电影,越来越离不开计算机及图象处理技术。最近引起轰动的大片“泰坦尼克号”中的很多特技镜头就是利用了庞大的SGI图形工作站机群没日没夜的计算产生的.图象处理技术和我们所喜爱的电影艺术紧密的结合了起来,更增加了我们学习它的兴趣。5.8灰度直方图统计有时我们需要知道一幅图中的灰度分布情况,这时就可以采用灰度直方图(histogram)来表示,图中的横坐标表示灰度值,纵坐标表示该灰度值出现的次数(频率)。图5。16为图5。13的灰度直方图,低灰度的象素占了绝大部分。图5.16
图5。13的灰度直方图下面的程序显示一幅图的灰度直方图。有两段程序,第一段统计出每个灰度的象素个数,存放在数组GrayTable[]中,然后产生一个新的窗口,把统计结果显示出来.第二段程序就是该窗口的消息处理函数。要注意的是,由于各灰度出现的频率可能相差很大,所以如何将结果显示在有限的窗口范围内,是一个必须考虑的问题。我们这里的做法是,在所有出现的灰度中,统计出一个最大值max和一个最小值min,假设能显示的窗口最大坐标为270,最小坐标为5,按成比例显示,这样,灰度出现的次数和显示坐标之间呈线形关系。设a×grayhits+b=coordinate,其中grayhits为灰度出现的次数,coordinate为显示坐标,a和b为两个常数。我们将max和min代入,应该满足a×max+b=270,a×min+b=5;由此可以解得a=265/(max-min),b=270。0-a×max.还有一点,不要忘了在WinMain函数中注册那个新产生窗口的窗口类。intGrayTable[256];intMaxGrayNum;intMinGrayNum;BOOLHistogram(HWNDhWnd){DWORD
OffBits,BufSize;LPBITMAPINFOHEADER
lpImgData;
LPSTR
lpPtr;
int
x,y;
int
grayindex;
HWND
hPopupWnd;
int
temp;
//计数器清零
for(grayindex=0;grayindex<256;grayindex++)
GrayTable[grayindex]=0;
//OffBits为到实际位图数据的偏移值
OffBits=bf.bfOffBits-sizeof(BITMAP);//BufSize为缓冲区的大小
BufSize=OffBits+bi。biHeight*LineBytes;
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
for(y=0;y〈bi。biHeight;y++){
lpPtr=(char*)lpImgData+(BufSize-LineBytes-y*LineBytes);
for(x=0;x<bi.biWidth;x++){
grayindex=(unsignedchar)*(lpPtr++);
Gr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年中国通信行业招聘考试热点解析
- 2025年中国航信项目管理面试模拟题解析从理论到实践的应用
- 2025年乡镇政府招聘考试模拟题及答题技巧
- 2025年乡村物流管理专业人才招聘考试指南及试题库
- 2025年乡村直播销售员中级考试复习建议与经验
- 人教版初中历史与社会八年级上册 1.3.2 罗马帝国的兴衰 教学设计
- 2025年乡村治理与社区管理人才引进面试题
- 第一节 压力的作用效果说课稿-2025-2026学年初中物理沪科版八年级全一册-沪科版2012
- 2025年人力资源经理应聘笔试预测题及解析
- 2025年中国铁路总公司招聘笔试模拟题及参考答案详解
- 2025年学法普法综合知识竞赛答题题库(附答案)
- 2025年河北唐山市芦台经济开发区公开招聘区属国有企业工作人员18人笔试模拟试题及答案解析
- 酒店突发事件应急预案2025优化版
- 冷水滩区2021上半年事业单位计算机岗位专业知识试题
- 马克思政治经济学考试题库含答案全套
- 渤中19-6凝析气田试验区开发项目(第二阶段)环评报告
- 部编版七年级历史上册练习题(全册-含答案)
- 微电网及储能技术
- 变压器主保护基本知识测试题
- 临汾市社区工作者考试题库2023
- 转型中的地方政府:官员激励与治理(第二版)
评论
0/150
提交评论