1. 程式人生 > >8位灰度影象BMP的儲存

8位灰度影象BMP的儲存

在影象處理中,我們經常需要將真彩色影象轉換為黑白影象。嚴格的講應該是灰度圖,因為真正的黑白影象是二色,即只有純黑,純白二色。開始之前,我們先簡單補充一下計算機中影象的表示原理。計算機中的影象大致可以分成兩類:點陣圖(Bitmap)和向量圖(Metafile)。 點陣圖可以視為一個二維的網格,整個影象就是由很多個點組成的,點的個數等於點陣圖的寬乘以高。每個點被稱為一個畫素點,每個畫素點有確定的顏色,當很多個像 素合在一起時就形成了一幅完整的影象。我們通常使用的影象大部分都是點陣圖,如數碼相機拍攝的照片,都是點陣圖。因為點陣圖可以完美的表示影象的細節,能較好的 還原影象的原景。但點陣圖也有缺點:第一是體積比較大,所以人們開發了很多壓縮影象格式來儲存點陣圖影象,目前應用最廣的是JPEG

格式,在WEB上得到了廣泛應用,另外還有GIF,PNG等 等。第二是點陣圖在放大時,不可避免的會出現“鋸齒”現象,這也由點陣圖的本質特點決定的。所以在現實中,我們還需要使用到另一種影象格式:向量圖。同位圖不 同,向量圖同位圖的原理不同,向量圖是利用數學公式通過圓,線段等繪製出來的,所以不管如何放大都不會出現變形,但向量圖不能描述非常複雜的影象。所以矢 量圖都是用來描述圖形圖案,各種CAD軟體等等都是使用向量格式來儲存檔案的。

  在講解顏色轉換之前,我們要先對點陣圖的顏色表示方式做一瞭解。點陣圖中通常是用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

表示方式中,亮度也是直接可以從顏色分量中得到的,每一顏色分量值的範圍都是從0255, 某一顏色分量的值越大,就表示這一分量的亮度值越高,所以255表示最亮,0表示最暗。那麼一個真彩色畫素點轉換為灰度圖時它的亮度值應該是多少呢,首先我們想到的平均值,即將R+G+B/3。但現實中我們使用的卻是如下的公式:

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();