一、BMP圖檔格式介紹
BMP格式的圖檔是衆多圖檔格式中的一種,也稱為位圖資料。通常BMP圖檔是沒有壓縮的,内部存放的是原始RGB資料,是以BMP檔案本身占用的空間比較大。目前在CPU強大的裝置上,最常用的格式都是JPG格式,JPG屬于壓縮格式,占用空間比較小,CPU強大就不在乎壓縮和解壓消耗的時間。在嵌入式裝置上,CPU性能一般較弱,如果要顯示圖檔,使用JPG格式就比較慢,不合适,解碼消耗時間太長,造成卡頓,這時候就可以采用BMP格式的圖檔,不需要解碼,直接按照BMP格式的結構讀取RGB圖形資料即可。而且BMP結構也比較簡單,不需要依賴任何外部庫,直接手撸幾十行代碼即可完成解碼編碼,非常友善。
典型的BMP圖像檔案由四部分組成:
`
1:檔案頭
2:圖像參數
3:調色闆
4:位圖資料
現在比較常用的是24位真彩色圖檔,24位真彩色圖檔就隻有3個部分,分别是: 檔案頭、圖像參數、位圖資料。這篇文章就介紹24位真彩色(RGB888)的BMP圖檔如何解碼編碼。
下面是BMP圖檔的存儲結構:
- 檔案頭: 它包含BMP圖像檔案的類型、内容尺寸和起始偏移量等資訊;
位元組順序 | 資料結構 | 描述 |
---|---|---|
1,2 | short | 高8位為字母’B’,低8位為字母’M’ |
3,4,5,6 | int | 檔案大小 |
7,8 | 保留字1 | |
9,10 | 保留字2 | |
11,12,13,14 | 資料部分偏移量 |
- 圖像參數,它包含圖像的寬、高、壓縮方法,以及顔色定義等資訊;
15,16,17,18 | 目前結構體的大小,通常是40或56 | ||
19,20,21,22 | 圖像寬度(像素) | 0x12~0x15是寬 | |
23,24,25,26 | 圖像高度(像素) | 0x16~0x19是寬 | |
27,28 | 這個字的值永遠是1 | 說的是兩個位元組總和是1, | |
29,30(0x18,0x19) | 每像素占用的位數,即bpp | 每個像素所需的位數,必須是1(雙色)、4(16色)、8(256色)、24(真彩色)之一 | |
31,32,33,34 | 壓縮方式 | 0x1e~0x21,值是0表示不壓縮 | |
35,36,37,38 | 水準分辨率,pixels-per-meter | ||
39,40,41,42 | 垂直分辨率,pixels-per-meter | ||
43,44,45,46 | |||
47,48,49,50 | 引用色彩數 | ||
51,52,53,54 | 關鍵色彩數 |
- 位圖資料
位圖資料存放的位置由檔案頭裡的第5個參數決定(位圖資料偏移量),正常情況下,位圖資料就緊接着存放在圖像參數的後面。
讀取或者寫入位圖資料的注意事項:
(1) 每行的位元組數必須是4的倍數,如果不是,則需要用0補齊。
(2) BMP位圖資料的存放是從下到上,從左到右的。先讀最後一行,讀完後在讀倒數第二行。
二、示例代碼
2.1 讀取BMP圖檔的參數資訊
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma pack(1) //強制1個位元組對齊
//BMP的檔案頭
struct _BMP_HEAD
{
char type[2]; //圖檔的類型 "BM"
unsigned int size; //檔案大小
unsigned short r1; //保留1
unsigned short r2; //保留2
unsigned int seek; //資料偏移位元組(真實像素點資料)
};
//BMP的參數資訊
struct _BMP_INFO
{
unsigned int size; //目前結構體大小
unsigned int w; //寬度
unsigned int h; //高度
unsigned short flag; //固定為1
unsigned short bit; //像素點的位數
unsigned int r1; //壓縮方式 0
unsigned int r2; //水準分辨率
unsigned int r3; //垂直分辨率
unsigned int r4; //垂直分辨率
unsigned int r5; //引用色彩
unsigned int r6; //關鍵色彩
};
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("傳入的參數格式: ./a.out <檔案名稱>\n");
return 0;
}
/*1. 打開BMP圖檔*/
FILE *fp=fopen(argv[1],"rb");
if(fp==NULL)
{
printf("%s 檔案不存在.\n",argv[1]);
return 0;
}
/*2. 讀取BMP的檔案頭*/
int cnt;
struct _BMP_HEAD bmp_head;
cnt=fread(&bmp_head,1,sizeof(struct _BMP_HEAD),fp);
printf("成功讀取:%d 位元組.\n",cnt);
printf("圖檔類型:%c%c\n",bmp_head.type[0],bmp_head.type[1]);
printf("檔案大小:%d\n",bmp_head.size);
printf("資料距離檔案頭的偏移量:%d\n",bmp_head.seek);
/*3. 讀取檔案參數資訊*/
struct _BMP_INFO bmp_info;
cnt=fread(&bmp_info,1,sizeof(struct _BMP_INFO),fp);
printf("成功讀取:%d 位元組.\n",cnt);
printf("目前結構體大小:%d\n",bmp_info.size);
printf("目前圖檔寬度:%d\n",bmp_info.w);
printf("目前圖檔高度:%d\n",bmp_info.h);
printf("目前圖檔顔色位數:%d\n",bmp_info.bit);
printf("目前圖檔的壓縮情況:%d\n",bmp_info.r1);
/*4. 關閉檔案*/
fclose(fp);
return 0;
}
2.2 建立一張純色圖檔
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#pragma pack(1) //強制1個位元組對齊
//BMP的檔案頭
struct _BMP_HEAD
{
char type[2]; //圖檔的類型 "BM"
unsigned int size; //檔案大小
unsigned short r1; //保留1
unsigned short r2; //保留2
unsigned int seek; //資料偏移位元組(真實像素點資料)
};
//BMP的參數資訊
struct _BMP_INFO
{
unsigned int size; //目前結構體大小
unsigned int w; //寬度
unsigned int h; //高度
unsigned short flag; //固定為1
unsigned short bit; //像素點的位數
unsigned int r1; //壓縮方式 0
unsigned int r2; //水準分辨率
unsigned int r3; //垂直分辨率
unsigned int r4; //垂直分辨率
unsigned int r5; //引用色彩
unsigned int r6; //關鍵色彩
};
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("傳入的參數格式: ./a.out <新圖檔的名稱>\n");
return 0;
}
/*1. 建立一張BMP圖檔*/
FILE *fp=fopen(argv[1],"wb");
if(fp==NULL)
{
printf("%s 檔案建立失敗.\n",argv[1]);
return 0;
}
/*2. 建立BMP的檔案頭*/
int cnt;
struct _BMP_HEAD bmp_head;
memset(&bmp_head,0,sizeof(struct _BMP_HEAD));
//圖檔的類型
bmp_head.type[0]='B';
bmp_head.type[1]='M';
//檔案大小
bmp_head.size=54+800*480*3;
//資料偏移量
bmp_head.seek=54;
//寫檔案頭
cnt=fwrite(&bmp_head,1,sizeof(struct _BMP_HEAD),fp);
printf("成功寫入:%d 位元組.\n",cnt);
/*3. 寫檔案參數資訊*/
struct _BMP_INFO bmp_info;
memset(&bmp_info,0,sizeof(struct _BMP_INFO));
//目前結構體大小
bmp_info.size=sizeof(struct _BMP_INFO);
//圖檔的寬度和高度
bmp_info.w=800;
bmp_info.h=480;
//圖檔的顔色位數
bmp_info.bit=24;
//标志位
bmp_info.flag=1;
//寫入檔案參數資訊
cnt=fwrite(&bmp_info,1,sizeof(struct _BMP_INFO),fp);
printf("成功寫入:%d 位元組.\n",cnt);
/*4. 寫入位圖資料*/
int w,h;
int c=0xFF0033; //紅色
for(h=0;h<480;h++)
{
for(w=0;w<800;w++)
{
fwrite(&c,1,3,fp);
}
}
/*5. 關閉檔案*/
fclose(fp);
return 0;
}