学习总结音视频转码(BMP格式)


BMP图像数据格式详解

BMP(Bitmap-File)位图文件是Windows采用的图形文件格式,在Windows环境下运行的所有图像处理软件都支持BMP图像文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。

Windows 3.0以前:
BMP图像件格式与显示设备有关,因此把这种BMP图像文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。
Windows 3.0以后:
BMP图像文件与显示设备无关,因此把这种BMP图像文件格式称为设备无关位图DIB(device-independent bitmap)文件格式。

注:Windows 3.0以后,在系统中仍然存在DDB位图,像BitBlt()这种函数就是基于DDB位图的,BMP位图文件默认的文件扩展名是BMP或者bmp(有时它也会以.DIB或.RLE作扩展名)。

一、BMP格式结构

BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息
◆ 位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
◆ 位图数据(bitmap data):图像数据。

数据段名称 大小(byte) 开始地址 结束地址
位图文件头(bitmap-file header) 14 0000h 000Dh
位图信息头(bitmap-information header) 40 000Eh 0035h
调色板(color table) 由biBitCount决定 0036h 未知
图片点阵数据(bitmap data) 由图片大小和颜色定 未知 未知

在BMP文件中,如果一个数据需要用几个字节来表示的话,那么该数据的存放字节顺序为“低地址存放低位数据,高地址存放高位数据”。如 0x1756在内存中的存储顺序为:
在这里插入图片描述
这种存储方式称为小端方式(little endian),与之相反的是大端方式(big endian)。

BMP格式比较简单,它只包含两个重要参数:编码格式(Encoding)和像素位数(bpp, bit-per-pixel)。到目前为止,BMP格式所支持的所有像素位数与编码格式的组合如下:

序号 像素位数(bpp) 编码格式(Encoding)
1 1 bit
2 4 bgr(blue-green-red)
3 4 rle(run-length encode)
4 8 bgr
5 8 rle
6 grayscale bgr
7 grayscale rle
8 16 bgr
9 16 bitfields-555
10 16 bitfields-565
11 16 bitfields-customized
12 24 bgr
13 32 bgr
14 32 bitfields-888
15 32 bitfields-customized

其中24bpp称为真彩(true-color)图像,应用最为广泛。16bpp的bmp图像拥有存储空间小,解析速度快,仿真彩效果好等特点,经常出现在游戏软件中。grayscale(灰度)图像其实是8bpp的一种情况。

二、BMP文件头

BMP文件结构体定义如下:

1
2
3
4
5
6
7
8
typedef struct tagBITMAPFILEHEARER
{
    UINT16 bfType;  //2Bytes,必须为“BM”,即0x424D 才是Windows位图文件
    DWORD bfSize;   //4Bytes,整个BMP文件的大小
    UINT16 bfReserved1; //2Bytes,保留,为0
    UINT16 bfReserved2; //2Bytes,保留,为0
    DWORD bfOffBits;    //4Bytes,文件起始位置到图像像素数据的字节偏移量
}BITMAPFILEHEADER;
变量名 地址偏移 大小 作用说明
bfType 0000h 2Bytes 文件标识符,必须为“BM”,即0x424D才是Windows位图文件
’BM’:Windows 3.1x,95,NT,…
bfSize 0002h 4Bytes 整个BMP文件的大小(以位B位单位)。
bfReserved1 0006h 2Bytes 保留,必须设置为0。
bfReserved2 0008h 2Bytes 保留,必须设置为0。
bfOffBits 000Ah 4Bytes 说明从文件头0000h开始到图像像素数据的字节偏移量(以字节Bytes为单位),
调色板长度根据位图格式不同而变化,可以用这个偏移量快速从文件中读取图像数据。
三、BMP信息头

