BMP 是最簡單的位圖格式,無壓縮、結構固定,適合學習圖像處理基礎。本文從文件頭結構、內存對齊、灰度加權公式、全局閾值二值化、行填充處理五個維度,給出完整可編譯運行的 C 實現代碼。
// 1. 文件頭 (14 字節)
#pragma pack(1)
typedef struct {
unsigned char bfType[2]; // "BM"
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 0
unsigned short bfReserved2; // 0
unsigned int bfOffBits; // 數據偏移
} BITMAPFILEHEADER;
#pragma pack()
// 2. 信息頭 (40 字節, BITMAPINFOHEADER)
#pragma pack(1)
typedef struct {
unsigned int biSize; // 40
int biWidth; // 寬(像素)
int biHeight; // 高(像素),負值為正序
unsigned short biPlanes; // 1
unsigned short biBitCount; // 位深:1/4/8/16/24/32
unsigned int biCompression; // 0=BI_RGB 無壓縮
unsigned int biSizeImage; // 圖像數據大小
int biXPelsPerMeter; // 水平分辨率
int biYPelsPerMeter; // 垂直分辨率
unsigned int biClrUsed; // 調色板顏色數
unsigned int biClrImportant; // 重要顏色數
} BITMAPINFOHEADER;
#pragma pack()
// 3. 調色板 (僅索引色圖像)
typedef struct {
unsigned char rgbBlue, rgbGreen, rgbRed, rgbReserved;
} RGBQUAD;
#pragma pack(1) 強制 1 字節對齊,確保讀取文件頭字節偏移正確。((width * bitCount/8) + 3) / 4 * 4。不足補 0。讀寫必須處理填充字節。人眼對綠最敏感,標準公式:Gray = 0.299*R + 0.587*G + 0.114*B。BMP 存儲順序為 BGR,代碼中對應調整。
閾值 T=190(經驗值),gray >= T → 1(白),else 0(黑)。進階方法:Otsu 自適應閾值、自適應局部閾值。
int main() {
FILE *fpSrc = fopen("1.bmp", "rb"), *fpDst = fopen("2.bmp", "wb");
BITMAPFILEHEADER fh; BITMAPINFOHEADER fi; RGBQUAD rg[2];
fread(&fh, sizeof(fh), 1, fpSrc);
fread(&fi, sizeof(fi), 1, fpSrc);
// 修改頭信息為 8 位索引色(二值)
fi.biBitCount = 8;
fi.biSizeImage = ((fi.biWidth + 3) / 4 * 4) * abs(fi.biHeight);
fi.biClrUsed = 2; fi.biClrImportant = 0;
fh.bfOffBits = sizeof(fh) + sizeof(fi) + 2*sizeof(RGBQUAD);
fh.bfSize = fh.bfOffBits + fi.biSizeImage;
// 調色板:索引 0=黑,1=白
rg[0] = (RGBQUAD){0,0,0,0}; rg[1] = (RGBQUAD){255,255,255,0};
fwrite(&fh, sizeof(fh), 1, fpDst);
fwrite(&fi, sizeof(fi), 1, fpDst);
fwrite(rg, sizeof(rg), 1, fpDst);
// 行緩衝區
int srcRowBytes = ((fi.biWidth * 3 + 3) / 4 * 4);
int dstRowBytes = ((fi.biWidth + 3) / 4 * 4);
unsigned char *srcRow = malloc(srcRowBytes);
unsigned char *dstRow = malloc(dstRowBytes);
for (int y = 0; y < abs(fi.biHeight); y++) {
fread(srcRow, srcRowBytes, 1, fpSrc);
for (int x = 0, k = 0; x < fi.biWidth; x++, k += 3) {
int gray = (int)(0.114*srcRow[k] + 0.587*srcRow[k+1] + 0.299*srcRow[k+2]);
dstRow[x] = (gray >= 190) ? 1 : 0;
}
fwrite(dstRow, dstRowBytes, 1, fpDst);
}
free(srcRow); free(dstRow);
fclose(fpSrc); fclose(fpDst);
printf("轉換成功!\n");
return 0;
}
原圖:
轉換後二值圖:
biCompression=1/2)。stb_image.h 支持 PNG/JPG 輸入。