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
 
  
    cpp
  
 1struct{
 2    u_int16_t bfType;   
 3    u_int32_t bfSize;   
 4    u_int16_t bfReserved1;   
 5    u_int16_t bfReserved2;   
 6    u_int32_t bfOffBits;  
 7    void show(){
 8        PRINT_FONT_GRE;
 9        printf("BmpFileHeader\n");
10        PRINT_ATTR_REC;
11        fflush(stdout);
12        printf("bfType: %d\n", bfType);
13        printf("bfSize: %d kb\n", bfSize);
14        printf("bfReservered1, 2: %d %d\n", bfReserved1, bfReserved2);
15        printf("bfOffBits: %d\n", bfOffBits);
16
17        puts("");
18    } 
19}BmpFileHeader;该头固定14字节
DIB header
 
  
    cpp
  
 1struct{  
 2    u_int32_t biSize;   
 3    u_int32_t biWidth;   
 4    u_int32_t biHeight;   
 5    u_int16_t biPlanes;   
 6    u_int16_t biBitCount;   
 7    u_int32_t biCompression;   
 8    u_int32_t biSizeImage;   
 9    u_int32_t biXPelsPerMeter;   
10    u_int32_t biYPelsPerMeter;   
11    u_int32_t biClrUsed;   
12    u_int32_t biClrImportant;   
13
14    /**
15     * 由于目前一般dedaodeBMP文件均采用第5代DIBHEADER格式,因而DIBHEADER信息长为124字节
16     * 我们额外吸收掉这些额外的信息,在本实验中不予考虑它们。
17     * */
18    u_int8_t foo[84];
19    void show(){
20        PRINT_FONT_GRE;
21        printf("DibHeader\n");
22        PRINT_ATTR_REC;
23        fflush(stdout);
24        printf("biSize: %d\n", biSize);
25        printf("biWidth: %d\n", biWidth);
26        printf("biHeight: %d\n", biHeight);
27        printf("biPlanes(always 1): %d\n", biPlanes);
28        printf("biBitCount: %d\n", biBitCount);
29        printf("biCompression: %d\n", biCompression);
30        printf("biSizeImage: %d\n", biSizeImage);
31        printf("biXPelsPerMeter: %d\n", biXPelsPerMeter);
32        printf("biYPelsPerMeter: %d\n", biYPelsPerMeter);
33        printf("biClrUsed(0 if 2^biBitCount): %d\n", biClrUsed);
34        printf("biClrImportant(0 if all important): %d\n", biClrImportant);
35
36        puts("");
37    }
38}DibHeader;pixel array
对于缩放后的图片的每一个像素点,将其按对应比例映射回原图,在原图中假设该投影点被四个点$A,B,C,D$构成的矩形包围,那么就利用这四个点的颜色构造出该点的颜色。

使用博客 位图图像文件缩放(c++)提及的双线性插值: $$ F(X) = dxdyF(B)+(1-dx)(1-dy)F(D)+(1-dx)dyF(a)+dx(1-dy)F(C) $$
实现
坑点: ** BMP文件的width**应当是4的倍数!
最开始虽然注意到了4的倍数,但是是使得height是4的倍数,导致最后的satou酱不仅是灰色的,还是倾斜的。经过好一番调试才发现是width,也就是列。

 
  
    cpp
  
  1#include <stdio.h>  
  2#include <string.h>  
  3#include <sys/types.h>  
  4#include "rqdmap.h"
  5 
  6#include <iostream> 
  7#pragma pack(2)   //对齐
  8using namespace std;  
  9 
 10struct{
 11    u_int16_t bfType;   
 12    u_int32_t bfSize;   
 13    u_int16_t bfReserved1;   
 14    u_int16_t bfReserved2;   
 15    u_int32_t bfOffBits;  
 16    void show(){
 17        PRINT_FONT_GRE;
 18        printf("BmpFileHeader\n");
 19        PRINT_ATTR_REC;
 20        fflush(stdout);
 21        printf("bfType: %d\n", bfType);
 22        printf("bfSize: %d kb\n", bfSize);
 23        printf("bfReservered1, 2: %d %d\n", bfReserved1, bfReserved2);
 24        printf("bfOffBits: %d\n", bfOffBits);
 25
 26        puts("");
 27    } 
 28}BmpFileHeader;
 29
 30struct{  
 31    u_int32_t biSize;   
 32    u_int32_t biWidth;   
 33    u_int32_t biHeight;   
 34    u_int16_t biPlanes;   
 35    u_int16_t biBitCount;   
 36    u_int32_t biCompression;   
 37    u_int32_t biSizeImage;   
 38    u_int32_t biXPelsPerMeter;   
 39    u_int32_t biYPelsPerMeter;   
 40    u_int32_t biClrUsed;   
 41    u_int32_t biClrImportant;   
 42
 43    /**
 44     * 由于目前一般dedaodeBMP文件均采用第5代DIBHEADER格式,因而DIBHEADER信息长为124字节
 45     * 我们额外吸收掉这些额外的信息,在本实验中不予考虑它们。
 46     * */
 47    u_int8_t foo[84];
 48    void show(){
 49        PRINT_FONT_GRE;
 50        printf("DibHeader\n");
 51        PRINT_ATTR_REC;
 52        fflush(stdout);
 53        printf("biSize: %d\n", biSize);
 54        printf("biWidth: %d\n", biWidth);
 55        printf("biHeight: %d\n", biHeight);
 56        printf("biPlanes(always 1): %d\n", biPlanes);
 57        printf("biBitCount: %d\n", biBitCount);
 58        printf("biCompression: %d\n", biCompression);
 59        printf("biSizeImage: %d\n", biSizeImage);
 60        printf("biXPelsPerMeter: %d\n", biXPelsPerMeter);
 61        printf("biYPelsPerMeter: %d\n", biYPelsPerMeter);
 62        printf("biClrUsed(0 if 2^biBitCount): %d\n", biClrUsed);
 63        printf("biClrImportant(0 if all important): %d\n", biClrImportant);
 64
 65        puts("");
 66    }
 67}DibHeader;
 68
 69struct pixel{
 70    u_int8_t c[3];
 71    pixel operator * (double x){
 72        pixel res; res.c[0] = res.c[1] = res.c[2] = 0;
 73        for(int i = 0; i < 3; i++) res.c[i] = c[i] * x;
 74        return res;
 75    }
 76    pixel operator +(const pixel &b){
 77        pixel res; res.c[0] = res.c[1] = res.c[2] = 0;
 78        for(int i = 0; i < 3; i++) res.c[i] = c[i] + b.c[i];
 79        return res;
 80    }
 81    void prt(){
 82        printf("<%d, %d, %d>\n", c[0], c[1], c[2]);
 83    }
 84};
 85
 86int height, width;
 87pixel *rawPic;
 88
 89int newHeight, newWidth;
 90pixel *toPic;
 91
 92int convert(int i, int j, int n, int m){
 93    i = max(0, min(i, n));
 94    j = max(0, min(j, m));
 95    return i * m + j;
 96}
 97
 98void prt(const char *to){
 99    FILE *out = fopen(to, "wb+");
100    BmpFileHeader.bfOffBits = 14 + 40;
101    BmpFileHeader.bfSize = BmpFileHeader.bfOffBits + newHeight * newWidth * 3;
102
103    DibHeader.biSize = 40;
104    DibHeader.biWidth = newWidth;
105    DibHeader.biHeight = newHeight;
106    fwrite(&BmpFileHeader, sizeof(BmpFileHeader), 1, out);
107    fwrite(&DibHeader, DibHeader.biSize, 1, out);
108
109    for(int i = 0; i < newHeight; i++) for(int j = 0; j < newWidth; j++)
110        fwrite(&toPic[convert(i, j, newHeight, newWidth)], sizeof(pixel), 1, out);
111}
112
113
114int main(int argc, char *argv[]){      
115
116    if(argc != 4 && argc != 2){
117        PRINT_FONT_RED;
118        puts(" usage: ./main bmpFrom ratio bmpto");
119        puts(" usage: ./main bmpFile");
120        PRINT_ATTR_REC;
121        fflush(stdout);
122        return 0;
123    }
124
125    FILE *in = fopen(argv[1], "rb");
126
127    fread(&BmpFileHeader, 1, sizeof(BmpFileHeader), in);
128    fread(&DibHeader, 1, sizeof(DibHeader), in);
129
130
131    if(argc == 2){
132        BmpFileHeader.show();
133        DibHeader.show();
134        return 0;
135    }
136
137    /**
138     * 考虑到24色RGB颜色无调色盘,接下来的信息就是像素点信息
139     * 这一点也可以从:
140     *  bfOffBits(138) = BmpFileHeader(14) + biSize(124)得以印证
141     * */
142
143    height = DibHeader.biHeight;
144    width = DibHeader.biWidth;
145    rawPic = (pixel*)malloc(height * width * sizeof(pixel));
146    for(int i = 0; i < height; i++) for(int j = 0; j < width; j++)
147        fread(rawPic + convert(i, j, height, width), 1, sizeof(pixel), in);
148    
149
150    
151    double ratio; sscanf(argv[2], "%lf", &ratio);
152    ratio /= 100;
153
154
155    newHeight = ratio * height;
156    // newHeight = (newHeight + 3) / 4 * 4;
157    newWidth = ratio * width;
158    newWidth = (newWidth + 3) / 4 * 4;
159
160    // newWidth = 600;
161    toPic = (pixel*)malloc(newHeight * newWidth * sizeof(pixel));
162    memset(toPic, 0, newHeight * newWidth * sizeof(pixel));
163
164    for(int i = 0; i < newHeight; i++) for(int j = 0; j < newWidth; j++){
165        // double _i = i * height / newHeight, _j = j * width / newWidth;
166        double _i = i / ratio, _j = j / ratio;
167        int ni = _i, nj = _j;
168        double dx = _i - ni, dy = _j - nj;
169
170        pixel B = rawPic[convert(ni + 1, nj + 1, height, width)];
171        pixel D = rawPic[convert(ni, nj, height, width)];
172        pixel A = rawPic[convert(ni + 1, nj, height, width)];
173        pixel C = rawPic[convert(ni, nj + 1, height, width)];
174
175
176        toPic[convert(i, j, newHeight, newWidth)] = \
177            (B * dx * dy) + (D * (1 - dx) * (1 - dy)) + (A * (1 - dx) * dy) + (C * dx * (1 - dy));
178        
179    }   
180  
181
182    prt(argv[3]);
183    fclose(in);  
184    return 0;  
185}最后放一张satou(逃

