1. 程式人生 > >BMP影象的灰度化---C++實現

BMP影象的灰度化---C++實現

灰度圖的結構主要包括檔案頭,BMP資訊頭,調色盤,BMP資料內容四部分。灰度圖的調色盤共有256項RGBQUAD結構,存放0到255的灰度值,每一項rgbRed、rgbGreen、rgbBlue分量值相等。


24位真彩BMP影象的灰度化
      把24位真彩BMP影象轉變成256階灰度圖的具體步驟如下:
(1) 修改資訊頭
       資訊頭共有11部分,灰度化時需要修改兩部分
bi2.biBitCount=8;
bi2.biSizeImage=( (bi.biWidth+3)/4 ) * 4*bi.biHeight;

(2)修改檔案頭
       檔案頭共有5部分,灰度化時需要修改兩部分
          bf2.bfOffBits = sizeof(bf2)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
bf2.bfSize = bf2.bfOffBits + bi2.biSizeImage;


(3)建立調色盤
RGBQUAD *ipRGB2 = (RGBQUAD *)malloc(256*sizeof(RGBQUAD));
for ( i = 0; i < 256; i++ )
ipRGB2[i].rgbRed = ipRGB2[i].rgbGreen = ipRGB2[i].rgbBlue = i;


(4)修改點陣圖資料部分
         這部分主要是由原真彩圖的rgbRed、rgbGreen、rgbBlue分量值得到灰度影象的灰度值Y,

可以用下面公式得到:
            Y=0.299*rgbRed+0.587* rgbGreen+0.114*rgbBlue;
具體修改程式碼如下:
int nBytesPerLine2 = ( (bi.biWidth+3)/4 ) * 4;
nLineStart2 = nBytesPerLine2 * i;
for ( int j = 0; j<nBytesPerLine2;j++ )
ImgData2[nLineStart2+j]= int( (float)Imgdata[i][3 * j] * 0.114 + \
(float)Imgdata[i][3 * j + 1] * 0.587 + \
(float)Imgdata[i][3 * j + 2] * 0.299 );//用一個一維陣列順序儲存灰度值


(5)按順序寫入BMP影象的各個部分
          fwrite(&bf2,sizeof(BITMAPFILEHEADER),1,fp); 
fwrite(&bi2,sizeof(BITMAPINFOHEADER),1,fp);
fwrite(ipRGB2,sizeof(RGBQUAD),256,fp);
fwrite(ImgData2,nImageSize2,1,fp);

#include<iostream>
#include <Windows.h>

using namespace std;


void main()
{
	
	FILE* stream=fopen("D:\\3.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(int 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(int 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;

	//修改檔案頭
	bitmapFileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
	bitmapFileHeader->bfSize=bitmapFileHeader->bfOffBits+bitmapInfoHeader->biSizeImage;
	

	//寫資料
	
	BYTE** newImageData=new BYTE*[bitmapInfoHeader->biHeight];

	for (int i=0;i<bitmapInfoHeader->biHeight;i++)
	{
		newImageData[i]=new BYTE[destImageLineByteCount];
	}

	for(int 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);
		}
	}


	//寫入檔案
	FILE* fileWrite=fopen("D:\\6.bmp","a+");
	fwrite(bitmapFileHeader,sizeof(char),sizeof(BITMAPFILEHEADER),fileWrite);
	fwrite(bitmapInfoHeader,sizeof(char),sizeof(BITMAPINFOHEADER),fileWrite);
	fwrite(pRgbQuards,sizeof(RGBQUAD),256,fileWrite);
	
	for(int 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;
}

 int srcImageLineByteCount=(((bitmapInfoHeader->biWidth*24)+31)/32)*4;
 int destImageLineByteCount=(((bitmapInfoHeader->biWidth)*8+31)/32)*4;

提醒:這裡沒有進行指標的釋放。。。

這兩行其實也可以用上一篇文章的WIDTHBYTES(bitmapInfoHeader->biWidth*24)和WIDTHBYTES(bitmapInfoHeader->biWidth*8)

有些地方也用( (bi.biWidth+3)/4 ) * 4和((bi.biWidth*3+3)/4)*4這樣的表示式。。原理都是一樣的。其實( (bi.biWidth+3)/4 ) * 4寫成( (bi.biWidth*1+3)/4 ) * 4估計會好理解吧。。

因為BMP影象每個畫素都是有三個RGB分量組成(24位,32位也就是多了個Alpha),而在灰度影象中每個畫素只是一個灰度值,從程式碼: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); 可看出,每個灰度值都是由原來的彩色影象的每個RGB分量通過一定的公式計算得來的。因此灰度影象和原來的彩色影象雖然在寬度和高度(畫素單位)是一樣的,但是因為組成不同,所以每行的位元組數就是不一樣的。。

效果如下:

原影象:

灰度影象: