BMP格式詳細介紹鏈接:
BMP格式總結:
① 前14字節描述文件類型,大小,數據偏移
② 接着的40字節描述圖片的大小,寬度,高度,位深度,分辨率等等
③ 位深度常見的是1,4,8,24,32。其中1表示只有黑白兩種顏色,4是16色,8是256色,24是RGB形式表示的,32是在24的基礎上加上透明度的RGBA表示。
④ 目前常見的彩色圖片位24位或32位,8位一般爲灰度圖
⑤ 如果是8位圖(256色),40字節的圖片描述後面會有顏色表,描述圖片上256種顏色的RGBA值。灰度圖的第 i 種顏色的RGB值均爲 i ,A值爲0。
⑥由於windows默認的掃描最小單位是4字節,所以BMP格式爲了能夠更快地按行讀取,如果每行的字節數不是4的倍數,會在後面補0直到長度爲4的倍數。可以簡單地得知,32位圖不需要對齊。(對齊規則)
PS:如果用fread整體讀入BMP文件信息和圖片信息,可能由於 內存對齊 的原因,讀入的信息可能存放不到內存裏預期的地方,下面的代碼實現種用#pragma pack(1)解決由於內存對齊產生的問題
簡單的實現代碼:
#include <cstdio>
#include <cstring>
#include <windows.h>
#include <iostream>
using namespace std;
//設置內存對齊
#pragma pack(1)
typedef struct tag_color_32{
BYTE Red;
BYTE Green;
BYTE Blue;
BYTE Alpha;
} color_32;
int main(int argc,char** argv){
FILE *fp,*out; // 定義一個文件指針
BITMAPFILEHEADER bmpFileHeader; // 定義一個 BMP 文件頭的結構體
BITMAPINFOHEADER bmpInfo; // 定義一個 BMP 文件信息結構體
string s(argv[1]);
s = s + "tmp.bmp";
FILE *out = fopen(s.c_str(),"wb");
if((fp = fopen(argv[1], "rb")) == NULL)
{
printf("Cann't open the file!\n");
return 0;
}
fseek(fp, 0, SEEK_SET);
fread(&bmpFileHeader,14,1,fp);
fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, fp);
// 輸出BMP文件的位圖文件頭的所有信息
printf("位圖文件頭主要是對位圖文件的一些描述:BMPFileHeader\n\n");
printf("文件標識符 = 0X%X\n", bmpFileHeader.bfType);
printf("BMP 文件大小 = %d 字節\n", bmpFileHeader.bfSize);
printf("保留值1 = %d \n", bmpFileHeader.bfReserved1);
printf("保留值2 = %d \n", bmpFileHeader.bfReserved2);
printf("文件頭到圖像數據位開始的偏移量 = %d 字節\n", bmpFileHeader.bfOffBits);
// 輸出BMP文件的位圖信息頭的所有信息
printf("\n\n位圖信息頭主要是對位圖圖像方面信息的描述:BMPInfo\n\n");
printf("信息頭的大小 = %d 字節\n", bmpInfo.biSize);
printf("位圖的高度 = %d \n", bmpInfo.biHeight);
printf("位圖的寬度 = %d \n", bmpInfo.biWidth);
printf("圖像的位面數(位面數是調色板的數量,默認爲1個調色板) = %d \n", bmpInfo.biPlanes);
printf("每個像素的位數 = %d 位\n", bmpInfo.biBitCount);
printf("壓縮類型 = %d \n", bmpInfo.biCompression);
printf("圖像的大小 = %d 字節\n", bmpInfo.biSizeImage);
printf("水平分辨率 = %d \n", bmpInfo.biXPelsPerMeter);
printf("垂直分辨率 = %d \n", bmpInfo.biYPelsPerMeter);
printf("使用的色彩數 = %d \n", bmpInfo.biClrUsed);
printf("重要的色彩數 = %d \n", bmpInfo.biClrImportant);
printf("\n\n\n壓縮說明:有0(不壓縮),1(RLE 8,8位RLE壓縮),2(RLE 4,4位RLE壓縮,3(Bitfields,位域存放)");
//32位色圖
if(bmpInfo.biBitCount == 32){
int Height = bmpInfo.biHeight;
int Width = bmpInfo.biWidth;
bool reverse = false;
if(Height < 0){
reverse = true;
Height *= -1;
}
color_32 *pixels;
pixels = new color_32[Height*Width];
//fseek(fp,bmpFileHeader.bfOffBits,SEEK_SET);
for(int i = 0;i < Height * Width;i++)
fread(pixels+i,4,1,fp);
//設置文件頭
bmpInfo.biBitCount = 8;
bmpInfo.biClrUsed = 256;
//灰度圖顏色表
RGBQUAD pRGB[256];
for(int i = 0;i < 256;i++){
pRGB[i].rgbRed = i;
pRGB[i].rgbGreen = i;
pRGB[i].rgbBlue = i;
pRGB[i].rgbReserved = 0;
}
bmpFileHeader.bfOffBits = 54 + 4 * 256;
//考慮對齊規則 計算每一行的字節數
int LineByte = ((bmpInfo.biBitCount / 8 * Width + 3) / 4) * 4;
bmpFileHeader.bfSize = 54 + 4 * 256 + Height * LineByte;
//寫入新的文件
fseek(out,0,SEEK_SET);
fwrite(&bmpFileHeader,14,1,out);
fwrite(&bmpInfo,40,1,out);
fwrite(&pRGB,4*256,1,out);
for(int i = 0;i < Height;i++){
for(int j = 0;j < Width;j++){
float pr = pixels[i*Width + j].Red;
float pg = pixels[i*Width + j].Green;
float pb = pixels[i*Width + j].Blue;
//提取灰度低的黑色部分
BYTE data = pr * 0.299 + pg * 0.587 + pb * 0.114;
if((pr - pg) > 5 || (pr - pb) > 5) data = 255;
data = data < 25 ? 0 : 255;
fwrite(&data,1,1,out);
}
for(int j = Width;j < LineByte;j++){
BYTE data = 0;
fwrite(&data,1,1,out);
}
}
}
fclose(out);
fclose(fp);
while(1);
return 0;
}