C#下之asp.net中将真彩图转256色图的问题

大可山人
博客专家认证
2004-11-06 03:51:22
真彩图转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种颜色中,则用代替色的索引值填入位图数据中。
http://www-scf.usc.edu/~flv/ipbook/chap05.files/image008.jpg为原真彩图,
http://www-scf.usc.edu/~flv/ipbook/chap05.files/image009.gif是用上面的算法转换成的256色图,可以看出,效果还不错。

C++源代码:(谁可以将它转为C#的?)
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=OffBits+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,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-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++)=(unsigned char)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(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,DstBufSize);
_lclose(hf);
//释放内存和资源
ReleaseDC(hWnd,hDc);
LocalUnlock(hTempImgData);
LocalFree(hTempImgData);
GlobalUnlock(hImgData);
return TRUE;
}
不要怕难哟!
...全文
295 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
大可山人 2004-11-06
  • 打赏
  • 举报
回复
哦。速马的方法可以试试,不过不知道色彩失真度如何?
速马 2004-11-06
  • 打赏
  • 举报
回复
转换为GIF格式就行了吧...GIF就是256色
leisang 2004-11-06
  • 打赏
  • 举报
回复
哈哈,不错,学习一下先
cpio 2004-11-06
  • 打赏
  • 举报
回复
这么长,C#应该有更简单的办法吧

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