24位转8位位图,不是灰度,有颜色的,尽量减少失真

具体做法见函数内注释

void CDib::ConverTo8Bit()
{
 //24位转8位,尽量减少失真
 //方法:
 //首先根据24位图颜色信息,取颜色的高4位进行组合成12位的信息,然后作为索引记录数组,数组内容记录出现的次数
 //然后对数组排序,计算颜色命中次数,取最大的256种作为调色板
 //对24位位图中其他颜色计算平方误差,用调色板中颜色与24位各点颜色差的平方,再把红绿蓝三色作和,取结果,最小的最为匹配
 //将最匹配的256色调色板索引写入256色位图相应的位置,并保存
 LPBITMAPINFOHEADER  lpbi;
 if(!hdib)
  return;
 lpbi = (BITMAPINFOHEADER*)hdib;
 int height = lpbi->biHeight;
 //两个宽度
 LONG lLineBytes24=((lpbi->biWidth*24+31)/32*4);
 LONG lLineBytes8=((lpbi->biWidth*8+31)/32*4);
 //源图像的指针
 BYTE* srcBits =NULL;
 //使其指向源图像的图像数据起始地址
 srcBits = (BYTE*)lpbi+sizeof(BITMAPINFOHEADER);
 //转换后的图像数据指针(大小为图片大小和调色板大小之和
 int iPaletteSize = sizeof(RGBQUAD)*256;//调色板大小
 BYTE* dstBits = (BYTE*)malloc(iPaletteSize+lLineBytes8*height);
 RGBQUAD* pRGB = (RGBQUAD *)dstBits;//调色板指针,指向数据部分开始地址
 //寻找原来24位图里的颜色信息,并按出现的频率加权值
 DWORD   ColorHits[4096];   //颜色命中
 WORD   ColorIndex[4096]; //颜色索引
 //ColorHits为记录颜色使用频率的数组,ColorIndex为记录颜色索引值的数组 
 memset(ColorHits, 0, 4096 * sizeof(DWORD)); 
 memset(ColorIndex, 0, 4096 * sizeof(WORD));
 //计算命中次数
 for(int y=0; y<height; y++)
 {
  BYTE* lpBits=srcBits+y*lLineBytes24;
  for(int x=0; x<lLineBytes24; )
  {
   int B = (int)(*(lpBits+x)&0xf0); //只取高四位
   x++;
   int G = (int)(*(lpBits+x)&0xf0);
   x++;
   int R = (int)(*(lpBits+x)&0xf0);
   x++;
   int ClrIndex=(B<<4) + G + (R>>4);  //拼成一个位整数,由于只取高四位,故拼后为4*3=12位,即2的12次方为4096项,所以索引里为4096大小的数组
   ColorHits[ClrIndex]++;//命中次数加1
  }
 }
 DWORD PalCounts=0;//最多调色板项数目
 //清除数组中为0的元素,为下面的排序提高效率
 for(int ClrIndex=0; ClrIndex<4096; ClrIndex++)
 {
  if( ColorHits[ClrIndex]!=0 )
  {
   ColorHits[PalCounts] = ColorHits[ClrIndex]; //由于ClrIndex>=PalCounts,故可以这样赋值
   ColorIndex[PalCounts]=ClrIndex;      //注意调整相应的索引值 
   PalCounts++;   //颜色数加
  }
 }
 //排序,从大到小,标准的冒泡排序
 for (int i = 0;   i < PalCounts-1; i++) 
 {
  for (int j = i + 1; j < PalCounts; j++)  
  {  
   if (ColorHits[j] > ColorHits[i])   // 把大的值排到前面
   {  
    DWORD dwTmp = ColorHits[i];  
    ColorHits[i] = ColorHits[j];  
    ColorHits[j] = dwTmp;      
    //注意调整相应的索引值 
    dwTmp = ColorIndex[i];  
    ColorIndex[i] = ColorIndex[j];  
    ColorIndex[j] = (WORD)dwTmp;  
   }  
  } 
 }
 //为新的调色板分配内存 
 //RGBQUAD pRGB[256];   // 临时的颜色表信息,后面要用到
 for   (int i   =   0;   i   <   256;   i++)    
 {  
  //由位索引值得到R,G,B的最高位值 
  pRGB[i].rgbRed=(BYTE)((ColorIndex[i]   &   0x00f)   <<   4);  
  pRGB[i].rgbGreen=(BYTE)((ColorIndex[i]   &   0x0f0));  
  pRGB[i].rgbBlue=(BYTE)((ColorIndex[i]   &   0xf00)   >>   4);  
  pRGB[i].rgbReserved =(BYTE)0;
  ColorHits[i] = i;   //ColorHits作为颜色记数的作用已经完成了,下面的作用是记录位索引值对应的调色板中的索引值
 }
 //其余的颜色依据最小平方误差近似为前中最接近的一种 
 long   ColorError1,ColorError2;
 if (PalCounts > 256)  
 {  
  for (int i = 256; i < PalCounts; i++)  
  {  
   //ColorError1记录最小平方误差,一开始赋一个很大的值 
   ColorError1=1000000000;  
   //由位索引值得到R,G,B的最高位值 
   long Blue = (long)((ColorIndex[i] & 0xf00) >> 4);  
   long Green = (long)((ColorIndex[i] & 0x0f0));  
   long Red = (long)((ColorIndex[i] & 0x00f) << 4);  
   int ClrIndex   =   0;  
   for   (int j   =   0;   j   <   256;   j++)  
   {  
    //ColorError2计算当前的平方误差 
    ColorError2=(long)(Blue - pRGB[j].rgbBlue)*  
     (Blue - pRGB[j].rgbBlue)+   (long)(Green - pRGB[j].rgbGreen)*  
     (Green - pRGB[j].rgbGreen)+  
     (long)(Red - pRGB[j].rgbRed)*  
     (Red - pRGB[j].rgbRed);    
    if   (ColorError2   <   ColorError1)  
    {   //找到更小的了 
     ColorError1   =   ColorError2;  
     ClrIndex   =   j;   //记录对应的调色板的索引值 
    }  
   }  
   //ColorHits记录位索引值对应的调色板中的索引值 
   ColorHits[i]   =   ClrIndex;  
  }
 }
 //匹配24位的颜色信息,并把调色板索引写入8位图里
 BYTE* pDstBits = dstBits+iPaletteSize;//指向目标位图调色板后
 for(int y=0; y<height; y++)
 {
  BYTE* lpBits=srcBits+y*lLineBytes24;
  for(int x=0,n=0; x<lLineBytes24; )
  {
   int Blue = (int)(*(lpBits+x)&0xf0); //只取高四位
   x++;
   int Green = (int)(*(lpBits+x)&0xf0);
   x++;
   int Red = (int)(*(lpBits+x)&0xf0);
   x++;
   //拼成一个位整数 
   int ClrIndex=(Blue<<4)+Green+(Red>>4);
   for (int i = 0; i < PalCounts; i++)
   {
    if (ClrIndex == ColorIndex[i])  
    {  
     //根据索引值取得对应的调色板中的索引值 
     *pDstBits = (unsigned   char)ColorHits[i];//写入目标位图
     *(pDstBits+ lLineBytes8*y + n) = (unsigned   char)ColorHits[i];//写入目标位图
     n++;
     break;  
    }
   }
  }
 }
 //8位BMP的信息头
 BITMAPINFOHEADER bi;
 bi.biBitCount=8;
 bi.biClrImportant=0;
 bi.biClrUsed=0;
 bi.biCompression=0L;
 bi.biHeight=Height();
 bi.biPlanes=1;
 bi.biSize=sizeof(BITMAPINFOHEADER);
 bi.biSizeImage=Height()*lLineBytes8;
 bi.biWidth=Width();
 bi.biXPelsPerMeter=0;
 bi.biYPelsPerMeter=0;
 SetBitmapinfoAndBits(bi, dstBits);
 CString FileName;
 strFileName.Delete(strFileName.GetLength()-4, 4);
 FileName.Format("%s的8位图.bmp",strFileName);
 //调用保存文件函数
 SaveFile(FileName);
 //收尾清除指针内存
 //free(srcBits);
 srcBits = NULL;
 free(dstBits);
 dstBits = NULL;
 AfxMessageBox("已经转换成8位图,另存为:"+FileName);
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值