C误读.bmp文件的解析

时间:2014-04-01 19:52:54

标签: c gcc bmp

我有一个项目,涉及将.bmp文件读入C程序,在其上放置一个掩码,然后将带有掩码的版本打印回另一个文件。我遇到问题的部分似乎是在文件中读取的实际过程。我看到的第一个大红旗是它继续以错误的分辨率阅读。我已经搜索了很多,看到一些脚本在.bmp文件中读取作为各种问题的答案,但使用这些脚本中的逻辑并没有帮助。

主要问题似乎是,不是在我教授给出的示例图像中读取200 x 300的正确尺寸,而是读入13107200 x 65536.但是,如果我要包含代码的一部分,打印到不同的文件,您会看到输出文件具有适当的分辨率。这告诉我,我可能正确地阅读了信息,但没有以我认为的方式存储它。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct HEADER {
    unsigned short int Type;        // Magic indentifier
    unsigned int Size;              // File size in bytes
    unsigned short int Reserved1, Reserved2;
    unsigned int Offset;            // Offset to data (in B)
} Header;                           // -- 14 Bytes

struct INFOHEADER {
    unsigned int Size;              // Header size in bytes
    int Width, Height;              // Width / height of image
    unsigned short int Planes;      // Number of colour planes
    unsigned short int Bits;        // Bits per pixel
    unsigned int Compression;       // Compression type
    unsigned int ImageSize;         // Image size in bytes
    int xResolution, yResolution;   // Pixels per meter
    unsigned int Colors;            // Number of colors
    unsigned int ImportantColors;   // Important colors
} InfoHeader;                       // -- 40 Bytes

struct PIXEL {
    unsigned char Red, Green, Blue; // Intensity of Red, Green, and Blue
};                                  // -- 3 Bytes

int getFileSize(FILE *input);
int getHexVal(FILE *input);
struct HEADER *getHeader(FILE *input);
struct INFOHEADER *getInfoHeader(FILE *input);
struct PIXEL *getPixel(FILE *input, struct PIXEL *loc);
struct HEADER *printHeader(FILE *output);
struct INFOHEADER *printInfoHeader(FILE *output);
struct PIXEL *printPixel(FILE *output, struct PIXEL *loc);

int main(int argc, char const *argv[]) {
    if (argc == 3) {
         if (!strcmp(argv[1], argv[2])) {
            printf("The input and output file must be different. Please try again.\n");
            return 1;
        }

        // char Matrix[3][3] =
        // { {  0, -1,  0 },
        //   { -1,  4, -1 },
        //   {  0, -1,  0 }
        // };

        FILE *input = fopen(argv[1], "rb");
        if (!input) return 1;
        int i, j;
        // getHeader(input);
        fread(&Header, sizeof(struct HEADER), 1, input);
        if (Header.Type != 0x4D42) {
            printf("The specified input file was not a bitmap. Please try again.");
            fclose(input);
            return 1;
        }
        // getInfoHeader(input);
        fread(&InfoHeader, sizeof(struct INFOHEADER), 1, input);
        fseek(input, Header.Offset, SEEK_SET);
        struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];

        printf("%d %d\n", InfoHeader.Width, InfoHeader.Height);
        for (i = 0; i < InfoHeader.Width; i++) {
            for (j = 0; j < InfoHeader.Height; j++) {
                getPixel(input, arr[i] + j);
                printf("%d %d %d\n", arr[i][j].Red, arr[i][j].Green, arr[i][j].Blue);
            }
        }
        fclose(input);
    }
}

3 个答案:

答案 0 :(得分:6)

我可以看到您的代码存在很多问题:

<强> 1。数据类型的大小不一致

在不同的平台上,intshort等类型可以有不同的大小。因此,int可能在一个平台上是一个大小,在另一个平台上可能是另一个大小。您可能需要使用精确大小的类型,例如uint32_t

<强> 2。填充和对齐

存储在文件中的标题已打包。你的结构是对齐的。这意味着编译器在成员之间插入填充以确保成员始终对齐以获得最佳内存访问。

有多种方法可以解决这个问题。您可以声明要打包的结构。这会让你到目前为止,但看到下一点。

第3。字节序

如果您正在大端系统上读取Windows位图,则可以将文件中的小端数据转换为系统的大端数据。

<强> 4。 xResolution,yResolution是错误的成员

这些用于表示像素的物理尺寸。在实践中,它们很少被指定。您打算阅读WidthHeight

<强> 5。 VLA(gah!)

您正在使用可变长度数组:struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution]。这很容易导致大位图的堆栈溢出。您确实需要为像素阵列使用动态分配的内存。


我如何处理这些问题?

  • 使用精确尺寸的类型。
  • 声明打包的结构。
  • 从文件中读取结构,然后根据需要执行字节序更正。
  • 使用malloc
  • 分配像素阵列

答案 1 :(得分:5)

类型intshort等仅保证具有特定的最小尺寸。它们可以根据不同的实现而变化即使我们假设intshort分别是四个和两个八位字节,在阅读和编写结构时仍然会遇到问题。

例如:

struct HEADER {
  unsigned short int Type;
  unsigned int Size;
  unsigned short int Reserved1, Reserved2;
  unsigned int Offset;
} Header;

为了使Size与处理器适当对齐,编译器将(通常)在TypeSize之间插入填充,将Size置于偏移+4处为+2(假设上述尺寸)。

读取(和写入)二进制格式的最佳方法是将文件读入unsigned char *缓冲区,然后从那里提取字段。例如

unsigned long Size = buffer[2] +
                     buffer[3] * 0x100UL +
                     buffer[4] * 0x10000UL +
                     buffer[5] * 0x1000000UL;

或类似。

答案 2 :(得分:0)

我怀疑你混淆了一些领域。 看了http://en.wikipedia.org/wiki/BMP_file_format之后,我想而不是这个

 struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution];

你真的是这个意思:

 struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];