BMP-FS

[toc]

BMP文件格式初探

实现BMP文件的放缩

搜索资料时找到,笑死。

此为课程设计的第四题,这道题体现了大作业的精髓:在抄中知道,在抄中学习,在抄中苟且。

BMP 文件结构

一般的情况如下所示,摘自wiki

Structure name Optional Size Purpose Comments
Bitmap file header No 14 bytes To store general information about the bitmap image file Not needed after the file is loaded in memory
DIB header No Fixed-size (7 different versions exist) To store detailed information about the bitmap image and define the pixel format Immediately follows the Bitmap file header
Extra bit masks Yes 3 or 4 DWORDs[6] (12 or 16 bytes) To define the pixel format Present only in case the DIB header is the BITMAPINFOHEADER and the Compression Method member is set to either BI_BITFIELDS or BI_ALPHABITFIELDS
Color table Semi-optional Variable size To define colors used by the bitmap image data (Pixel array) Mandatory for color depths ≤ 8 bits
Gap1 Yes Variable size Structure alignment An artifact of the File offset to Pixel array in the Bitmap file header
Pixel array No Variable size To define the actual values of the pixels The pixel format is defined by the DIB header or Extra bit masks. Each row in the Pixel array is padded to a multiple of 4 bytes in size
Gap2 Yes Variable size Structure alignment An artifact of the ICC profile data offset field in the DIB header
ICC color profile Yes Variable size To define the color profile for color management Can also contain a path to an external file containing the color profile. When loaded in memory as “non-packed DIB”, it is located between the color table and Gap1.[7]

一般来说,现在获取到的BMP文件的DIB头都是第五代(BITMAPV5HEADER),长124字节;但是针对本实验而言,只需要极少的一些信息,因而只保留40字节,剩余84字节读进来不与使用。

调色盘在24位(RGB各占一个字节)时不需要,因而像素矩阵的偏移就是14 + 124

因而在本实验中,BMP结构仅有三部分:

  • BMP file header
  • DIB header
  • pixel array

BMP file header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct{
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
void show(){
PRINT_FONT_GRE;
printf("BmpFileHeader\n");
PRINT_ATTR_REC;
fflush(stdout);
printf("bfType: %d\n", bfType);
printf("bfSize: %d kb\n", bfSize);
printf("bfReservered1, 2: %d %d\n", bfReserved1, bfReserved2);
printf("bfOffBits: %d\n", bfOffBits);

puts("");
}
}BmpFileHeader;

该头固定14字节

DIB header

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
struct{  
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
u_int16_t biPlanes;
u_int16_t biBitCount;
u_int32_t biCompression;
u_int32_t biSizeImage;
u_int32_t biXPelsPerMeter;
u_int32_t biYPelsPerMeter;
u_int32_t biClrUsed;
u_int32_t biClrImportant;

/**
* 由于目前一般dedaodeBMP文件均采用第5代DIBHEADER格式,因而DIBHEADER信息长为124字节
* 我们额外吸收掉这些额外的信息,在本实验中不予考虑它们。
* */
u_int8_t foo[84];
void show(){
PRINT_FONT_GRE;
printf("DibHeader\n");
PRINT_ATTR_REC;
fflush(stdout);
printf("biSize: %d\n", biSize);
printf("biWidth: %d\n", biWidth);
printf("biHeight: %d\n", biHeight);
printf("biPlanes(always 1): %d\n", biPlanes);
printf("biBitCount: %d\n", biBitCount);
printf("biCompression: %d\n", biCompression);
printf("biSizeImage: %d\n", biSizeImage);
printf("biXPelsPerMeter: %d\n", biXPelsPerMeter);
printf("biYPelsPerMeter: %d\n", biYPelsPerMeter);
printf("biClrUsed(0 if 2^biBitCount): %d\n", biClrUsed);
printf("biClrImportant(0 if all important): %d\n", biClrImportant);

puts("");
}
}DibHeader;

pixel array

对于缩放后的图片的每一个像素点,将其按对应比例映射回原图,在原图中假设该投影点被四个点$A,B,C,D$构成的矩形包围,那么就利用这四个点的颜色构造出该点的颜色。

使用博客位图图像文件缩放(c++)提及的双线性插值

实现

坑点: BMP文件的width应当是4的倍数!

最开始虽然注意到了4的倍数,但是是使得height是4的倍数,导致最后的satou酱不仅是灰色的,还是倾斜的。经过好一番调试才发现是width,也就是列。

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <stdio.h>  
#include <string.h>
#include <sys/types.h>
#include "rqdmap.h"

#include <iostream>
#pragma pack(2) //对齐
using namespace std;

struct{
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
void show(){
PRINT_FONT_GRE;
printf("BmpFileHeader\n");
PRINT_ATTR_REC;
fflush(stdout);
printf("bfType: %d\n", bfType);
printf("bfSize: %d kb\n", bfSize);
printf("bfReservered1, 2: %d %d\n", bfReserved1, bfReserved2);
printf("bfOffBits: %d\n", bfOffBits);

puts("");
}
}BmpFileHeader;

