2014-04-01 40 views
2

我有一個項目,涉及將一個.bmp文件讀入C程序,在其上放置一個掩碼,並將該掩碼的版本打印回不同的文件。我遇到問題的部分似乎是在文件中讀取的實際過程。我看到的第一個大紅旗是它一直以錯誤的分辨率閱讀。我搜索了很多,並看到一些腳本可以在.bmp文件中讀取,作爲各種問題的答案,但是使用這些腳本中的邏輯沒有幫助。C錯誤讀取.bmp文件的分辨率

主要問題似乎是,而不是閱讀我的教授給出的示例圖像上200×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); 
    } 
} 
+1

也許這是一個填充問題,你需要'#pragma pack'結構INFOHEADER'。兩個「短」成員跳入我的眼睛。 「HEADER.Size」的值是否正確? – alk

+1

這個程序似乎並沒有使用'width'和'height'被讀取 –

+0

所以我假設你在調試器中看着它們,對吧? –

回答

6

我可以看到很多的問題與您的代碼:

1.數據類型

對不同平臺的尺寸不一致,類型,如intshort可以有不同的尺寸。因此,int可能在一個平臺上是一種尺寸,在另一個平臺上可能是另一種尺寸。您可能需要使用精確大小的類型,如uint32_t

2.填充和對齊

該存儲在文件標頭被打包。你的結構是一致的。這意味着編譯器會在成員之間插入填充以確保成員始終對齊以獲得最佳內存訪問權限。

有多種處理方法。你可以聲明你的結構被打包。那會讓你如此,但看到下一點。

3字節序

如果你正在讀大端系統上的Windows位圖,你從小尾數數據文件以大端數據爲您的系統中轉換。

4. xResolution,yResolution是錯誤的構件

這些意在表示像素的物理尺寸。實際上它們很少被指定。你的意思是讀WidthHeight

5. VLA(GAH!)

正在使用的可變長度數組:struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution]。這很容易導致大型位圖的堆棧溢出。你真的需要爲像素數組使用動態分配的內存。


我該如何處理這些問題?

  • 使用精確大小的類型。
  • 聲明壓縮結構。
  • 從文件中讀取結構,然後在需要時執行endian校正。
  • malloc分配像素數組。
+0

我該如何去改變代碼中文件的字節順序? – ZapTap

+1

堆棧溢出中有一百萬個問題涉及端口交換。 –

0

我懷疑你混淆了一些字段。 看着http://en.wikipedia.org/wiki/BMP_file_format後,我覺得不是這個

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

你真正的意思是:

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

雖然這讀取似乎有點更好的值,他們仍然關閉 - 任何想法,我可能會失蹤? – ZapTap

+2

是的,閱讀Nisse的回答。 –

5

類型intshort等只能保證有一定的最小尺寸。它們可以根據不同的實現而不同即使我們假設intshort分別是4個和2個八位字節,在讀寫結構時仍會遇到問題。

例如:

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

'x << 8UL'不會將'x'提升爲unsigned long,所以這段代碼不起作用(除非'int'和'long'是相同的大小)。你可以做'((unsigned long)buffer [3])<< 8',或者最好是我,'buffer [3] * 0x100UL' –