diverse Fragen zu Bildformat einlesen



  • dirkski schrieb:

    Das Problem: bfType ist nur 16Bit breit. Da das hier eine 64Bit-Maschine
    ist fügt der Compiler nach BMPFileHeader.bfType noch 2 Bytes ein um die
    nachfolgenden 32-Bit-Werte auf eine 4 Byte-Grenze auszurichten. Dumm gelaufen....

    Nur kurzer Einschub / Ergänzung:
    Für MS VS gibt es die Präprozessordirektive pack (https://msdn.microsoft.com/de-de/library/2e70t5y1.aspx), dass keine Speicherausrichtung ausgeführt wird. Für GCC gibts das auch siehe https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Structure-Packing-Pragmas.html

    Daher hatte ich das damals so gelöst:

    // pragma pack wird hier benötigt, dass der Compiler keine Speicherausrichtung an den Strukturen vornimmt
    #pragma pack(push, 1)
            /// <summary>Beschreibt die Datenstruktur des Dateikopfes.</summary>
            struct FileHeader {
              uint8_t m_type[2];  // BM
              uint32_t m_size;    // Dateigröße (unzuverlässig)
              uint32_t m_reserved;
              uint32_t m_offset;  // Offset der Bilddaten in Bytes von Beginn der Datei
            };
    
            /// <summary>Beschreibt die Datenstruktur die Informationen zum Bitmap enthält.</summary>
            struct InfoHeader {
              uint32_t m_size;    // 40 Byte = Größe des InfoHeader
              uint32_t m_width;   // Breite der Bitmap in Pixel.
              uint32_t m_height;  // Höhe der Bitmap in Pixel.
              uint16_t m_planes;  // 1
              uint16_t m_bpp;     // 1, 4, 8, 16, 24
              uint32_t m_compression; // 0 = unkompremiert
              // andere Datenfelder interessieren aktuell nicht
            };
    
            /// <summary>Beschreibt die Datenstruktur die ein Pixel beschreibt.</summary>
            struct Color {
              uint8_t m_blue;
              uint8_t m_green;
              uint8_t m_red;         
              //uint8_t m_reserved;
            };
    // Die pop Anweisung dient dazu, dass der Compiler ab hier wird Speicherausrichtungen durchführen kann
    #pragma pack(pop)
    

    Ich hab damals die komplette Datei in ein std::vector<char> gelesen und hab die Speicherbereiche dann mit reinterpret_cast<> auf meine Strukturen gecastet.

    const Bitmap::FileHeader& fileHeader = *reinterpret_cast<const FileHeader*>(&m_data[0]);
    

    Einschub Ende



  • sebi707 schrieb:

    Ich hatte auch eher an sowas gedacht:

    Ja, vielen Dank für die Anregung 🙂

    So hatte ich es auch vorhin mit std::istream & is; als member,
    hatte irgendwie nicht geklappt und ich habe es etwas verblüfft wieder rückgängig gemacht. Die Klasse ist halt nur ein Test deshalb ist alles zusammen. Werde ich gleich nochmal versuchen, auch das splitten der Klasse.

    Beim Doku-lesen haben sich bei mir aber noch weitere Fragen bzgl. streams aufgetan wo ich eigentlich einen neuen Thread aufmachen wollte...



  • Gast3 schrieb:

    Naja, ist ein erster Versuch zum einlesen, hab drauf los gebastelt.
    Den operator>> kann ich ja eh nur innerhalb der Klasse verwenden.

    "erster Versuch" UND "eh nur innerhalb" macht es aber trotzdem nicht
    richtiger eine BMP-Datei als Stream abzubilden - man kann sie mittels eines Stream lesen - sie ist aber selber keiner, und damit ist das Design schlecht - auch wenn es technisch funktioniert

    Ja, hast ja recht 😃 War nur zum testen. Ich will das jetzt eh umbauen. Ich habe noch ein paar Fragen und dafür muss ich jetzt eh ein Minimalbeispiel basteln, allerdings besser in einen neuen Thread...



  • jb schrieb:

    dirkski schrieb:

    Das Problem: bfType ist nur 16Bit breit. Da das hier eine 64Bit-Maschine
    ist fügt der Compiler nach BMPFileHeader.bfType noch 2 Bytes ein um die
    nachfolgenden 32-Bit-Werte auf eine 4 Byte-Grenze auszurichten. Dumm gelaufen....

    Nur kurzer Einschub / Ergänzung:
    Für MS VS gibt es die Präprozessordirektive pack (https://msdn.microsoft.com/de-de/library/2e70t5y1.aspx), dass keine Speicherausrichtung ausgeführt wird. Für GCC gibts das auch siehe https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Structure-Packing-Pragmas.html

    Daher hatte ich das damals so gelöst:
    [...]

    Einschub Ende

    Danke dir für den Tip, leider nicht portabel. Und nur für das einlesen/schreiben die ganze Zeit mit nicht ausgerichteten Daten arbeiten. Ich weiß nicht, hängt halt von der Anwendung ab. Für dieses Beispiel würde es allerdings reichen.



  • Und nur für das einlesen/schreiben die ganze Zeit mit nicht ausgerichteten Daten arbeiten.

    portabel solange die Hardware unaligned access kann - also nur problematisch unter ARM, MIPS?, SPARC usw. - aber du hast recht - sollte man vermeiden



  • dirkski schrieb:

    Danke dir für den Tip, leider nicht portabel. Und nur für das einlesen/schreiben die ganze Zeit mit nicht ausgerichteten Daten arbeiten. Ich weiß nicht, hängt halt von der Anwendung ab. Für dieses Beispiel würde es allerdings reichen.

    Sehr gerne. Ich hab eben einfach die Ganze Datei als ein char-Array eingelesen und habe dann den Speicherblock je nach Dateiformat interpretiert. Somit führe ich keine Schreibe/Lese-Operation über diese nicht ausgerichteter Klassenstruktur aus - sondern interpretiere damit nur den Speicherblock. Daher sollte das doch gehen und deine AFo erfüllen, oder?

    Gast3 schrieb:

    Und nur für das einlesen/schreiben die ganze Zeit mit nicht ausgerichteten Daten arbeiten.

    portabel solange die Hardware unaligned access kann - also nur problematisch unter ARM, MIPS?, SPARC usw. - aber du hast recht - sollte man vermeiden

    Ist vermutlich eine Basisfrage, aber warum nicht? Sinkt dadurch die Performance drastisch? Bzw. was sind die (möglichen) Auswirkungen?


  • Mod

    jb schrieb:

    Ist vermutlich eine Basisfrage, aber warum nicht? Sinkt dadurch die Performance drastisch?

    Ja, die Zugriffszeit kann sich durchaus mehr als verdoppeln. Das bedeutet natürlich "nur" das Mehrfache von so gut wie nichts. Das heißt, ein Programm in dem das nur ein paar Mal gemacht wird, merkt davon nichts, wohingegen ein Programm, das auf diese Struktur in der innersten Schleife zugreift, deutlich langsamer werden kann.



  • Ist vermutlich eine Basisfrage, aber warum nicht? Sinkt dadurch die Performance drastisch? Bzw. was sind die (möglichen) Auswirkungen?

    Platformen die unaligned access koennen sind dabei meist langsamer(oder auch viel langsamer) - hier kann der Zugriff aligned sein

    Platformen die unaligned access nicht koennen liefern z.B. Hardware-Exceptions, falsche Werte usw. - hier MUSS der Zugriff aligned sein



  • jb schrieb:

    dirkski schrieb:

    Danke dir für den Tip, leider nicht portabel. Und nur für das einlesen/schreiben die ganze Zeit mit nicht ausgerichteten Daten arbeiten. Ich weiß nicht, hängt halt von der Anwendung ab. Für dieses Beispiel würde es allerdings reichen.

    Sehr gerne. Ich hab eben einfach die Ganze Datei als ein char-Array eingelesen und habe dann den Speicherblock je nach Dateiformat interpretiert. Somit führe ich keine Schreibe/Lese-Operation über diese nicht ausgerichteter Klassenstruktur aus - sondern interpretiere damit nur den Speicherblock. Daher sollte das doch gehen und deine AFo erfüllen, oder?

    Ja, für den speziellen Fall des TE passt das schon. Ich selbst hab aber eher nach einer Lösung gesucht um beliebige structs zu füllen.
    Der Hintergedanke war halt das bei EVA die Hauptarbeit bei der Verarbeitung liegt, nicht beim schreiben/lesen der Daten. Und der Compiler wird ja einen Grund haben das er den Speicher nach dem Datentyp ausrichtet (Geschwindikeit z.B.).

    Aber trotzdem Danke für den Lösungsansatz



  • Hallo, ich habe ein Problem mit einer Test-bmp und dem Wikipedia-Artikel zu BMP.

    Habe hier eine Datei test.bmp, sie ist 306 Bytes groß.

    Der BITMAPFILEHEADER ist 14 Bytes (fest). Dort ist bfSize = 306 (was die korrekte Dateigröße ist) und bfOffBits = 54 (was auch korrekt ist).

    Dann kommt der BITMAPINFOHEADER mit member biSize = 40. Ist auch richtig, 40 + 14 = 54 (== Anfang der Bilddaten). Auch biSizeImage = 252 ist richtig, weil 252 + 54 = 306 (Die Dateigröße).

    Der Header der Datei sieht so aus (vollständig):

    // BITMAPFILEHEADER
     bfType = 19778, bfSize = 306, bfReserved = 0, bfOffBits = 54
    // BITMAPINFOHEADER
     biSize: 40, biWidth: 9, biHeight: 9, biPlanes: 1, biBitCount: 24, biCompression: 0 (RGB), biSizeImage: 252,
     biXPelsPerMeter: 3779, biYPelsPerMeter: 3779, biClrUsed: 0, biClrImportant: 0 (errorState: 0, is.tellg(): 54, is.good(): true)
    

    Wikipedia schrieb:

    Die Bilddaten beginnen am Offset bfOffBits. Die Größe der Bilddaten beträgt näherungsweise (gilt nur für durch 4 teilbare Bildbreiten) biWidth×biHeight×biBitCount/8 wenn biCompression=BI_RGB, ansonsten biSizeImage.

    Die Bilddaten werden Zeile für Zeile gespeichert. Wenn biHeight positiv ist, beginnen die Bilddaten mit der letzten und enden mit der ersten Bildzeile, ansonsten ist es umgekehrt. Bei BI_BITFIELDS und bei BI_RGB ist die Länge jeder Zeile ein Vielfaches von 4 Bytes und wird, falls erforderlich, mit Nullbytes aufgefüllt.

    Das weitere Format der Bilddaten hängt vom Wert des biCompression-Felds ab:

    *BI_BITFIELDS
    [...]
    *BI_RGB
    Jede Bildzeile ist durch rechtsseitiges Auffüllen mit Nullen auf ein ganzzahliges Vielfaches von 4 Bytes ausgerichtet.
    [...]
    24 bpp:
    Die Daten jedes Pixels bestehen aus jeweils einem Byte für den Blau-, Grün- und Rot-Kanal (in dieser Reihenfolge!).

    Es gilt also BI_RGB und 24bpp, biWidth ist 9:
    Einmal aufgerundet auf 12 Pixel breite: 12(breite) x 9(höhe) x 24(bpp) / 8(Bits/Byte) = 324

    Und nicht aufgerundet: 9 x 9 x 24 / 8 = 243

    Selbst ein Test mit 10x9x24/8 = 270 passt nicht...

    Tja, 324 ist zu groß, 243 zu klein. Was nun? Einfach biSizeImage nehmen wenn nicht 0?
    Nochmal WP:
    „Die Größe der Bilddaten beträgt näherungsweise (gilt nur für durch 4 teilbare Bildbreiten) biWidth×biHeight×biBitCount/8 wenn biCompression=BI_RGB, ansonsten biSizeImage.“

    Oder habe ich hier den Denkfehler? Soll es folgendes bedeuten: Wenn Bildbreite nicht durch 4 teilbar dann gilt biSizeImage? Liegt nahe, oder?
    Ich hatte es so verstanden das biSizeImage nur gilt wenn biCompression != BI_RGB... Ich glaube hier liegt mein Denkfehler...

    Hier noch den Hexdump des Bildes, ist ja nicht so groß:

    00000000  42 4d 32 01 00 00 00 00  00 00 36 00 00 00 28 00  |BM2.......6...(.|
    00000010  00 00 09 00 00 00 09 00  00 00 01 00 18 00 00 00  |................|
    00000020  00 00 fc 00 00 00 c3 0e  00 00 c3 0e 00 00 00 00  |................|
    00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000050  00 00 00 00 00 ff c3 c3  ff c3 c3 ff c3 c3 ff c3  |................|
    00000060  c3 ff c3 c3 ff c3 c3 ff  c3 c3 00 00 00 00 00 00  |................|
    00000070  00 ff c3 c3 ff c3 c3 ff  c3 c3 ff 00 00 ff c3 c3  |................|
    00000080  ff c3 c3 ff c3 c3 00 00  00 00 00 00 00 ff c3 c3  |................|
    00000090  ff c3 c3 ff c3 c3 ff 00  00 ff c3 c3 ff c3 c3 ff  |................|
    000000a0  c3 c3 00 00 00 00 00 00  00 ff c3 c3 ff 00 00 ff  |................|
    000000b0  00 00 ff 00 00 ff 00 00  ff 00 00 ff c3 c3 00 00  |................|
    000000c0  00 00 00 00 00 ff c3 c3  ff c3 c3 ff c3 c3 ff 00  |................|
    000000d0  00 ff c3 c3 ff c3 c3 ff  c3 c3 00 00 00 00 00 00  |................|
    000000e0  00 ff c3 c3 ff c3 c3 ff  c3 c3 ff 00 00 ff c3 c3  |................|
    000000f0  ff c3 c3 ff c3 c3 00 00  00 00 00 00 00 ff c3 c3  |................|
    00000100  ff c3 c3 ff c3 c3 ff c3  c3 ff c3 c3 ff c3 c3 ff  |................|
    00000110  c3 c3 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000130  00 00                                             |..|
    00000132
    

    Das Bild ist aus dem Paket docbook-xsl-stylesheets Release 3.7.1 Version 1.78.1+svn9743 File /usr/share/xml/docbook/stylesheet/nwalsh/1.78.1/slides/slidy/graphics/unfold.bmp auf OpenSuse 13.2.

    Oder habe ich sonst noch was übersehen?



  • Jede Zeile wird wieder auf einer durch 4 teilbaren Adresse begonnen, so daß dann pro Zeile ein Füllbyte angefügt wird: ((9 * 3) + 1) * 9 = 252.



  • Th69 schrieb:

    Jede Zeile wird wieder auf einer durch 4 teilbaren Adresse begonnen, so daß dann pro Zeile ein Füllbyte angefügt wird: ((9 * 3) + 1) * 9 = 252.

    Das könnte es natürlich auch sein. Danke 🙂

    Edit: Das ist es!

    „Jede Bildzeile ist durch rechtsseitiges Auffüllen mit Nullen auf ein ganzzahliges Vielfaches von 4 Bytes ausgerichtet.“

    Ich weiß jetzt nicht wie ich auf „Breite in Pixel muss durch 4 teilbar sein“ kam... Danke nochmal


Anmelden zum Antworten