BMP信息头结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _tagBMP_INFOHEADER
{
    DWORD  biSize;              //4Bytes,INFOHEADER结构体大小,存在其他版本INFOHEADER,用作区分
    LONG   biWidth;             //4Bytes,图像宽度(以像素为单位)
    LONG   biHeight;            //4Bytes,图像高度,+:图像存储顺序为Bottom2Top,-:Top2Bottom
    WORD   biPlanes;            //2Bytes,图像数据平面,BMP存储RGB数据,因此总为1
    WORD   biBitCount;          //2Bytes,图像像素位数
    DWORD  biCompression;       //4Bytes,0:不压缩,1:RLE8,2:RLE4
    DWORD  biSizeImage;         //4Bytes,4字节对齐的图像数据大小
    LONG   biXPelsPerMeter;     //4Bytes,用象素/米表示的水平分辨率
    LONG   biYPelsPerMeter;     //4Bytes,用象素/米表示的垂直分辨率
    DWORD  biClrUsed;           //4Bytes,实际使用的调色板索引数,0:使用所有的调色板索引
    DWORD biClrImportant;       //4Bytes,重要的调色板索引数,0:所有的调色板索引都重要
}BMP_INFOHEADER;
变量名 地址偏移 大小 作用说明
biSize 000Eh 4Bytes BMP信息头即BMP_INFOHEADER结构体所需要的字节数(以字节为单位)。
biWidth 0012h 4Bytes 图像的宽度(以像素为单位)。
biHeight 0016h 4Bytes 图像的高度(以像素为单位)。
还有一个用处,指明图像是正向的位图还是倒向的位图,
值是正数说明图像是倒向的即图像存储是由下到上;
值是负数说明图像是倒向的即图像存储是由上到下。
大多数BMP位图是倒向的位图,所以此值是正值。
biPlanes 001Ah 2Bytes 为目标设备说明位面数,其值总设置为1。
biBitCount 001Ch 2Bytes 说明一个像素点占几位(以比特位/像素为单位),其值可为1,4,8,16,24或32。
biCompression 001Eh 4Bytes 说明图像数据的压缩类型,取值范围为:
0 BI_RGB 不压缩(最常用)
1 BI_RLE8 8比特游程编码(BLE),只用于8位位图
2 BI_RLE4 4比特游程编码(BLE),只用于4位位图
3 BI_BITFIELDS比特域(BLE),只用于16/32位位图
biSizeImage 0022h 4Bytes 说明图像的大小,以字节为单位。当用BI_RGB格式时,总设置为0
biXPelsPerMeter 0026h 4Bytes 说明水平分辨率,用像素/米表示,有符号整数。
biYPelsPerMeter 002Ah 4Bytes 说明垂直分辨率,用像素/米表示,有符号整数。
biClrUsed 002Eh 4Bytes 说明位图实际使用的调色板索引数,0:使用所有的调色板索引。
biClrImportant 0032h 4Bytes 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。
四、BMP调色板

BMP调色板结构体定义如下:

1
2
3
4
5
6
7
typedef struct _tagRGBQUAD
{
 BYTE  rgbBlue;       //指定蓝色强度
 BYTE  rgbGreen;      //指定绿色强度
 BYTE  rgbRed;        //指定红色强度
 BYTE  rgbReserved;  //保留,设置为0
} RGBQUAD;

调色板其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。它在文件中的布局就像一个二维数组

palette[N][4]palette[N][4]

palette[N][4],其中N表示总的颜色索引数,每行的四个元素分别表示该索引对应的B、G、R、和Alpha的值,每个分量占一个字节。如不设透明通道时,Alpha为0。N为256。

索引:(蓝,绿,红,Alpha)

说明:使用调色板是为了减少文件的存储大小,如果其索引需要的位数和直接存储一样没有什么减少,那么也就没有必要使用调色板;调色板最多只需要256项,因此,8位以上则不需要调色板。

1,4,8位图像才会使用调色板数据,16,24,32位图像不需要调色板数据,即调色板最多只需要256项(索引0 - 255)。
颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。
其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。
即首先4字节表示颜色号1的颜色,接下来表示颜色号2的颜色,依此类推。

颜色表中RGBQUAD结构数据的个数有biBitCount来确定,
当biBitCount=1,4,8时,分别有2,16,256个表项。

当biBitCount=1时,为2色图像,BMP位图中有2个数据结构RGBQUAD,
一个调色板占用4字节数据,故2色图像的调色板长度为

2?42*4

2?4为8字节。

当biBitCount=4时,为16色图像,BMP位图中有16个数据结构RGBQUAD,
一个调色板占用4字节数据,故16像的调色板长度为

16?416*4

16?4为64字节。

当biBitCount=8时,为256色图像,BMP位图中有256个数据结构RGBQUAD,
一个调色板占用4字节数据,故256色图像的调色板长度为

256?4256*4

256?4为1024字节。

当biBitCount=16,24或32时,没有颜色表。

五、BMP图像数据区

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上,又称为Bottom_Up位图。
位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
也就是说,一个像素所占的字节数是biBitCount/8

对齐规则

Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,数据对齐满足以上要求,对数据的获取速度等都是有很大的增益。
//一个扫描行所占的字节数计算方法:

DataSizePerLine=(biWidth?biBitCount+31)/8;DataSizePerLine= (biWidth* biBitCount+31)/8;

DataSizePerLine=(biWidth?biBitCount+31)/8;即

DataSizePerLine=(biWidth?biBitCount+31)/32?4;DataSizePerLine= (biWidth* biBitCount+31)/32*4;

DataSizePerLine=(biWidth?biBitCount+31)/32?4;

1Byte = 8Bits 想得到的就是BYTES,但是计算中间单位是4Bytes,这个明白吧?就是说即使是1Bits也作为4Bytes来看待,因为是“大于或等于biWidth的,离4最近的整倍数”。 4Bytes的单位,就是32Bits的单位,所以,让它除以32就得到了当下的4的整倍数,但是,如果有余数就必须给结果再增加1,既然这样,不如在做除法之前就把它加进去,这样,给bits加上31,确保只要有余数,结果就会加1。最后要得到Bytes值,需要给这个中间单位再

?4*4

?4.
位图数据的大小(不压缩情况下):

DataSize=DataSizePerLine?biHeight;DataSize= DataSizePerLine* biHeight;

DataSize=DataSizePerLine?biHeight;

颜色表接下来位为位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位(8位为1字节);16色图像每点占4位(半字节);256色图像每点占8位(1字节);真彩色图像每点占24位(3字节)。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:

=??/8图像数据信息大小=(图像宽度*图像高度*记录像素的位数)/8

图像数据信息大小=(图像宽度?图像高度?记录像素的位数)/8。

六、识别BMP图片包含的数据内容

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <stdio.h>    
#include <stdlib.h>  
#include <iostream>  

using namespace std;

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef unsigned short UINT16;
typedef long LONG;

typedef struct tagBITMAPFILEHEARER
{
    //UINT16 bfType;
    DWORD bfSize;
    UINT16 bfReserved1;
    UINT16 bfReserved2;
    DWORD bfOffBits;
}BITMAPFILEHEADER;

typedef struct _tagBMP_INFOHEADER
{
    DWORD  biSize;             
    LONG   biWidth;            
    LONG   biHeight;
    WORD   biPlanes;
    WORD   biBitCount;  
    DWORD  biCompression;  
    DWORD  biSizeImage;      
    LONG   biXPelsPerMeter;    
    LONG   biYPelsPerMeter;    
    DWORD  biClrUsed;  
    DWORD biClrImportant;      
}BMP_INFOHEADER;

typedef struct _tagRGBQUAD
{
    BYTE  rgbBlue;      
    BYTE  rgbGreen;
    BYTE  rgbRed;  
    BYTE  rgbReserved;  
} RGBQUAD;

typedef struct tagIMAGEDATA
{
    BYTE red;
    BYTE green;
    BYTE blue;
}IMAGEDATA;

BITMAPFILEHEADER strHead;
RGBQUAD strPla[256];
BMP_INFOHEADER strInfo;
 
