8位灰度影象BMP的儲存
在影象處理中,我們經常需要將真彩色影象轉換為黑白影象。嚴格的講應該是灰度圖,因為真正的黑白影象是二色,即只有純黑,純白二色。開始之前,我們先簡單補充一下計算機中影象的表示原理。計算機中的影象大致可以分成兩類:點陣圖(Bitmap)和向量圖(Metafile)。 點陣圖可以視為一個二維的網格,整個影象就是由很多個點組成的,點的個數等於點陣圖的寬乘以高。每個點被稱為一個畫素點,每個畫素點有確定的顏色,當很多個像 素合在一起時就形成了一幅完整的影象。我們通常使用的影象大部分都是點陣圖,如數碼相機拍攝的照片,都是點陣圖。因為點陣圖可以完美的表示影象的細節,能較好的 還原影象的原景。但點陣圖也有缺點:第一是體積比較大,所以人們開發了很多壓縮影象格式來儲存點陣圖影象,目前應用最廣的是JPEG
在講解顏色轉換之前,我們要先對點陣圖的顏色表示方式做一瞭解。點陣圖中通常是用RGB三色方式來表示顏色的(位數很少時要使用調色盤) 。所以每個畫素採用不同的位數,就可以表示出不同數量的顏色。如下圖所示:
每畫素的位數 |
一個畫素可分配到的顏色數量 |
1 |
2^1 = 2 |
2 |
2^2 = 4 |
4 |
2^4 = 16 |
8 |
2^8 = 256 |
16 |
2^16 = 65,536 |
24 |
2^24 = 16,777,216 |
從中我們可以看出,當使用24位色(3個位元組)時,我們可以得到1600多萬種顏色,這已經非常豐富了,應該已接近人眼所能分辨的顏色了。現在計算機中使用最多的就是24位色,別外在GDI+中還有一種32位色,多出來的一個通道用來描述Alpha,即透明分量。
24位色中3個位元組分別用來描述R,G,B三種顏色分量,我們看到這其中是沒有亮度分量的,這是因為在RGB
Y = 0.299R+0.587G+0.114B
這個公式通常都被稱為心理學灰度公式。這裡面我們看到綠色分量所佔比重最大。因為科學家發現使用上述公式進行轉換時所得到的灰度圖最接近人眼對灰度圖的感覺。
因為灰度圖中顏色數量一共只有256種(1個位元組),所以轉換後的影象我們通常儲存為8位格式而不是24位格式,這樣比較節省空間。而8點陣圖像是使用調色盤方式來儲存顏色的。而不是直接儲存顏色值。調色盤中可以儲存256顏色,所以可以正好可以將256種灰度顏色儲存到調色版中。下面是個私人例子:
BITMAPFILEHEADER targetfileheader;
BITMAPINFOHEADER targetinfoheader;
memset(&targetfileheader,0,sizeof(BITMAPFILEHEADER));
memset(&targetinfoheader,0,sizeof(BITMAPINFOHEADER));
//構造灰度圖的檔案頭
targetfileheader.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
targetfileheader.bfSize=192*192+sizeof(RGBQUAD)*256 +sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
targetfileheader.bfReserved1=0;
targetfileheader.bfReserved2=0;
targetfileheader.bfType=0x4d42;
//構造灰度圖的資訊頭
targetinfoheader.biBitCount=8;
targetinfoheader.biSize=sizeof(BITMAPINFOHEADER);
targetinfoheader.biHeight=192;
targetinfoheader.biWidth=192;
targetinfoheader.biPlanes=1;
targetinfoheader.biCompression=BI_RGB;
targetinfoheader.biSizeImage=0;
targetinfoheader.biXPelsPerMeter=0;
targetinfoheader.biYPelsPerMeter=0;
targetinfoheader.biClrImportant=0;
targetinfoheader.biClrUsed=0;
//構造灰度圖的調色版
RGBQUAD rgbquad[256];
int i;
for(i=0;i<256;i++)
{
rgbquad[i].rgbBlue=i;
rgbquad[i].rgbGreen=i;
rgbquad[i].rgbRed=i;
rgbquad[i].rgbReserved=0;
}
BYTE* targetbuf;
targetbuf=new BYTE[192*192];
//由於BMP影象對於行是倒置的,即影象顯示的第一行是最後一行資料,所以要倒置,這裡的pcutface已經////是灰度影象了
for(long i=191;i>=0;i--)
{
for(long j=0;j<192;j++)
{
targetbuf[i*192+j]=pcutface[(191-i)*192+j];
}
}
CFile cf;
if(!cf.Open(LPCTSTR("f://fire.BMP"),CFile::modeCreate|CFile::modeWrite))
return ;
cf.Write(&targetfileheader,sizeof(BITMAPFILEHEADER));
cf.Write(&targetinfoheader,sizeof(BITMAPINFOHEADER));
cf.Write(&rgbquad,sizeof(RGBQUAD)*256);
cf.Write(targetbuf,192*192); //這裡targetbuf的大小為192x192
cf.Close();