struct{
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
u_int16_t biPlanes;
u_int16_t biBitCount;
u_int32_t biCompression;
u_int32_t biSizeImage;
u_int32_t biXPelsPerMeter;
u_int32_t biYPelsPerMeter;
u_int32_t biClrUsed;
u_int32_t biClrImportant;

/**
* 由于目前一般dedaodeBMP文件均采用第5代DIBHEADER格式,因而DIBHEADER信息长为124字节
* 我们额外吸收掉这些额外的信息,在本实验中不予考虑它们。
* */
u_int8_t foo[84];
void show(){
PRINT_FONT_GRE;
printf("DibHeader\n");
PRINT_ATTR_REC;
fflush(stdout);
printf("biSize: %d\n", biSize);
printf("biWidth: %d\n", biWidth);
printf("biHeight: %d\n", biHeight);
printf("biPlanes(always 1): %d\n", biPlanes);
printf("biBitCount: %d\n", biBitCount);
printf("biCompression: %d\n", biCompression);
printf("biSizeImage: %d\n", biSizeImage);
printf("biXPelsPerMeter: %d\n", biXPelsPerMeter);
printf("biYPelsPerMeter: %d\n", biYPelsPerMeter);
printf("biClrUsed(0 if 2^biBitCount): %d\n", biClrUsed);
printf("biClrImportant(0 if all important): %d\n", biClrImportant);

puts("");
}
}DibHeader;

struct pixel{
u_int8_t c[3];
pixel operator * (double x){
pixel res; res.c[0] = res.c[1] = res.c[2] = 0;
for(int i = 0; i < 3; i++) res.c[i] = c[i] * x;
return res;
}
pixel operator +(const pixel &b){
pixel res; res.c[0] = res.c[1] = res.c[2] = 0;
for(int i = 0; i < 3; i++) res.c[i] = c[i] + b.c[i];
return res;
}
void prt(){
printf("<%d, %d, %d>\n", c[0], c[1], c[2]);
}
};

int height, width;
pixel *rawPic;

int newHeight, newWidth;
pixel *toPic;

int convert(int i, int j, int n, int m){
i = max(0, min(i, n));
j = max(0, min(j, m));
return i * m + j;
}

void prt(const char *to){
FILE *out = fopen(to, "wb+");
BmpFileHeader.bfOffBits = 14 + 40;
BmpFileHeader.bfSize = BmpFileHeader.bfOffBits + newHeight * newWidth * 3;

DibHeader.biSize = 40;
DibHeader.biWidth = newWidth;
DibHeader.biHeight = newHeight;
fwrite(&BmpFileHeader, sizeof(BmpFileHeader), 1, out);
fwrite(&DibHeader, DibHeader.biSize, 1, out);

for(int i = 0; i < newHeight; i++) for(int j = 0; j < newWidth; j++)
fwrite(&toPic[convert(i, j, newHeight, newWidth)], sizeof(pixel), 1, out);
}


int main(int argc, char *argv[]){

if(argc != 4 && argc != 2){
PRINT_FONT_RED;
puts(" usage: ./main bmpFrom ratio bmpto");
puts(" usage: ./main bmpFile");
PRINT_ATTR_REC;
fflush(stdout);
return 0;
}

FILE *in = fopen(argv[1], "rb");

fread(&BmpFileHeader, 1, sizeof(BmpFileHeader), in);
fread(&DibHeader, 1, sizeof(DibHeader), in);


if(argc == 2){
BmpFileHeader.show();
DibHeader.show();
return 0;
}

/**
* 考虑到24色RGB颜色无调色盘,接下来的信息就是像素点信息
* 这一点也可以从:
* bfOffBits(138) = BmpFileHeader(14) + biSize(124)得以印证
* */

height = DibHeader.biHeight;
width = DibHeader.biWidth;
rawPic = (pixel*)malloc(height * width * sizeof(pixel));
for(int i = 0; i < height; i++) for(int j = 0; j < width; j++)
fread(rawPic + convert(i, j, height, width), 1, sizeof(pixel), in);



double ratio; sscanf(argv[2], "%lf", &ratio);
ratio /= 100;


newHeight = ratio * height;
// newHeight = (newHeight + 3) / 4 * 4;
newWidth = ratio * width;
newWidth = (newWidth + 3) / 4 * 4;

// newWidth = 600;
toPic = (pixel*)malloc(newHeight * newWidth * sizeof(pixel));
memset(toPic, 0, newHeight * newWidth * sizeof(pixel));

for(int i = 0; i < newHeight; i++) for(int j = 0; j < newWidth; j++){
// double _i = i * height / newHeight, _j = j * width / newWidth;
double _i = i / ratio, _j = j / ratio;
int ni = _i, nj = _j;
double dx = _i - ni, dy = _j - nj;

pixel B = rawPic[convert(ni + 1, nj + 1, height, width)];
pixel D = rawPic[convert(ni, nj, height, width)];
pixel A = rawPic[convert(ni + 1, nj, height, width)];
pixel C = rawPic[convert(ni, nj + 1, height, width)];


toPic[convert(i, j, newHeight, newWidth)] = \
(B * dx * dy) + (D * (1 - dx) * (1 - dy)) + (A * (1 - dx) * dy) + (C * dx * (1 - dy));

}


prt(argv[3]);
fclose(in);
return 0;
}

最后放一张satou(逃