天天看點

C語言代碼建立、解析BMP格式圖檔

一、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圖檔的存儲結構:

  1. 檔案頭: 它包含BMP圖像檔案的類型、内容尺寸和起始偏移量等資訊;
位元組順序 資料結構 描述
1,2 short 高8位為字母’B’,低8位為字母’M’
3,4,5,6 int 檔案大小
7,8 保留字1
9,10 保留字2
11,12,13,14 資料部分偏移量
  1. 圖像參數,它包含圖像的寬、高、壓縮方法,以及顔色定義等資訊;
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 關鍵色彩數
  1. 位圖資料

位圖資料存放的位置由檔案頭裡的第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;
}           

繼續閱讀