Hexadezimal aus Binär-Datei lesen



  • Hallo,
    ich hoffe mir kann jemand von euch helfen!
    Nun zu meinem Problem:
    Ich will aus einer BMP-Datei die Bilddimensionen auslesen.
    (wo das im BMP-Header steht, weiß ich).
    Allerdings weiß ich nicht, wie ich diese aus der Datei rauskriege.
    Ich arbeite unter WinXP und compiliere mit Borland Builder 6., bin c++Anfänger, jedoch mit C ganz gut vertraut.
    In anderen Themen hab ich leider auch nix dazu finden können :(.

    Hier mal meine bisher erfolglosen Quelltextzeilen

    #include <stdio.h>
    #include <fstream>
    using namespace std;
    ...
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    int breite=0, laenge=0;
    
    if ( OpenPic->Execute() )
    	{
    	AnsiString FileName=OpenPic->FileName;
        ifstream BildFile;
        char nix[36]; //meine dummy-zeile
    
        BildFile.open(FileName.c_str(),ios::binary);
        BildFile.get(nix,36);
        BildFile.get(nix,4);//hier soll der bereich eingelesen werden, der im
                            //Header die breite beschreibt
        sscanf(nix,"%lx",&breite);
        BildFile.get(nix,4);//hier soll der bereich eingelesen werden, der im
                           //Header die breite beschreibt
        sscanf(nix,"%lx",&laenge);
        BildFile.close(); //unnuetz, hab ich mir aber so angewoehnt
        }
    Form1->Edit1->Text=IntToStr(breite); //ist immer null
    Form1->Edit2->Text=IntToStr(laenge); //ist immer null
    }
    

    Vielen Dank für eure Hilfe!!
    DerHorst



  • In dem Bitmap-Header stehen die Daten binär - das heißt, für den Menschen nicht lesbar. Dafür kannst du sie direkt per read() oder get() in deine int-Variablen reinpacken.

    PS: Anstelle der dummy-get() kannst du auch mit seekg() an die richtige Stelle springen.



  • @ cStoll: Danke für die Hinweise, aber genau bei:

    CStoll schrieb:

    In dem Bitmap-Header stehen die Daten binär - das heißt, für den Menschen nicht lesbar. Dafür kannst du sie direkt per read() oder get() in deine int-Variablen reinpacken.

    liegt mein Problem.
    Mir ist völlig unklar, wie ich das machen soll.

    Ich muß ja irgendwie "sagen", was er wie einlesen soll.
    Wird etwa automatisch das Ende einer Zahl erkannt?
    Habs mit:

    AnsiString FileName=OpenPic->FileName;
        ifstream BildFile;
        char zeile[2];
        BildFile.seekg(36);      //sprung zu position 36
        BildFile.get(zeile,2);   //einlesen der naechsten 2 - was auch immer -als  
                                 //zeile
        int a=0;
        sscanf(zeile,"%lx",&a);
    

    vesucht, klappt nicht 😕

    Danke für die Hilfe,
    DerHorst



  • DerHorst schrieb:

    Ich muß ja irgendwie "sagen", was er wie einlesen soll.
    Wird etwa automatisch das Ende einer Zahl erkannt?

    Nein, das nicht, aber in der BMP-Spezifikation sollte drin stehen, wie groß das Element ist.

    Das Auslesen selber klappt dann mit:

    int breite;
    BildFile.read(&breite,4);
    

    Da brauchst du kein zusätzliches sscanf(), um die Daten für deine Anwendung umwandeln zu müssen.

    (sscanf() dient dazu, Eingaben in Textform so umzuwandeln, daß der Computer damit weiterrechnen kann - du hast deine Eingabedaten aber schon im richtigen Format für die Verarbeitung vorliegen)



  • Nur leider kappt das nicht, da beim Aufruf von

    int breite=0;
    BildFile.read(&breite,4);
    

    die Fehlermeldung:
    "Konvertierung von 'int *' nach 'char *' nicht möglich!
    'char *' erwartet, 'int *'erhalten"

    Wenn ich dann wieder

    char breite;
    BildFile.read(&breite,4);
    

    versuche, kommt nix sinnvolles bei raus (<char> breite enthält nur Zeilenumbrüche).



  • Da die Funktion 'Read' nicht weiß, was sie einlesen soll, erwartet sie einfach einen 'char *'. Da du als Anwender der Funktion aber weißt, das an der entsprechenden Stelle ein int (4 Byte) stehen, mußt du einfach den Zeiger entsprechend casten:

    int breite=0;
    BildFile.read(static_cast<char *>(&breite), sizeof(int));
    

    So einfach ist das...



  • Na, in dem Fall ist wohl tatsächlich ein Cast angebracht (und notwendig):

    BildFile.read(reinterpret_cast<char*>(&breite),4);
    

    Alternativ kannst du den int-Wert auch aus den vier Bytes zusammenrechnen:

    unsigned char data[4];
    BildFile.read(data,4);
    breite = data[0]*0x01000000 + data[1]*0x00010000 + data[2]*0x00000100 + data[3];
    

    (eventuell sind die indizes auch andersherum - das hängt mit big-endian/little-endian zusammen)



  • In windows.h (genauer in wingdi.h) stehen die Bitmapheader-Strukturen bereit.
    Du musst Dir bloss Instanzen davon anlegen und den Bitmapheader deiner Bitmap da einlesen.
    So bekommst Du bequemen Zugang zu allen Infos.



  • @CStoll:
    funktioniert beides nicht. komisch komisch

    @gerie: sorry, was meinst du?
    ich hab da die :
    typedef struct tagBITMAP
    {...}
    gefunden, womit leider noch immer das Problem des einlesens bleibt.



  • DerHorst schrieb:

    @CStoll:
    funktioniert beides nicht. komisch komisch

    Wie drückt sich dieses "funktioniert nicht" in Worten aus?



  • es werden konstanten geliefert, egal wo ich mit seekg "hingehe".
    Wenn ich die Indizes in umgekehrter Reihenfolge verwende, wird immer 16 als ergebnis geliefert (bei der Additionsvariante)
    Bei der Cast-variante werden auch nur Zahlen geliefert, die einer Bereichsüberschreitung nahe kommen.
    Ich habe es auch schon so versucht:

    unsigned char data[50];
    reite = data[i]*0x01000000 + data[j]*0x00010000 + data[k]*0x00000100 + data[l];
    //mit: int i,j,k,l
    //gleiches Resultat

    komisch wa?



  • So wie 'gerie' geschrieben hat, ist es der einfachste Weg direkt die Bitmap-Struktur(en) zu nutzen:

    struct tagBITMAPFILEHEADER bmFile;
    struct tagBITMAPINFOHEADER bmInfo;
    
    BildFile.read(reinterpret_cast<char*>(&bmFile), sizeof(struct tagBITMAPFILEHEADER));
    BildFile.read(reinterpret_cast<char*>(&bmInfo), sizeof(struct tagBITMAPINFOHEADER));
    

    Nun sollten in den entsprechenden Membern von 'bmInfo' die richtigen Werte stehen, z.b. bmInfo.biWidth oder bmInfo.biHeight.

    Am besten du schaust mal in die MSDN-Hilfe unter 'Bitmap Storage'.

    P.S. Ich komme rechnerisch nur auf 18 Bytes (statt 36) zum Auslesen der Breite (und Höhe)...



  • DerHorst schrieb:

    es werden konstanten geliefert, egal wo ich mit seekg "hingehe".

    Häh?

    Wenn ich die Indizes in umgekehrter Reihenfolge verwende, wird immer 16 als ergebnis geliefert (bei der Additionsvariante)
    Bei der Cast-variante werden auch nur Zahlen geliefert, die einer Bereichsüberschreitung nahe kommen.

    Bist du sicher, daß du an der richtigen Stelle gelandet bist?
    Vielleicht solltest du dir mal ansehen, wie der BMP Header aufgebaut ist (bei Offset 36 bist du mitten in der Größenangabe für die Bilddaten).



  • Th hats gezeigt
    Hier nochmal:

    #include<fstream> 
    
    using namespace std;
    #include <windows.h>
    
    int main() 
    { 
    
        BITMAPFILEHEADER eins;
        BITMAPINFOHEADER zwei;
        ifstream ein;
        ein.open("c:\\tests.bmp", ios_base::binary);
        ein.read((char*)&eins, sizeof BITMAPFILEHEADER);
        ein.read((char*)&zwei, sizeof BITMAPINFOHEADER);      
       //...
    }
    


  • Vielleicht hilft dir ja ein lauffähiges Beispiel weiter:

    #include <fstream>
    #include <iostream>
    
    using namespace std;
    
    typedef long int32;
    
    void bmpWidthAndHeight(const char *path)
    {
        int32 width, height;
    
        ifstream bmp(path);
        bmp.seekg(0x12);
        bmp.read(reinterpret_cast<char*>(&width), 4);
        bmp.read(reinterpret_cast<char*>(&height), 4);
        bmp.close();
    
        cout << "width:\t" << width << "\nheight\t" << height << endl;
    }
    
    int main()
    {
        bmpWidthAndHeight("test.bmp");
    }
    

    Um die Header komplett auszulesen, solltest Du aber, wie schon erwähnt wurde, Bitmap Strukturen aus dem MSDN, wie zum Beispiel BITMAPINFOHEADER verwenden.



  • Konstruktör schrieb:

    Vielleicht hilft dir ja ein lauffähiges Beispiel weiter:[cpp]#include <fstream>

    Jedenfalls ist es nicht sinnvoll eine Bitmap im Textmodus zu öffnen.



  • @ TH & CStoll:

    P.S. Ich komme rechnerisch nur auf 18 Bytes (statt 36) zum Auslesen der Breite (und Höhe)...

    Jo, da habt ihr Recht, das war eines dieser vielen Vertsändnisprobleme, die ich beim Handling mit BMP-Dateien hatte/habe - gruß an die Zahlensysteme 😉

    und:
    Juchhuu, hab es hinbekommen !!!
    Dank euch allen!!
    Wie meine Abendlektüre aussieht, is ja klar:

    Bitmap Strukturen aus dem MSDN

    Ich mach es jetzt mi Variante 2 - Variante 1 hat nicht hingehauen.

    struct tagBITMAPFILEHEADER bmFile;
    struct tagBITMAPINFOHEADER bmInfo;
    ...
    if ( OpenPic->Execute() )
    	{
    	AnsiString FileName=OpenPic->FileName;
        ifstream BildFile;
        BildFile.open(FileName.c_str(), ios::binary);
    //Variante 1
    /*
    	BildFile.read(reinterpret_cast<char*>(&bmFile), sizeof(struct tagBITMAPFILEHEADER));
    	BildFile.read(reinterpret_cast<char*>(&bmInfo), sizeof(struct tagBITMAPINFOHEADER));
    */
    //Variante 2
        BildFile.seekg(0x12);
        BildFile.read(reinterpret_cast<char*>(&width), 4);
        BildFile.read(reinterpret_cast<char*>(&height), 4);
    
        BildFile.close(); //unnuetz, hab ich mir aber so angewoehnt
        }
    
    Form1->Edit1->Text=IntToStr(width);
    Form1->Edit2->Text=IntToStr(height);
    Form1->Edit3->Text=IntToStr(bmInfo.biWidth);
    Form1->Edit4->Text=IntToStr(bmInfo.biHeight);
    

    jetzt gibts Bier - auf euch!!

    DerHorst 😃



  • Du solltest das bitmap file parsen und nicht einfach an einem fixen offset was rauslesen. Also erst header 'BM' checken, dann size vom BITMAPFILEHEADER (bfSize) checken, dann BITMAPFILEHEADER lesen, dann size vom BITMAPINFOHEADER (biSize) checken, dann BITMAPINFOHEADER lesen. Dann biCompression, biPlanes, biBitCount etc. checken was für ein Format das ist und ob du damit was anfangen kannset etc., und dann über bfOffBits die Bilddaten suchen und lesen.

    Am einfachsten und schnellsten (sowohl beim Programmieren als auch in der Ausführung) ist es aber immernoch wenn du die ganze Daten mit einem einzigen riesen read in den Speicher holst und dann alles checkst/interpretierst.


Anmelden zum Antworten