音影片入門-03-RGB轉成BMP圖片

  • 2019 年 10 月 3 日
  • 筆記

* 音影片入門文章目錄 *

BMP 文件格式解析

BMP 文件由文件頭、點陣圖資訊頭、顏色資訊和圖形數據四部分組成。

點陣圖文件頭(14個位元組) 點陣圖資訊頭(40個位元組) 顏色資訊 圖形數據
  • 文件頭與資訊頭一共是 54 位元組
  • RGB 數據部分:
    RGB24 文件存儲的順序是 RGB, RGB, RGB ...... RGB
    BMP 文件 RGB 數據存儲的順序是 BGR, BGR, BGR ... BGR

點陣圖文件頭

點陣圖文件頭分 4 部分,共 14 位元組

名稱 佔用空間 內容 示例數據
bfType 2位元組 標識,就是「BM」 BM
bfSize 4位元組 整個 BMP 文件的大小 0x000C0036(786486)
bfReserved1 2位元組 保留字 0
bfReserved2 2位元組 保留字 0
bfOffBits 4位元組 偏移數,即點陣圖文件頭+點陣圖資訊頭+調色板的大小 0x36(54)

點陣圖資訊頭

點陣圖資訊頭共 40 位元組

名稱 佔用空間 內容 示例數據
biSize 4位元組 點陣圖資訊頭的大小,為40 0x28(40)
biWidth 4位元組 點陣圖的寬度,單位是像素 0x200(512)
biHeight 4位元組 點陣圖的高度,單位是像素 0x200(512)
biPlanes 2位元組 固定值1 1
biBitCount 2位元組 每個像素的位數 1-黑白圖,4-16色,8-256色,24-真彩色,32-帶 alpha 通道 0x18(24)
biCompression 4位元組 壓縮方式,BI_RGB(0)為不壓縮 0
biSizeImage 4位元組 點陣圖全部像素佔用的位元組數,BI_RGB時可設為0 0x0C
biXPelsPerMeter 4位元組 水平解析度(像素/米) 0
biYPelsPerMeter 4位元組 垂直解析度(像素/米) 0
biClrUsed 4位元組 點陣圖使用的顏色數 如果為0,則顏色數為2的biBitCount次方 0
biClrImportant 4位元組 重要的顏色數,0代表所有顏色都重要 0

將 RGB24 像素點數據轉成 BMP 格式圖片

轉換程式碼:

#include <stdio.h>  #include <stdlib.h>    // 彩虹的七種顏色  u_int32_t rainbowColors[] = {          0XFF0000, // 紅          0XFFA500, // 橙          0XFFFF00, // 黃          0X00FF00, // 綠          0X007FFF, // 青          0X0000FF, // 藍          0X8B00FF  // 紫  };    /*bmp file header*/  typedef struct {      unsigned int   bfSize;           /* Size of file */      unsigned short bfReserved1;      /* Reserved */      unsigned short bfReserved2;      /* ... */      unsigned int   bfOffBits;        /* Offset to bitmap data */  } BitmapFileHeader;    /*bmp info header*/  typedef struct {      unsigned int   biSize; /* Size of info header */      int            biWidth; /* Width of image */      int            biHeight; /* Height of image */      unsigned short biPlanes; /* Number of color planes */      unsigned short biBitCount; /* Number of bits per pixel */      unsigned int   biCompression; /* Type of compression to use */      unsigned int   biSizeImage; /* Size of image data */      int            biXPelsPerMeter; /* X pixels per meter */      int            biYPelsPerMeter; /* Y pixels per meter */      unsigned int   biClrUsed; /* Number of colors used */      unsigned int   biClrImportant; /* Number of important colors */  } BitmapInfoHeader;    void writeRGBToBmp(char *filename, int width, int height) {      FILE *bitmapFile = fopen(filename, "wb");      if(!bitmapFile) {          printf("Could not write file n");          return;      }        uint16_t bfType = 0x4d42;        BitmapFileHeader fileHeader;      fileHeader.bfReserved1 = 0;      fileHeader.bfReserved2 = 0;      fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + width*height*3;      fileHeader.bfOffBits = 0x36;        BitmapInfoHeader infoHeader;      infoHeader.biSize = sizeof(BitmapInfoHeader);      infoHeader.biWidth = width;      infoHeader.biHeight = height;      infoHeader.biPlanes = 1;      infoHeader.biBitCount = 24;      infoHeader.biSizeImage = 0;      infoHeader.biCompression = 0;      infoHeader.biXPelsPerMeter = 5000;      infoHeader.biYPelsPerMeter = 5000;      infoHeader.biClrUsed = 0;      infoHeader.biClrImportant = 0;        fwrite(&bfType, sizeof(bfType), 1, bitmapFile);      fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);      fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);        // 寫入影像數據      for (int i = 0; i < width; ++i) {            // 當前顏色          u_int32_t currentColor = rainbowColors[0];          if(i < 100) {              currentColor = rainbowColors[0];          } else if(i < 200) {              currentColor = rainbowColors[1];          } else if(i < 300) {              currentColor = rainbowColors[2];          } else if(i < 400) {              currentColor = rainbowColors[3];          } else if(i < 500) {              currentColor = rainbowColors[4];          } else if(i < 600) {              currentColor = rainbowColors[5];          } else if(i < 700) {              currentColor = rainbowColors[6];          }          // 當前顏色 R 分量          u_int8_t R = (currentColor & 0xFF0000) >> 16;          // 當前顏色 G 分量          u_int8_t G = (currentColor & 0x00FF00) >> 8;          // 當前顏色 B 分量          u_int8_t B = currentColor & 0x0000FF;            for (int j = 0; j < height; ++j) {              // 按 BGR 順序寫入一個像素 RGB24 到文件中              fwrite(&B, 1, 1, bitmapFile);              fwrite(&G, 1, 1, bitmapFile);              fwrite(&R, 1, 1, bitmapFile);          }      }        // 關閉文件      fclose(bitmapFile);  }    int main() {      writeRGBToBmp("/Users/staff/Desktop/rainbow-700x700.bmp", 700, 700);      return 0;  }

檢查生成的 BMP 圖片

Congratulations! 圖片查看軟體識別出我們的 BMP 圖片了,預覽正常!
BUT!好像哪裡不對勁?!我們的彩虹倒過來了!
彩虹的顏色,從上到下應該是:
紅 -> 橙 -> 黃 -> 綠 -> 青 -> 藍 -> 紫
這張圖是:
紫 -> 藍 -> 青 -> 綠 -> 黃 -> 橙 -> 紅

image-demo-rainbow-bmp-reverse

處理圖片倒立問題

BitmapInfoHeader 中的 biHeight 欄位,
biHeight 為正,點陣圖自底向頂掃描,
biHeight 為負,點陣圖自頂向底掃描。
如果這個值的設置和原始點陣圖文件掃描方式不符,則影像顯示可能會顛倒。


將上面的轉換程式碼中,BitmapInfoHeader 部分:

// infoHeader.biHeight = height;  infoHeader.biHeight = -height;

image-demo-rainbow-bmp

Congratulations!

成功用像素點拼出了一張 「真正」 的圖片!


程式碼:

rgb-to-bmp

參考資料:

維基百科-BMP

點陣圖(bmp)文件格式分析

RGB24轉換成BMP影像

關於RGB32和RGB24的區別

non-dword-aligned-pixel-to-dword-aligned-bitmap

generate-bmp-file-from-array-of-rgb-values

內容有誤?聯繫作者:

联系作者


Tags: