C 中的 .BMP 文件 - DIB 标头返回图像大小 0,即使 BMP 标头返回文件大小
Posted
技术标签:
【中文标题】C 中的 .BMP 文件 - DIB 标头返回图像大小 0,即使 BMP 标头返回文件大小【英文标题】:.BMP files in C - DIB header returns image size 0 even though the BMP header returns the file size 【发布时间】:2022-01-15 20:24:53 【问题描述】:我正在尝试在 C 中翻转 BMP 图像。 我遇到了一个问题,当我尝试读取 BMP 标头时一切正常并且值正确,但是当我尝试读取 DIB 标头时,我得到了除图像大小(原始位图数据)之外的所有值。我得到一个零,考虑到我在 BMP 标头中获得了文件大小,这真的很奇怪。我尝试调试代码并在线查找问题,但没有太大帮助。
这是我的代码:
#include <stdlib.h>
typedef unsigned int int32;
typedef unsigned short int int16;
typedef unsigned char char8;
struct BITMAP_header
char name[2]; // BM
int32 size;
int garbage; // ?
int32 image_offset; //offset from where the image starts in the file
;
struct DIB_header
int32 header_size;
int32 width;
int32 height;
int16 colorplanes;
int16 bitsperpixel;
int32 compression;
int32 image_size;
int32 temp[4];
;
struct RGB
char8 blue;
char8 green;
char8 red;
;
struct Image
struct RGB** rgb;
int height;
int width;
;
struct Image readImage(FILE *fp, int height, int width)
struct Image pic;
pic.rgb = (struct RGB**)malloc(height * sizeof(void*)); // pointer to a row of rgb data (pixels)
pic.height = height;
pic.width = width;
for (int i = height-1; i >=0 ; i--)
pic.rgb[i] = (struct RGB*)malloc(width * sizeof(struct RGB)); // allocating a row of pixels
fread(pic.rgb[i], width, sizeof(struct RGB), fp);
return pic;
void freeImage(struct Image pic)
for (int i = pic.height -1; i>= 0; i--)
free(pic.rgb[i]);
free(pic.rgb);
void createImage(struct BITMAP_header header, struct DIB_header dibheader, struct Image pic)
FILE* fpw = fopen("new.bmp", "w");
if (fpw == NULL)
return 1;
fwrite(header.name, 2, 1, fpw);
fwrite(&header.size, 3 * sizeof(int), 1, fpw);
fwrite(&dibheader, sizeof(struct DIB_header), 1, fpw);
int count = 0;
for (int i = pic.height - 1; i >= 0; i--)
fwrite(pic.rgb[count], pic.width, sizeof(struct RGB), fpw);
count++;
fclose(fpw);
int openbmpfile()
FILE* fp = fopen("C:\\Users\\User\\Downloads\\MARBLES.BMP", "rb"); // read binary
if (fp == NULL)
return 1;
struct BITMAP_header header;
struct DIB_header dibheader;
fread(header.name, 2, 1, fp); //BM
fread(&header.size, 3 * sizeof(int), 1, fp); // 12 bytes
printf("First two characters: %c%c\n", header.name[0], header.name[1]);
if ((header.name[0] != 'B') || (header.name[1] != 'M'))
fclose(fp);
return 1;
printf("Size: %d\n", header.size);
printf("Offset: %d\n", header.image_offset);
fread(&dibheader.header_size, sizeof(struct DIB_header) , 1, fp);
printf("Header size: %d\nWidth: %d\nHeight: %d\nColor planes: %d\nBits per pixel: %d\nCompression: %d\nImage size: %d\n",
dibheader.header_size, dibheader.width, dibheader.height, dibheader.colorplanes, dibheader.bitsperpixel,
dibheader.compression, dibheader.image_size);
if ((dibheader.header_size != 40) || (dibheader.compression != 0) || (dibheader.bitsperpixel != 24))
fclose(fp);
return 1;
fseek(fp, header.image_offset, SEEK_SET);
struct Image image = readImage(fp, dibheader.height, dibheader.width);
createImage(header, dibheader, image);
fclose(fp);
freeImage(image);
return 0;
int main()
openbmpfile();
我还尝试通过将文件头大小减小文件大小来手动写入大小,但没有奏效。
提前致谢!
【问题讨论】:
确保对齐正确。试试#pragma pack(push, 1) struct ... #pragma pack(pop)
。
请不要为特定的 32 位或 16 位整数定义自己的类型别名。使用标准的uint16_t
或uint32_t
(在<stdint.h>
中定义)。
@extratype 我试过了,没用
typedef unsigned int int32;
让一个已经很痛苦的话题更加混乱:错位的微软遗留问题。
【参考方案1】:
BITMAP_header
和 DIB_header
的格式要求结构大小为 14 和 40。但是编译器对齐结构,大小会有所不同。您必须使用编译器特定的标志来禁用结构对齐(不清楚您使用哪个编译器)。 RGB
结构也是同样的问题。
还有字节序依赖性,位图不以 RGB 格式存储像素,它会翻转字节。
typedef unsigned int int32;
这会将unsigned
隐藏为signed
。结构中的一些值应该是有符号的。例如,高度可以是负数,表示它应该被翻转。
你可以一一读取整数,使用
void readint(FILE *f, int *val, int size)
unsigned char buf[4];
fread(buf, size, 1, f);
*val = 0;
for (int i = size - 1; i >= 0; i--)
*val += (buf[i] << (8 * i));
void writeint(FILE* f, int val, int size)
unsigned char buf[4];
for (int i = size - 1; i >= 0; i--)
buf[i] = (val >> 8 * i) & 0xff;
fwrite(buf, size, 1, f);
然后你读取一个 24 位的位图如下:
fread(header.name, 2, 1, fp); //BM
readint(fp, &header.size, 4);
readint(fp, &header.garbage, 4);
readint(fp, &header.image_offset, 4);
readint(fp, &dibheader.header_size, 4);
readint(fp, &dibheader.width, 4);
readint(fp, &dibheader.height, 4);
readint(fp, &dibheader.colorplanes, 2);
readint(fp, &dibheader.bitsperpixel, 2);
readint(fp, &dibheader.compression, 4);
readint(fp, &dibheader.image_size, 4);
readint(fp, &dibheader.temp[0], 4);
readint(fp, &dibheader.temp[1], 4);
readint(fp, &dibheader.temp[2], 4);
readint(fp, &dibheader.temp[3], 4);
char *buf = malloc(dibheader.image_size);
if (!buf)
return 0;
fread(buf, 1, dibheader.image_size, fp);
还有其他问题需要考虑,例如位图的宽度对齐等。您希望从宽度为 4 的倍数的 24 位位图开始。
要写入位图,使用二进制标志,按相同顺序写入
FILE* fout = fopen("new.bmp", "wb");
if (fout == NULL) return 0;
fwrite(header.name, 1, 2, fout);
writeint(fout, header.size, 4);
writeint(fout, header.reserved, 4);
writeint(fout, header.image_offset, 4);
writeint(fout, info.header_size, 4);
writeint(fout, info.width, 4);
writeint(fout, info.height, 4);
writeint(fout, info.colorplanes, 2);
writeint(fout, info.bitsperpixel, 2);
writeint(fout, info.compression, 4);
writeint(fout, info.image_size, 4);
writeint(fout, info.temp[0], 4);
writeint(fout, info.temp[1], 4);
writeint(fout, info.temp[2], 4);
writeint(fout, info.temp[3], 4);
fwrite(buf, 1, info.image_size, fout);
【讨论】:
非常感谢!我进行了更改,它现在确实翻转了图片。但是仍然存在打印的image_size为0的问题。另外,我检查了BMP文件格式,它确实将像素保存为RGB 您的写入函数不使用二进制标志。按照更新答案中的示例进行操作。这主要是为了锻炼。已经有图书馆可以读取位图。标准格式以 BGR 格式写入。以上是关于C 中的 .BMP 文件 - DIB 标头返回图像大小 0,即使 BMP 标头返回文件大小的主要内容,如果未能解决你的问题,请参考以下文章