void showBmpHead(BITMAPFILEHEADER pBmpHead) {
    cout << "Bitmap file header" << endl << endl;
    cout << "File size:" << pBmpHead.bfSize << endl;
    cout << "Reserved1:" << pBmpHead.bfReserved1 << endl;
    cout << "Reserved2:" << pBmpHead.bfReserved2 << endl;
    cout << "The number of bytes offset from the actual bitmap data:" << pBmpHead.bfOffBits << endl << endl;
}

void showBmpInforHead(BMP_INFOHEADER pBmpInforHead) {
    cout << "Bitmap information header" << endl << endl;
    cout << "The length of the structure:" << pBmpInforHead.biSize << endl;
    cout << "Width:" << pBmpInforHead.biWidth << endl;
    cout << "Height:" << pBmpInforHead.biHeight << endl;
    cout << "biPlanes:" << pBmpInforHead.biPlanes << endl;
    cout << "biBitCount:" << pBmpInforHead.biBitCount << endl;
    cout << "Compression:" << pBmpInforHead.biCompression << endl;
    cout << "The number of bytes consumed by the actual bitmap data:" << pBmpInforHead.biSizeImage << endl;
    cout << "X:" << pBmpInforHead.biXPelsPerMeter << endl;
    cout << "Y:" << pBmpInforHead.biYPelsPerMeter << endl;
    cout << "Clr:" << pBmpInforHead.biClrUsed << endl;
    cout << "Significant color number:" << pBmpInforHead.biClrImportant << endl;
}

RGBQUAD* ReadFile(char *strFile)
{
   
    FILE *fpBMP;
    fpBMP = fopen(strFile, "rb");

    if (fpBMP != NULL) {
       
        WORD bfType;

        fread(&bfType, 1, sizeof(WORD), fpBMP);
        if (0x4d42 != bfType) {
            cout << "the file is not a bmp file!" << endl;
            return NULL;
        }

        fread(&strHead, 1, sizeof(BITMAPFILEHEADER), fpBMP);
        showBmpHead(strHead);

        fread(&strInfo, 1, sizeof(BMP_INFOHEADER), fpBMP);
        showBmpInforHead(strInfo);  

        cout << endl;

        int i;
        IMAGEDATA **imagedata;
        imagedata = (IMAGEDATA **)malloc(sizeof(IMAGEDATA*)*strInfo.biHeight);

        for (i = 0; i < strInfo.biHeight; i++)
            *(imagedata + i) = (IMAGEDATA*)malloc(sizeof(IMAGEDATA)*strInfo.biWidth);  

        for (i = 0; i < strInfo.biHeight; i++) {
            for (int j = 0; j < strInfo.biWidth; j++) {
                fread(*(imagedata + i) + j, sizeof(IMAGEDATA), 1, fpBMP);
            }
        }

        cout << "==================red======================" << endl;
        for (i = 0; i < strInfo.biHeight; i++) {
            for (int j = 0; j < strInfo.biWidth; j++) {
                printf("%d  ", imagedata[i][j].red);
            }
            cout << endl;
        }
        cout << "===================green=====================" << endl;
        for (i = 0; i < strInfo.biHeight; i++) {
            for (int j = 0; j < strInfo.biWidth; j++) {
                printf("%d  ", imagedata[i][j].green);
            }
            cout << endl;
        }
        cout << "====================blue====================" << endl;
        for (i = 0; i < strInfo.biHeight; i++) {
            for (int j = 0; j < strInfo.biWidth; j++) {
                printf("%d  ", imagedata[i][j].blue);
            }
            cout << endl;
        }
        fclose(fpBMP);
    }
    else {
        cout << "file open error!" << endl;
        return NULL;
    }
}

int main()
{
    char strFile[30] = "..//test.bmp";
    cout << strFile << endl;
    ReadFile(strFile);
    system("pause");
}
参考说明

BMP数据格式详解
代码参考
BMP文件格式详解(BMP file format)