2017-09-28 6 views
1

Hintergrund:
Ich möchte ein BMP (unkomprimiertes 24 RGB) Bild von einem Dateinamen zum anderen kopieren. Ich verwende den Mingw-Compiler von TDM-GCC (Version 4.9.2, 32 Bit, SJLJ), der mit Codeblocks ausgeliefert wird.Kopieren eines BMP in c

Problem:
Programm funktioniert für Schwarzweißbilder und einfache Farbbilder, aber nicht komplizierte Farbbilder. Bitte überprüfen Sie die angehängten Bilder. Ich hatte nicht genug Ansehen, um die anderen Bilder zu posten, also habe ich versucht, die 2 relevantesten zu posten. Das Programm kann das Lennabild nicht kopieren. Was ist die Ursache für dieses Verhalten?

Code:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#pragma pack(1) 

/* The following is to access the DIB information 
https://msdn.microsoft.com/en-us/library/cc230309.aspx 
https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx 
https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx */ 

typedef uint8_t BYTE; 
typedef uint32_t DWORD; 
typedef int32_t LONG; 
typedef uint16_t WORD; 

typedef struct 
{ 
    WORD bfType; 
    DWORD bfSize; 
    WORD bfReserved1; 
    WORD bfReserved2; 
    DWORD bfOffBits; 
}BITMAPFILEHEADER; 

typedef struct 
{ 
    DWORD biSize; 
    LONG biWidth; 
    LONG biHeight; 
    WORD biPlanes; 
    WORD biBitCount; 
    DWORD biCompression; 
    DWORD biSizeImage; 
    LONG biXPelsPerMeter; 
    LONG biYPelsPerMeter; 
    DWORD biClrUsed; 
    DWORD biClrImportant; 
}BITMAPINFOHEADER; 


typedef struct 
{ 
    BYTE rgbtBlue; 
    BYTE rgbtGreen; 
    BYTE rgbtRed; 
}RGBTRIPLE; 

int main(void) 
{ 
    char *infile = "testin.bmp"; 
    char *outfile = "testout.bmp"; 

    FILE *inptr = fopen(infile, "r"); 
    if (inptr == NULL) 
    { 
     fprintf(stderr, "Could not open %s.\n", infile); 
     return 2; 
    } 

    FILE *outptr = fopen(outfile, "w"); 
    if (outptr == NULL) 
    { 
     fclose(inptr); 
     fprintf(stderr, "Could not create %s.\n", outfile); 
     return 3; 
    } 

    BITMAPFILEHEADER bf; 
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); 

    BITMAPINFOHEADER bi; 
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); 

    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr); 

    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr); 

    int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; 

    int i, j, k, biHeight; 

    for(i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) 
    { 
     for(j = 0; j < bi.biWidth; j++) 
     { 
     RGBTRIPLE triple; 

     fread(&triple, sizeof(RGBTRIPLE), 1, inptr); 

     fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr); 
     } 
    } 

    fseek(inptr, padding, SEEK_CUR); 

    for(k = 0; k < padding; k++) 
    { 
     fputc(0x00, outptr); 
    } 

fclose(inptr); 

fclose(outptr); 

return 0; 

} 

Eingangsbild:

input image

Ausgangsbild:

output image

+0

Es wäre ein Problem in MSC sein - etwa gcc auf Windows Ich bin nicht sicher. Versuchen Sie jedoch bitte 'fopen (infile," rb ")' und 'fopen (outfile," wb ")' und melden Sie, ob sich etwas ändert. (Im besten Fall behebt es das Problem, zumindest ändert es nichts.) – Scheff

+0

Es gibt zahlreiche Probleme mit diesem Code. Du hast es nur durch einen Zufall funktioniert. Ist es Ihr Ziel, einfach die Bitmap zu kopieren? oder extrahiere Informationen Bitmap-Informationen? Wenn Sie nur kopieren möchten, gibt es eine viel einfachere Lösung. –

Antwort

0

I zur Verfügung gestellt, ein wenig mit den Daten fiddled und I bin ganz sur e, was passiert ist:

FILE *inptr = fopen(infile, "r"); 

und

FILE *outptr = fopen(outfile, "w"); 

öffnen Sie die Dateien im Textmodus. Dies ist ein spezielles Verhalten, das ich aus der C-API von Microsoft kenne, und es scheint, dass dies auch für Mingw TDM-GCC gilt (was ich zu glauben glaubte, bis die gedumpten Daten mich davon überzeugt hatten, dass mein Verdacht richtig war).

Die Datei I/O in Microsofts C API distinguishs zwischen Textmodus und Binär-Modus:

  • fopen(infile, "rt") und fopen(outfile, "wt") die Dateien im Textmodus öffnen.
  • fopen(infile, "rb") und fopen(outfile, "wb") öffnen Sie die Dateien im Binärmodus.
  • fopen(infile, "r") und fopen(outfile, "w") Standard im Textmodus.

Im Textmodus Lesen der Datei ersetzt alle Microsoft Zeilenende ("\r\n") von Unix Zeilenenden ("\n") sowie das Schreiben des Gegenteil tut ("\n" wird "\r\n").

Dies ist sinnvoll, wenn der Inhalt Klartext ist, aber es verdirbt wahrscheinlich Ausgabe von binärem Inhalt, wo ein Byte 0x0d eingefügt wird, wenn ein Byte 0x0a (egal mit welcher Bedeutung) im Datenstrom auftritt.

Um dies zu beweisen,

  1. ich Ihre Beispieldateien heruntergeladen (leider im PNG-Format hochgeladen)
  2. konvertiert die Dateien (zurück) zu 24-Bit-BMP (mit GIMP)
  3. einen hex -dump für jeden:

    $ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt 
    
    $ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt 
    
  4. geladen und schließlich IkW6FbN.bmp.txt und jnxpTwE.bmp.txt in WinMerge zum Vergleich.

Snapshot of WinMerge showing where wrong contents start in output file

Da die Momentaufnahme zeigt, haben sie die Eingabe- und Ausgabedatei identischen Inhalt für den ersten 14037 (0x36d5) Bytes. Dann enthält die Eingabedatei "versehentlich" drei Bytes 0a 0a 0a, wobei die Ausgabedatei stattdessen 0d 0a 0d 0a 0d 0a hat. Somit sind die jeweiligen ursprünglichen Pixel (und alle folgenden) beschädigt.

(Wie Sie vielleicht schon erraten, ist 0a der hexadezimale Wert des Zeilenvorschubzeichen '\n', 0d die eine der Wagenrücklauf '\r'.)

Btw. Die Ausgabedatei ist wahrscheinlich etwas länger als die Eingabedatei (aufgrund der eingefügten CR-Bytes). Dies kann von einem BMP-Viewer ignoriert werden, da der BMP-Header genau angibt, wie viele Bytes für das Rohbild benötigt werden (und die zusätzlichen Bytes werden einfach ignoriert).

Wie Sie bereits erkannt vielleicht haben Sie

Ihr Problem ändern sollten die fopen() Anrufe
FILE *inptr = fopen(infile, "rb"); 

und

FILE *outptr = fopen(outfile, "wb"); 

zu beheben.

Btw. Die C-APIs auf * x-Betriebssystemen (z. B. Linux) unterscheiden sich nicht im Text- und Binärmodus. Stattdessen wird die b einfach ignoriert (was hilfreich ist, um portablen Code zu schreiben).

Weiterführende Literatur: fopen, fopen_s on cppreference.com

+0

Vielen Dank für Ihre umfassende Antwort. Wie Sie beschrieben haben, wurde das Problem behoben. – ebmadrio