将24位真彩色位图保存为256色(8位)位图
2009-09-15 20:08
603 查看
24位真彩色位图如果想保存位256色,即8位位图,关键是构造合适的调色板,同时考虑程序的运行效率。如果采用Windows自带的调色版来创建,则最后生成的图片失真太大,甚至惨不忍睹。下面采用一个更加优化的方式,使得生成的256色位图基本和24位的原图一致,函数代码如下:
void Save256Bmp(void)
{
// TODO: 在此添加实现代码
//::MessageBox(NULL, _T("test"), _T("Note"), MB_OK);
//准备拷贝桌面图像
HDC hDeskTopDC = ::GetDC(NULL); //取得桌面DC
HDC hMemDC = ::CreateCompatibleDC(hDeskTopDC); //创建兼容DC
int iScreenX = ::GetSystemMetrics(SM_CXSCREEN);
int iScreenY = ::GetSystemMetrics(SM_CYSCREEN);
HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDeskTopDC, iScreenX, iScreenY); //创建兼容位图
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hMemBmp);
//拷贝图像
::BitBlt(hMemDC, 0, 0, iScreenX, iScreenY, hDeskTopDC, 0, 0, SRCCOPY);
SaveDCToBmp(hMemDC, hMemBmp, _T("C:/temp.bmp"));
//设置位图信息头结构
BITMAPINFOHEADER tInfoHeader;
tInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
tInfoHeader.biWidth = iScreenX;
tInfoHeader.biHeight = iScreenY;
tInfoHeader.biPlanes = 1;
tInfoHeader.biBitCount = 24; // 256色位图是位,即一个颜色点占个字节
tInfoHeader.biCompression = BI_RGB;
tInfoHeader.biSizeImage = 0;
tInfoHeader.biXPelsPerMeter = 0;
tInfoHeader.biYPelsPerMeter = 0;
tInfoHeader.biClrUsed = 0;
tInfoHeader.biClrImportant = 0;
DWORD SrcBufSize; // 源图缓冲区大小
DWORD OffBits;
DWORD DstBufSize;
LPBITMAPINFOHEADER lpImgData;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
HDC hDc;
HFILE hf;
BITMAPFILEHEADER DstBf;
BITMAPINFOHEADER DstBi;
LOGPALETTE *pPal;
HPALETTE hPrevPalette;
HLOCAL hPal;
WORD i,j;
int Red,Green,Blue,ClrIndex;
DWORD ColorHits[4096];
WORD ColorIndex[4096];
int DstLineBytes = 0;
if (tInfoHeader.biWidth % 4 == 0)
{
DstLineBytes = tInfoHeader.biWidth * 3;
}
else
{
DstLineBytes = (tInfoHeader.biWidth + 4 - tInfoHeader.biWidth % 4 ) * 3; //计算大于图像宽度的最小的的倍速
}
//DstLineBytes = (DWORD)WIDTHBYTES(tInfoHeader.biWidth * 8); // 计算图像每行象素所占的字节数目,设置成的整数倍,参数是一行像素的位数总和
DstBufSize = (DWORD)DstLineBytes * tInfoHeader.biHeight; //图像数据的字节大小
//分配内存
BYTE* pBuffer = new BYTE[DstBufSize];
if (pBuffer == NULL)
{
MessageBox(NULL,_T("Error alloc memory!"), _T("Error Message"),MB_OK| MB_ICONEXCLAMATION);
return FALSE;
}
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pBuffer;
*lpbi = tInfoHeader;
int bRet = ::GetDIBits(hMemDC, hMemBmp, 0, tInfoHeader.biHeight, pBuffer,(LPBITMAPINFO)&tInfoHeader, DIB_RGB_COLORS);
//ColorHits为记录颜色使用频率的数组,ColorIndex为记录颜色索引值的数组
memset(ColorHits, 0, 4096 * sizeof(DWORD));
memset(ColorIndex, 0, 4096 * sizeof(WORD));
BYTE * lpPtr;
for(int y=0; y < tInfoHeader.biHeight; y++)
{
lpPtr = pBuffer + (DstBufSize - DstLineBytes - y * DstLineBytes);
for(int x = 0; x < tInfoHeader.biWidth; x++)
{
//R,G,B各取位
Blue=(int)(*(lpPtr++) & 0xf0);
Green=(int)(*(lpPtr++) & 0xf0);
Red=(int)(*(lpPtr++) & 0xf0);
ClrIndex=(Blue<<4) + Green +(Red >>4); //拼成一个位整数
ColorHits[ClrIndex]++; //相应的数组元素加
}
}
//将为零的元素清除出去
DWORD PalCounts=0;
for (ClrIndex = 0; ClrIndex < 4096; ClrIndex++)
{
if(ColorHits[ClrIndex] != 0)
{
ColorHits[PalCounts] = ColorHits[ClrIndex]; // 比如白色的颜色数量是个,付给使用到的颜色数组
ColorIndex[PalCounts]=ClrIndex; //注意调整相应的索引值
PalCounts++; //颜色数加
}
}
//用起泡排序将PalCounts种颜色按从大到小的顺序排列
DWORD dwTmp;
for (i = 0; i < PalCounts-1; i++)
{
for (j = i + 1; j < PalCounts; j++)
{
if (ColorHits[j] > ColorHits[i]) // 把大的值排到前面
{
dwTmp = ColorHits[i];
ColorHits[i] = ColorHits[j];
ColorHits[j] = dwTmp;
//注意调整相应的索引值
dwTmp = ColorIndex[i];
ColorIndex[i] = ColorIndex[j];
ColorIndex[j] = (WORD)dwTmp;
}
}
}
//为新的调色板分配内存
RGBQUAD tRGBQTmp[256]; // 临时的颜色表信息,后面要用到
for (i = 0; i < 256; i++)
{
//由位索引值得到R,G,B的最高位值
tRGBQTmp[i].rgbRed=(BYTE)((ColorIndex[i] & 0x00f) << 4);
tRGBQTmp[i].rgbGreen=(BYTE)((ColorIndex[i] & 0x0f0));
tRGBQTmp[i].rgbBlue=(BYTE)((ColorIndex[i] & 0xf00) >> 4);
tRGBQTmp[i].rgbReserved =(BYTE)0;
ColorHits[i] = i; //ColorHits作为颜色记数的作用已经完成了,下面的作用是记录位索引值对应的调色板中的索引值
}
//其余的颜色依据最小平方误差近似为前中最接近的一种
long ColorError1,ColorError2;
if (PalCounts > 256)
{
for (i = 256; i < PalCounts; i++)
{
//ColorError1记录最小平方误差,一开始赋一个很大的值
ColorError1=1000000000;
//由位索引值得到R,G,B的最高位值
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 - tRGBQTmp[j].rgbBlue)*
(Blue - tRGBQTmp[j].rgbBlue)+ (long)(Green - tRGBQTmp[j].rgbGreen)*
(Green - tRGBQTmp[j].rgbGreen)+
(long)(Red - tRGBQTmp[j].rgbRed)*
(Red - tRGBQTmp[j].rgbRed);
if (ColorError2 < ColorError1)
{ //找到更小的了
ColorError1 = ColorError2;
ClrIndex = j; //记录对应的调色板的索引值
}
}
//ColorHits记录位索引值对应的调色板中的索引值
ColorHits[i] = ClrIndex;
}
}
// 把颜色数据替换成颜色表里的索引
BYTE * pRetIndex = new BYTE[tInfoHeader.biWidth * tInfoHeader.biHeight];
if (pRetIndex == NULL)
{
return FALSE;
}
int iOffset = 0;
for(int y = 0; y < tInfoHeader.biHeight;y++)
{
lpPtr = pBuffer + y * DstLineBytes;
for(int x = 0; x < tInfoHeader.biWidth; x++)
{
//R,G,B各取位
Blue=(int)(*(lpPtr++) & 0xf0);
Green=(int)(*(lpPtr++) & 0xf0);
Red=(int)(*(lpPtr++) & 0xf0);
//拼成一个位整数
ClrIndex=(Blue<<4) + Green +(Red >>4);
for (i = 0; i < PalCounts; i++)
{
if (ClrIndex == ColorIndex[i])
{
//根据索引值取得对应的调色板中的索引值
pRetIndex[iOffset++] = (unsigned char)ColorHits[i];
break;
}
}
}
}
//产生新的位图
// hf=_lcreat("c://256.bmp",0);
FILE * pFile = _tfopen(_T("c://256.bmp"), _T("w+b"));
if (pFile == NULL)
{
return FALSE;
}
BITMAPFILEHEADER tFileHead; //文件头
BITMAPINFO Info;
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = iScreenX;
Info.bmiHeader.biHeight = iScreenY;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = 8; // 256色位图是位,即一个颜色点占个字节
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = 0;
Info.bmiHeader.biXPelsPerMeter = 0;
Info.bmiHeader.biYPelsPerMeter = 0;
Info.bmiHeader.biClrUsed = 0;
Info.bmiHeader.biClrImportant = 0;
tFileHead.bfType = (WORD)('M'<<8)|'B'; //位图文件头位“BM”
tFileHead.bfSize = sizeof(BITMAPFILEHEADER) + DstBufSize; // BMP文件的字节大小
tFileHead.bfReserved1 = 0;
tFileHead.bfReserved2 = 0;
tFileHead.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
fwrite((LPSTR)&tFileHead,sizeof(BITMAPFILEHEADER), 1, pFile); // 写文件头
fwrite((LPSTR)&Info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
fwrite(&tRGBQTmp, sizeof(RGBQUAD) * 256, 1, pFile);
fwrite((LPSTR)pRetIndex, tInfoHeader.biWidth * tInfoHeader.biHeight, 1, pFile); // 写索引数据
fclose(pFile);
//释放内存和资源
delete []pBuffer;
delete []pRetIndex;
SelectObject(hMemDC, hOldBmp);
::DeleteObject(hMemBmp);
::DeleteDC(hMemDC);
}
void Save256Bmp(void)
{
// TODO: 在此添加实现代码
//::MessageBox(NULL, _T("test"), _T("Note"), MB_OK);
//准备拷贝桌面图像
HDC hDeskTopDC = ::GetDC(NULL); //取得桌面DC
HDC hMemDC = ::CreateCompatibleDC(hDeskTopDC); //创建兼容DC
int iScreenX = ::GetSystemMetrics(SM_CXSCREEN);
int iScreenY = ::GetSystemMetrics(SM_CYSCREEN);
HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDeskTopDC, iScreenX, iScreenY); //创建兼容位图
HBITMAP hOldBmp = (HBITMAP)::SelectObject(hMemDC, hMemBmp);
//拷贝图像
::BitBlt(hMemDC, 0, 0, iScreenX, iScreenY, hDeskTopDC, 0, 0, SRCCOPY);
SaveDCToBmp(hMemDC, hMemBmp, _T("C:/temp.bmp"));
//设置位图信息头结构
BITMAPINFOHEADER tInfoHeader;
tInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
tInfoHeader.biWidth = iScreenX;
tInfoHeader.biHeight = iScreenY;
tInfoHeader.biPlanes = 1;
tInfoHeader.biBitCount = 24; // 256色位图是位,即一个颜色点占个字节
tInfoHeader.biCompression = BI_RGB;
tInfoHeader.biSizeImage = 0;
tInfoHeader.biXPelsPerMeter = 0;
tInfoHeader.biYPelsPerMeter = 0;
tInfoHeader.biClrUsed = 0;
tInfoHeader.biClrImportant = 0;
DWORD SrcBufSize; // 源图缓冲区大小
DWORD OffBits;
DWORD DstBufSize;
LPBITMAPINFOHEADER lpImgData;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
HDC hDc;
HFILE hf;
BITMAPFILEHEADER DstBf;
BITMAPINFOHEADER DstBi;
LOGPALETTE *pPal;
HPALETTE hPrevPalette;
HLOCAL hPal;
WORD i,j;
int Red,Green,Blue,ClrIndex;
DWORD ColorHits[4096];
WORD ColorIndex[4096];
int DstLineBytes = 0;
if (tInfoHeader.biWidth % 4 == 0)
{
DstLineBytes = tInfoHeader.biWidth * 3;
}
else
{
DstLineBytes = (tInfoHeader.biWidth + 4 - tInfoHeader.biWidth % 4 ) * 3; //计算大于图像宽度的最小的的倍速
}
//DstLineBytes = (DWORD)WIDTHBYTES(tInfoHeader.biWidth * 8); // 计算图像每行象素所占的字节数目,设置成的整数倍,参数是一行像素的位数总和
DstBufSize = (DWORD)DstLineBytes * tInfoHeader.biHeight; //图像数据的字节大小
//分配内存
BYTE* pBuffer = new BYTE[DstBufSize];
if (pBuffer == NULL)
{
MessageBox(NULL,_T("Error alloc memory!"), _T("Error Message"),MB_OK| MB_ICONEXCLAMATION);
return FALSE;
}
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pBuffer;
*lpbi = tInfoHeader;
int bRet = ::GetDIBits(hMemDC, hMemBmp, 0, tInfoHeader.biHeight, pBuffer,(LPBITMAPINFO)&tInfoHeader, DIB_RGB_COLORS);
//ColorHits为记录颜色使用频率的数组,ColorIndex为记录颜色索引值的数组
memset(ColorHits, 0, 4096 * sizeof(DWORD));
memset(ColorIndex, 0, 4096 * sizeof(WORD));
BYTE * lpPtr;
for(int y=0; y < tInfoHeader.biHeight; y++)
{
lpPtr = pBuffer + (DstBufSize - DstLineBytes - y * DstLineBytes);
for(int x = 0; x < tInfoHeader.biWidth; x++)
{
//R,G,B各取位
Blue=(int)(*(lpPtr++) & 0xf0);
Green=(int)(*(lpPtr++) & 0xf0);
Red=(int)(*(lpPtr++) & 0xf0);
ClrIndex=(Blue<<4) + Green +(Red >>4); //拼成一个位整数
ColorHits[ClrIndex]++; //相应的数组元素加
}
}
//将为零的元素清除出去
DWORD PalCounts=0;
for (ClrIndex = 0; ClrIndex < 4096; ClrIndex++)
{
if(ColorHits[ClrIndex] != 0)
{
ColorHits[PalCounts] = ColorHits[ClrIndex]; // 比如白色的颜色数量是个,付给使用到的颜色数组
ColorIndex[PalCounts]=ClrIndex; //注意调整相应的索引值
PalCounts++; //颜色数加
}
}
//用起泡排序将PalCounts种颜色按从大到小的顺序排列
DWORD dwTmp;
for (i = 0; i < PalCounts-1; i++)
{
for (j = i + 1; j < PalCounts; j++)
{
if (ColorHits[j] > ColorHits[i]) // 把大的值排到前面
{
dwTmp = ColorHits[i];
ColorHits[i] = ColorHits[j];
ColorHits[j] = dwTmp;
//注意调整相应的索引值
dwTmp = ColorIndex[i];
ColorIndex[i] = ColorIndex[j];
ColorIndex[j] = (WORD)dwTmp;
}
}
}
//为新的调色板分配内存
RGBQUAD tRGBQTmp[256]; // 临时的颜色表信息,后面要用到
for (i = 0; i < 256; i++)
{
//由位索引值得到R,G,B的最高位值
tRGBQTmp[i].rgbRed=(BYTE)((ColorIndex[i] & 0x00f) << 4);
tRGBQTmp[i].rgbGreen=(BYTE)((ColorIndex[i] & 0x0f0));
tRGBQTmp[i].rgbBlue=(BYTE)((ColorIndex[i] & 0xf00) >> 4);
tRGBQTmp[i].rgbReserved =(BYTE)0;
ColorHits[i] = i; //ColorHits作为颜色记数的作用已经完成了,下面的作用是记录位索引值对应的调色板中的索引值
}
//其余的颜色依据最小平方误差近似为前中最接近的一种
long ColorError1,ColorError2;
if (PalCounts > 256)
{
for (i = 256; i < PalCounts; i++)
{
//ColorError1记录最小平方误差,一开始赋一个很大的值
ColorError1=1000000000;
//由位索引值得到R,G,B的最高位值
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 - tRGBQTmp[j].rgbBlue)*
(Blue - tRGBQTmp[j].rgbBlue)+ (long)(Green - tRGBQTmp[j].rgbGreen)*
(Green - tRGBQTmp[j].rgbGreen)+
(long)(Red - tRGBQTmp[j].rgbRed)*
(Red - tRGBQTmp[j].rgbRed);
if (ColorError2 < ColorError1)
{ //找到更小的了
ColorError1 = ColorError2;
ClrIndex = j; //记录对应的调色板的索引值
}
}
//ColorHits记录位索引值对应的调色板中的索引值
ColorHits[i] = ClrIndex;
}
}
// 把颜色数据替换成颜色表里的索引
BYTE * pRetIndex = new BYTE[tInfoHeader.biWidth * tInfoHeader.biHeight];
if (pRetIndex == NULL)
{
return FALSE;
}
int iOffset = 0;
for(int y = 0; y < tInfoHeader.biHeight;y++)
{
lpPtr = pBuffer + y * DstLineBytes;
for(int x = 0; x < tInfoHeader.biWidth; x++)
{
//R,G,B各取位
Blue=(int)(*(lpPtr++) & 0xf0);
Green=(int)(*(lpPtr++) & 0xf0);
Red=(int)(*(lpPtr++) & 0xf0);
//拼成一个位整数
ClrIndex=(Blue<<4) + Green +(Red >>4);
for (i = 0; i < PalCounts; i++)
{
if (ClrIndex == ColorIndex[i])
{
//根据索引值取得对应的调色板中的索引值
pRetIndex[iOffset++] = (unsigned char)ColorHits[i];
break;
}
}
}
}
//产生新的位图
// hf=_lcreat("c://256.bmp",0);
FILE * pFile = _tfopen(_T("c://256.bmp"), _T("w+b"));
if (pFile == NULL)
{
return FALSE;
}
BITMAPFILEHEADER tFileHead; //文件头
BITMAPINFO Info;
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = iScreenX;
Info.bmiHeader.biHeight = iScreenY;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = 8; // 256色位图是位,即一个颜色点占个字节
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = 0;
Info.bmiHeader.biXPelsPerMeter = 0;
Info.bmiHeader.biYPelsPerMeter = 0;
Info.bmiHeader.biClrUsed = 0;
Info.bmiHeader.biClrImportant = 0;
tFileHead.bfType = (WORD)('M'<<8)|'B'; //位图文件头位“BM”
tFileHead.bfSize = sizeof(BITMAPFILEHEADER) + DstBufSize; // BMP文件的字节大小
tFileHead.bfReserved1 = 0;
tFileHead.bfReserved2 = 0;
tFileHead.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
fwrite((LPSTR)&tFileHead,sizeof(BITMAPFILEHEADER), 1, pFile); // 写文件头
fwrite((LPSTR)&Info.bmiHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
fwrite(&tRGBQTmp, sizeof(RGBQUAD) * 256, 1, pFile);
fwrite((LPSTR)pRetIndex, tInfoHeader.biWidth * tInfoHeader.biHeight, 1, pFile); // 写索引数据
fclose(pFile);
//释放内存和资源
delete []pBuffer;
delete []pRetIndex;
SelectObject(hMemDC, hOldBmp);
::DeleteObject(hMemBmp);
::DeleteDC(hMemDC);
}
相关文章推荐
- DIB(设备无关位图)编程系列(二)―24位真彩图转256阶(8位)灰度图
- 24位真彩色位图转换成8位灰度图片的代码实现
- 24位真彩色位图转换成8位灰度图片的代码实现
- DIB(设备无关位图)编程系列(二)——24位真彩图转256阶(8位)灰度图
- 24位真彩色位图和8位灰度位图相互转换(C语言实现)
- MFC的View区显示OpenCV的Mat数据结构图像,24位彩色或者8位灰度图像
- 【数字图像】C++8位和24位BMP位图的平滑、锐化、二值化处理,以及24位真彩图的灰度化
- C语言创建24位真彩色位图
- 将24位及32位图像数据保存到位图中
- 24位位图转8位灰度图
- 24位位图转化为8位位图 位图头部信息BITMAPFILEHEADER BITMAPINFOHEADER修改代码
- 将24位位图转换8位位图
- 24位真色位图转化为8位灰度位图
- 24位位图转8位灰度图
- 24位和8位BMP图片保存纯C代码
- 256色转灰度+24位真彩色转灰度
- 24位真彩色图像转8位灰度图像
- bmp24位彩色图像转8位灰度图像(纯C++)
- 为什么24位位图(真彩色)的biSizeImage不等于(biWidth*biBitCount+31)/32*4*biHeight?
- 完整24位真彩色位图灰度化源代码