📄 图像处理.txt
字号:
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++){
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);
GlobalUnlock(hImgData);
return TRUE;
}
3. 真彩图转256色图(true color to 256 indexed color)
我们知道,真彩图中包含最多达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种颜色中,则用代替色的索引值填入位图数据中。
下面的两幅图,图3是原真彩图,图4是用上面的算法转成的256色图,可以看出,效果还不错。
图3. 原真彩图 图4. 转换后的256色图
下面是上述算法的源程序。
BOOL Trueto256(HWND hWnd)
{
DWORD
SrcBufSize,OffBits,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;
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));
DstBi.biClrUsed=0;
DstBi.biBitCount=8;
//OffBits为到实际位图数据的偏移值
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
//SrcBufSize为原图缓冲区的大小
SrcBufSize=bf.bfSize-sizeof(BITMAPFILEHEADER);
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,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=(unsigned char *)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++)=(unsigned char)((ColorIndex[i] & 0xf00) >> 4);
*(lpTempPtr++)=(unsigned char)((ColorIndex[i] & 0x0f0));
*(lpTempPtr++)=(unsigned char)((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-
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -