音影片入門-04-BMP影像四位元組對齊的問題
- 2019 年 10 月 3 日
- 筆記
BMP 影像四位元組對齊
表示 BMP 點陣圖中像素的位元是以行為單位對齊存儲的,每一行的大小都向上取整為4位元組(32 位 DWORD)的倍數。如果影像的高度大於 1,多個經過填充實現對齊的行就形成了像素數組。
完整存儲的一行像素所需的位元組數可以通過這個公式計算:
-
每一行的末尾通過填充若干個位元組的數據(並不一定為 0)使該行的長度為 4 位元組的倍數。像素數組讀入記憶體後,每一行的起始地址必須為 4 的倍數。這個限制僅針對記憶體中的像素數組,針對存儲時,僅要求每一行的大小為 4 位元組的倍數,對文件的偏移沒有限制。
-
例如:對於 24 位色的點陣圖,如果它的寬度為 1 像素,那麼除了每一行的數據(藍、綠、紅)需要佔 3 位元組外,還會填充 1 位元組;而如果寬為 2 像素,則需要 2 位元組的填充;寬為 3 像素時,需要 3 位元組填充;寬為 4 像素時則不需要填充。
-
影像相同的條件下,點陣圖影像文件通常比使用其它壓縮演算法的影像文件大很多。
四位元組對齊問題-發現
沒有四位元組對齊的 700×700 BMP 文件
之前,用 RGB 拼出一張 700×700 的彩虹圖片,並成功保存成 BMP 文件。
當時並沒有進行四位元組對齊,保存成的 BMP 文件也沒有問題。
原因:
根據四位元組對齊的要求,700 x 3 = 2100,2100 / 4 = 525,行像素數據已經四位元組對齊了。
如果沒有對齊會怎麼樣
將圖片尺寸改成 711×711:
#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/hubin/Desktop/rainbow-711x711.bmp", 711, 711); return 0; }
彩虹圖片已經無法顯示出來了:
四位元組對齊問題-解決
計算一行像素,四位元組對齊後的位元組數
// 計算每一行像素 4 位元組對齊後的位元組數 int caculateLineBytes(int width) { //******* 四位元組對齊 ******* return (24 * width + 31)/32 *4; //******* 四位元組對齊 ******* }
寫入一行像素的數據
// 計算一行像素四位元組對齊所需位元組數 int lineBytes = caculateLineBytes(width); for (int i = 0; i < width; ++i) { u_int32_t currentColor = rainbowColors[i]; u_int8_t R = (currentColor & 0xFF0000) >> 16; u_int8_t G = (currentColor & 0x00FF00) >> 8; u_int8_t B = currentColor & 0x0000FF; // 存儲一行像素數據的數組 u_int8_t lineBytesArray[lineBytes]; for (int j = 0; j < height; ++j) { int currentIndex = 3*j; lineBytesArray[currentIndex] = B; lineBytesArray[currentIndex+1] = G; lineBytesArray[currentIndex+2] = R; } // 將四位元組對齊後的一行像素數據寫入文件 fwrite(lineBytesArray, sizeof(lineBytesArray), 1, file); }
完整的程式碼
#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; // 計算每一行像素 4 位元組對齊後的位元組數 int caculateLineBytes(int width) { //******* 四位元組對齊 ******* return (24 * width + 31)/32 *4; //******* 四位元組對齊 ******* } 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; int lineBytes = caculateLineBytes(width); BitmapFileHeader fileHeader; fileHeader.bfReserved1 = 0; fileHeader.bfReserved2 = 0; fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + lineBytes*height; 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; u_int8_t lineBytesArray[lineBytes]; for (int j = 0; j < height; ++j) { int currentIndex = 3*j; lineBytesArray[currentIndex] = B; lineBytesArray[currentIndex+1] = G; lineBytesArray[currentIndex+2] = R; } fwrite(lineBytesArray, sizeof(lineBytesArray), 1, bitmapFile); } fclose(bitmapFile); } int main() { writeRGBToBmp("/Users/staff/Desktop/rainbow-711x711-fix.bmp", 711, 711); return 0; }
711×711 的彩虹圖片也顯示出來了:
程式碼:
參考資料:
non-dword-aligned-pixel-to-dword-aligned-bitmap
generate-bmp-file-from-array-of-rgb-values
內容有誤?聯繫作者: