24位真彩位元影像轉4位(16色)灰階圖(BMP)

來源:互聯網
上載者:User

首先要聲明的是,這個4位(16)色圖比較特殊,不是彩色的16色圖,而已一個用4位16色,類比的灰階圖

什麼是灰階圖?

灰階圖是指只含亮度資訊不含彩色資訊的圖象,就像我們平時看到的亮度由暗到明的黑白照片,亮度變化是連續的。因此,要表示
灰階圖,就需要把亮度值進行亮化。通常分成0-255共256個層級,0最暗(全黑),255最亮(全白)。
BMP格式的檔案中並沒有灰階圖這個概念,但是可以很容易的用BMP檔案來表示灰階圖。一般的方法是用256色的調色盤,這個調色盤

每一項的RGB值都是相同的,即從(0,0,0),(1,1,1)一直到(255,255,255),(0,0,0)表示全黑(255,255,255)表示全白

1.BMP位元影像的格式

BMP檔案的結構分為4部分,本文假定讀者都已經瞭解BMP位元影像的格式(幾乎所有教VC的書上多媒體部分都有講,再google一下也很容

易就查得到,這裡主要介紹其中的調色盤,和圖象資料部分。

對於非真彩的位元影像,都有一個調色盤,調色盤的格式如下

typedef struct tagRGBQUAD{

 BYTE rgbBlue;//藍色的分量
 BYTE rgbGreen;//綠色的分量
 BYTE rgbRed;//紅色的分量
 BYTE rgbReserved;//保留值不用管它為0就好

}RGBQUAD;
一般的調色版是一個,由上面的結構體組成的結構體數組,儲存具體的顏色資訊,而位元影像中,圖象資料部分儲存的只是調色盤的下標

。這樣做就可以大大的節省空間的。

例如:
RGBQUAD rgb[2];
rgb[0].rgbBlue = 0;
rgb[0].rgbGreen = 0;
rgb[0].rgbRed = 0;
rgb[0].rgbReserved = 0;
rgb[1].rgbBlue = 255;
rgb[1].rgbGreen = 255;
rgb[1].rgbRed = 255;
rgb[1].rgbReserved = 255;

這個長度為2的RGBQUAD數組就是一個1位2色黑白圖的調色盤,
在位元影像資料部分只需要用1位的長度儲存0表示黑,1表示白就可以了,1位元組可以表示8個像素的資訊,比用3位元組直接表示R,G,B節

省了24倍的儲存空間

(一般來說,BMP檔案的資料是從上到下、從左至右的(參考:精通Visual C++數字圖象處理典型演算法及實現,第二版18頁).注意:最先讀出來的應該是B然後是G然後是R)

而真彩圖則不然,比如24位元影像,那麼他就需要一個數組大小為2的24次方的調色盤,而調色盤的下標也需要3個位元組才儲存,這樣還

不如直接就R,G,B這三個分量來直接表示每一個像素的色值。使用調色盤技術還浪費了一個256*256*256*3位元組大的調色盤空間.

而這裡要用4位表示一個灰階圖,那麼它的調色盤只有16項,每一項的RGB值同通常由256色構成的灰階圖的調色盤一樣的道理

這裡這樣建立這個調色盤

 RGBQUAD pa[16];
 BYTE c;
 for(int i=0;i<16;i++)
 {
  c= i * 17;
  pa[i].rgbRed = c;
  pa[i].rgbGreen = c;
  pa[i].rgbBlue = c;
  pa[i].rgbReserved = 0;
 }

2.轉換演算法

現在的圖象是24位真彩的,表示它的資料部分,3位元組表示一個像素,這三個位元組分別表示RGB。
我們現在要做的是求每一像素點的RGB值的平均值,然後用16色調色盤中最接近這個顏色亮度的值來表示它。
而4位的圖象是1個位元組表示2個像素,在這裡需要特殊注意

具體演算法實現代碼如下,pBuffer是儲存圖象資料的數組

  USHORT R,G,B;

  ////////////////////////////////////////////////////////////////
  // 第一個像素
  B = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  R = pBuffer[dwIndex++];

  int maxcolor = (R+G+B)/3;

  maxcolor /= 17;//計算在16色調色盤中的下標
  

  //第二個像素
  B = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  R = pBuffer[dwIndex++];

  int maxcolor2 = (R+G+B)/3;

  maxcolor2 /= 17;
  
  pNew[dwOldIndex++] = ( maxcolor<<4 )| maxcolor2;//合成一個位元組表示兩個像素

 

3.實現代碼

完整的實現代碼如下
BOOL Convert24To4(LPCTSTR lpszSrcFile, LPCTSTR lpszDestFile)//24->4灰階圖
{
 BITMAPFILEHEADER bmHdr;  // BMP檔案頭
 BITMAPINFOHEADER bmInfo; // BMP檔案資訊 

 HANDLE hFile, hNewFile;
 DWORD dwByteWritten = 0;

 // 開啟源檔案控制代碼
 hFile = CreateFile(lpszSrcFile,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

 if (hFile == INVALID_HANDLE_VALUE)
  return FALSE;

 // 建立新檔案
 hNewFile = CreateFile(lpszDestFile,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
 if (hNewFile == INVALID_HANDLE_VALUE)
 {
  CloseHandle(hFile);
  return FALSE;
 }

 // 讀取源檔案BMP頭和檔案資訊
 ReadFile(hFile, &bmHdr, sizeof(bmHdr), &dwByteWritten, NULL); 
 ReadFile(hFile, &bmInfo, sizeof(bmInfo), &dwByteWritten, NULL);

 TRACE("biSize: %d , biWidth: %d , biHeight: %d , biBitCount: %d , biSizeImage: %d

/n",bmInfo.biSize,bmInfo.biWidth,bmInfo.biHeight,bmInfo.biBitCount,bmInfo.biSizeImage);
 TRACE("biX: %d , biY: %d , biClrUsed: %d , biClrImportant: %d

/n",bmInfo.biXPelsPerMeter,bmInfo.biYPelsPerMeter,bmInfo.biClrUsed,bmInfo.biClrImportant);

 // 只處理24位未壓縮的映像
 if (bmInfo.biBitCount != 24 || bmInfo.biCompression!=0)
 {
  CloseHandle(hNewFile);
  CloseHandle(hFile);
  DeleteFile(lpszDestFile);
  return FALSE;
 }

 // 計算映像資料大小
 DWORD dwOldSize = bmInfo.biSizeImage;
 if(dwOldSize == 0) // 重新計算
 {
  dwOldSize = bmHdr.bfSize - sizeof(bmHdr) - sizeof(bmInfo);
 }

 TRACE("Old Width: %d , Old Height: %d ,Old Size: %d bytes/n",bmInfo.biWidth,bmInfo.biHeight,dwOldSize);

 long wid = bmInfo.biWidth % 4;

 if(wid>0)
 {
  wid = 4 - wid;
 }

 wid += bmInfo.biWidth;

 DWORD dwNewSize;

 dwNewSize = wid * bmInfo.biHeight / 2; //計算轉換後新圖象大小

 TRACE("New Size: %d bytes/n", dwNewSize);
 

 // 讀取未經處理資料
 UCHAR *pBuffer = NULL;
 pBuffer = new UCHAR[dwOldSize]; // 申請未經處理資料空間
 if(pBuffer == NULL)
 {
  CloseHandle(hNewFile);
  CloseHandle(hFile);
  DeleteFile(lpszDestFile);
  return FALSE;
 }
 // 讀取資料
 ReadFile(hFile, pBuffer, dwOldSize, &dwByteWritten, NULL);

 UCHAR *pNew = new UCHAR[dwNewSize];

 UCHAR  color = 0;
 DWORD dwIndex = 0, dwOldIndex = 0;
 while( dwIndex < dwOldSize )//一位元組表示兩個像素
 {
  USHORT R,G,B;

  ////////////////////////////////////////////////////////////////
  // 第一個像素
  B = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  R = pBuffer[dwIndex++];

  int maxcolor = (R+G+B)/3;

  maxcolor /= 17;
  

  //第二個像素
  B = pBuffer[dwIndex++];
  G = pBuffer[dwIndex++];
  R = pBuffer[dwIndex++];

  int maxcolor2 = (R+G+B)/3;

  maxcolor2 /= 17;
  
  pNew[dwOldIndex++] = ( maxcolor<<4 )| maxcolor2;//合成一個位元組表示兩個像素

 }

 ////////////////////////////////////////////////////////////////////////////////
 // 完工, 把結果儲存到新檔案中 

 // 修改屬性
 bmHdr.bfSize = sizeof(bmHdr)+sizeof(bmInfo)+sizeof(RGBQUAD)*16+dwNewSize;
 bmHdr.bfOffBits = bmHdr.bfSize - dwNewSize;
 bmInfo.biBitCount = 4;
 bmInfo.biSizeImage = dwNewSize; 

 // 建立調色盤
 RGBQUAD pa[16];
 UCHAR c;
 for(int i=0;i<16;i++)
 {
  c= i * 17;
  pa[i].rgbRed = c;
  pa[i].rgbGreen = c;
  pa[i].rgbBlue = c;
  pa[i].rgbReserved = 0;
 }

 // BMP頭
 WriteFile(hNewFile, &bmHdr, sizeof(bmHdr), &dwByteWritten, NULL);
 // 檔案資訊頭
 WriteFile(hNewFile, &bmInfo, sizeof(bmInfo), &dwByteWritten, NULL);
 // 調色盤
 WriteFile(hNewFile, pa, sizeof(RGBQUAD)*16, &dwByteWritten, NULL);
 // 檔案資料
 WriteFile(hNewFile, pNew, dwNewSize, &dwByteWritten, NULL);

 delete []pBuffer;
 delete []pNew;

 // 關閉檔案控制代碼
 CloseHandle(hNewFile);
 CloseHandle(hFile);

 return TRUE;
}

4.疑問

既然可以由24位真菜圖轉換為4位灰階圖,那麼一定有一個合適的方法把它轉換成4位彩色圖,而具體的區別就是
調色盤不同(調色盤都要表示哪些顏色),再有最重要的是原來的顏色用現有的16色,哪個表示更合適.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.