C++实现24位位图的灰度化

来源:互联网 发布:炫踪网络 ceo 编辑:程序博客网 时间:2024/06/05 16:00
  位图(BMP)文件主要包括文件头,图像信息头,调色板和图像数据等四个部分。24位位图由于是RGB真彩色,所以没有调色板;灰度图是8位位图,因此必须要有调色板。
  关于BMP文件的结构,更详细的内容可以参考:bmp文件格式详解

  将24位真彩BMP图像灰度化的代码如下:

#include "stdafx.h" #include <iostream>#include <Windows.h> using namespace std;  void main(){ FILE* stream=fopen("Source.bmp","rb");if(stream==NULL){cout<<"文件不存在"<<endl;return;}  int sizeFileHeader=sizeof(BITMAPFILEHEADER);int sizeInfoHeader=sizeof(BITMAPINFOHEADER);  BITMAPFILEHEADER* bitmapFileHeader=new BITMAPFILEHEADER[sizeFileHeader+1];BITMAPINFOHEADER* bitmapInfoHeader=new BITMAPINFOHEADER[sizeInfoHeader+1];  memset(bitmapFileHeader,0,sizeFileHeader+1);memset(bitmapInfoHeader,0,sizeInfoHeader+1);fread(bitmapFileHeader,sizeof(char),sizeFileHeader,stream);fseek(stream,sizeFileHeader,0);fread(bitmapInfoHeader,sizeof(char),sizeInfoHeader,stream);int srcImageLineByteCount=(((bitmapInfoHeader->biWidth*24)+31)/32)*4;int destImageLineByteCount=(((bitmapInfoHeader->biWidth)*8+31)/32)*4; //************位图信息头**********************  BYTE** oldImageData=new BYTE*[bitmapInfoHeader->biHeight];for(int i=0;i<bitmapInfoHeader->biHeight;i++){oldImageData[i]=new BYTE[srcImageLineByteCount+1];memset(oldImageData[i],0,srcImageLineByteCount+1);} //***********位图数据***********************fseek(stream,sizeFileHeader+sizeInfoHeader,0);//读取图像数据for(i=0;i<bitmapInfoHeader->biHeight;i++){for (int j=0;j<srcImageLineByteCount;j++){fread(&oldImageData[i][j],sizeof(BYTE),1,stream); }   } fclose(stream);  //调色板RGBQUAD* pRgbQuards=new RGBQUAD[256];for(i=0;i<256;i++){pRgbQuards[i].rgbBlue=i;pRgbQuards[i].rgbRed=i;pRgbQuards[i].rgbGreen=i; }  //修改信息头bitmapInfoHeader->biBitCount=8;bitmapInfoHeader->biSizeImage=(bitmapInfoHeader->biHeight)*destImageLineByteCount;bitmapInfoHeader->biClrUsed=256; // 调色板中使用的颜色索引数 //修改文件头bitmapFileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;bitmapFileHeader->bfSize=bitmapFileHeader->bfOffBits+bitmapInfoHeader->biSizeImage;   //写数据  BYTE** newImageData=new BYTE*[bitmapInfoHeader->biHeight]; for (i=0;i<bitmapInfoHeader->biHeight;i++){newImageData[i]=new BYTE[destImageLineByteCount];} for(i=0;i<bitmapInfoHeader->biHeight;i++){for(int j=0;j<destImageLineByteCount;j++){newImageData[i][j]=(int)((float)oldImageData[i][j*3]*0.114+(float)oldImageData[i][j*3+1]*0.587+(float)oldImageData[i][3*j+2]*0.299);}}//写入文件[cpp]FILE* fileWrite=fopen("Sink.bmp","ab");fwrite(bitmapFileHeader,sizeof(char),sizeof(BITMAPFILEHEADER),fileWrite);fwrite(bitmapInfoHeader,sizeof(char),sizeof(BITMAPINFOHEADER),fileWrite);fwrite(pRgbQuards,sizeof(RGBQUAD),256,fileWrite);  for(i=0;i<bitmapInfoHeader->biHeight;i++){for(int j=0;j<destImageLineByteCount;j++){fwrite(&newImageData[i][j],sizeof(BYTE),1,fileWrite);} }fclose(fileWrite);cout<<"success"<<endl;return;}


  程序中主要需要注意的部分:

1. 以原图640*480图像为例,在VC6.0中建立了一个Win32 Console Application的工程。

2. 修改信息头:

  信息头共11个部分,这里需要修改3部分:

bitmapInfoHeader->biBitCount=8; // 将每个像素的数据位宽从24改为8

bitmapInfoHeader->biSizeImage=(bitmapInfoHeader->biHeight)*destImageLineByteCount; // 图像大小改为640*480字节

bitmapInfoHeader->biClrUsed=256; // 将调色板中使用的颜色索引数改为256

3. 修改文件头:

  文件头共5个部分,灰度化修改两个部分:

bitmapFileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256; // 图像数据相对文件开头位置的偏移量

bitmapFileHeader->bfSize=bitmapFileHeader->bfOffBits+bitmapInfoHeader->biSizeImage; // 文件大小,字节数

4. 创建调色板:

RGBQUAD *ipRGB2 = (RGBQUAD*)malloc(256*sizeof(RGBQUAD));
for ( i = 0; i < 256; i++ )
ipRGB2[i].rgbRed = ipRGB2[i].rgbGreen = ipRGB2[i].rgbBlue = i;

Roman";mso-bidi-theme-font:minor-bidi;color:#333333;mso-ansi-language:EN-US;mso-fareast-language:ZH-CN;mso-bidi-language:AR-SA'>;

5. 修改位图数据

这部分主要是由原真彩图的rgbRedrgbGreenrgbBlue分量值得到灰度图像的灰度值Y可以用下面公式得到:

Y=0.299*rgbRed+0.587* rgbGreen+0.114*rgbBlue

6. 按顺序写入BMP图像的各个部分

fwrite(bitmapFileHeader,sizeof(char),sizeof(BITMAPFILEHEADER),fileWrite);
fwrite(bitmapInfoHeader,sizeof(char),sizeof(BITMAPINFOHEADER),fileWrite);
fwrite(pRgbQuards,sizeof(RGBQUAD),256,fileWrite);
  
for(i=0;i<bitmapInfoHeader->biHeight;i++)
{
for(int j=0;j<destImageLineByteCount;j++)
{
fwrite(&newImageData[i][j],sizeof(BYTE),1,fileWrite);
}
 
}

  注意:向文件中写入数据时,打开文件的函数fopen使用了参数“ab”,即必须用二进制格式写入。如果按照文本方式将数据流写入文件,数据0x0A将会被自动扩展为ox0D0A。这是因为0x0D是回车,0x0A是换行,文本方式写入时windows会自动转换,这样反而破坏了bmp文件格式。文件读取时,同样也需要用二进制格式。

  另外,上面的程序没有释放在堆上分配的内存指针,需要注意。

原图是这样滴:


变成灰度图是这样滴:


效果还不错吧!

0 0
原创粉丝点击