音影片入門-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!好像哪裡不對勁?!我們的彩虹倒過來了!
彩虹的顏色,從上到下應該是:
紅 -> 橙 -> 黃 -> 綠 -> 青 -> 藍 -> 紫
這張圖是:
紫 -> 藍 -> 青 -> 綠 -> 黃 -> 橙 -> 紅

處理圖片倒立問題
BitmapInfoHeader 中的 biHeight 欄位,
biHeight 為正,點陣圖自底向頂掃描,
biHeight 為負,點陣圖自頂向底掃描。
如果這個值的設置和原始點陣圖文件掃描方式不符,則影像顯示可能會顛倒。
將上面的轉換程式碼中,BitmapInfoHeader 部分:
// infoHeader.biHeight = height; infoHeader.biHeight = -height; 
Congratulations!
成功用像素點拼出了一張 「真正」 的圖片!
程式碼:
參考資料:
non-dword-aligned-pixel-to-dword-aligned-bitmap
generate-bmp-file-from-array-of-rgb-values
內容有誤?聯繫作者:
