diverse Fragen zu Bildformat einlesen
-
Oho, na vielen Dank
und für die weiteren Anmerkungen von sebi707 
Werde ich auf jeden Fall mal durcharbeiten und ausprobieren.Wollte nur kurz anmerken, das durch das Casten inzwischen auch beim ersten Pic die grundlegenden Wert korrekt dargestellt werden. Bei neuen Bildern, die ich mit einem aktuellen Programm zu bmp konvertiere sind dann wirklich alle Werte (jedenfalls die Eigenschaften des Bild und entsprechende Anmerkungen in der Tabelle) übereinstimmend.
-
sebi707 schrieb:
Wäre das Projekt ein C++ Projekt gewesen hätte ich dafür wohl irgendeine Klasse gebaut die binär Zeug lesen kann und den
operator>>überladen. Da hätte der gleiche Code danndata >> obj->storageID >> obj->objectFormat >> obj->protectionStatus >> obj->objectSize >> obj->thumbFormat >> obj->thumbSize >> ...oder so ausgesehen. Das angeben der richtigen Größe in C ist schon ziemlich nervig.
Hallo sebi707,
wenn man die Klasse nur zum auslesen braucht, sollte sie dann von std::basic_ifstream abgeleitet sein?Und was wenn sie zum manipulieren von Dateiinhalten gedacht ist,
von std::basic_fstream für I/O?Bin selbst nicht so firm in solchen Fragen, würde mir aber so in den Sinn kommen.
Einen schönen Abend noch
-
dirkski schrieb:
sebi707 schrieb:
Wäre das Projekt ein C++ Projekt gewesen hätte ich dafür wohl irgendeine Klasse gebaut die binär Zeug lesen kann und den
operator>>überladen. Da hätte der gleiche Code danndata >> obj->storageID >> obj->objectFormat >> obj->protectionStatus >> obj->objectSize >> obj->thumbFormat >> obj->thumbSize >> ...oder so ausgesehen. Das angeben der richtigen Größe in C ist schon ziemlich nervig.
Hallo sebi707,
wenn man die Klasse nur zum auslesen braucht, sollte sie dann von std::basic_ifstream abgeleitet sein?Und was wenn sie zum manipulieren von Dateiinhalten gedacht ist,
von std::basic_fstream für I/O?Bin selbst nicht so firm in solchen Fragen, würde mir aber so in den Sinn kommen.
Einen schönen Abend noch
Hmm, ich denk mir gerade das es eigentlich Blödsinn ist, oder?
Mann will ja manipulieren? Hmm, ist ein z.B. PNG ein stream? Ja, auch.
Im Bildbearbeitungsprogramm ist es nur noch ein Bild. Im 3d-Renderer eine Textur im Grafikspeicher. Wie gesagt ich tu mich mit solchen Fragen immer etwas schwer
-
Ich hätte es wohl gar nicht von einer der Standard Stream Klassen abgeleitet sondern stattdessen eine Referenz oder Pointer auf eine solche gespeichert, weil ich mir die ganzen Standard Operatoren nicht reinholen will. Ich würde dann einen
operator>>haben der nur ints (signed und unsigned in allen Größen) lesen kann und die dann immer binär mit .read aus dem Stream ließt. Wenn man möchte kann man dann noch Endianness behandeln.Und zu
fstreamoderiostream: Irgendwie hatte ich bisher noch nie das Bedürfnis eine Datei gleichzeitig lesen und schreiben zu können.
-
sebi707 schrieb:
Ich hätte es wohl gar nicht von einer der Standard Stream Klassen abgeleitet sondern stattdessen eine Referenz oder Pointer auf eine solche gespeichert, weil ich mir die ganzen Standard Operatoren nicht reinholen will. Ich würde dann einen
operator>>haben der nur ints (signed und unsigned in allen Größen) lesen kann und die dann immer binär mit .read aus dem Stream ließt. Wenn man möchte kann man dann noch Endianness behandeln.Hört sich gut an, werde mal sowas zum testen basteln. Hab ja schon angefangen, dabei sind ein paar Fragen aufgekommen die ich heute oder morgen mal in einen neuen Thread posten werde. Habe gerade ncht so viel Zeit...
sebi707 schrieb:
Und zu
fstreamoderiostream: Irgendwie hatte ich bisher noch nie das Bedürfnis eine Datei gleichzeitig lesen und schreiben zu können.Ok, ich bis jetzt auch nicht
Ich dachte zum manipulieren, aber lesen > manipulieren > schreiben geht auch mit 2 streams... Es war aber nur so eine Idee wo ich mir nicht so viele Gedanken gemacht habe...Edit: Ich habe (hatte) eine Klasse BMP_ifstream abgeleitet von std::ifstream.
Damit klappte es ganz gut, operator>> hab ich für uint32_t etc. (template) überladen, dann für die beiden structs BMPFileHeader und BMPInfoHeader.Jetzt habe ich das versucht nach deinen Vorschlag zu ändern, also Klasse
nicht abgeleitet und stattdessen std::ifstream als member (Referenz). Nur wie überlade ich dann den operator>> korrekt. Hab da jetzt eine Schere im Kopf....
-
Damit klappte es ganz gut, operator>> hab ich für uint32_t etc. (template) überladen, dann für die beiden structs BMPFileHeader und BMPInfoHeader.
es macht bei einem Stream Sinn mehrfach einen int zu lesen
also
ifs >> x;
ifs >> y;
ifs >> z;weil man ja immer weiter lesen kann - und der Stream-Pointer sich ja auch dann weiterbewegt
aber fuer einen BMP-Header der Fix immer am Anfang der Datei steht macht das irgendwie wenig Sinn
ifs >> bmp_header;
ifs >> bmp_header;
ifs >> bmp_header;wird so niemals jemand schreiben wollen
da muss der Stream zwangslauefig am Anfang stehen oder dort hin gesetzt werden und danach wieder an seinen alten Platz - hört sich so gar nicht nach "Stream" an
ich wuerde das lassen - eher Teile der BMP als Stream abstrahieren - aber nicht das ganze
-
Gast3 schrieb:
Damit klappte es ganz gut, operator>> hab ich für uint32_t etc. (template) überladen, dann für die beiden structs BMPFileHeader und BMPInfoHeader.
es macht bei einem Stream Sinn mehrfach einen int zu lesen
also
ifs >> x;
ifs >> y;
ifs >> z;weil man ja immer weiter lesen kann - und der Stream-Pointer sich ja auch dann weiterbewegt
aber fuer einen BMP-Header der Fix immer am Anfang der Datei steht macht das irgendwie wenig Sinn
ifs >> bmp_header;
ifs >> bmp_header;
ifs >> bmp_header;wird so niemals jemand schreiben wollen
da muss der Stream zwangslauefig am Anfang stehen oder dort hin gesetzt werden und danach wieder an seinen alten Platz - hört sich so gar nicht nach "Stream" an
ich wuerde das lassen - eher Teile der BMP als Stream abstrahieren - aber nicht das ganze
Naja, ist ein erster Versuch zum einlesen, hab drauf los gebastelt.
Den operator>> kann ich ja eh nur innerhalb der Klasse verwenden. Zumindest wüste ich nicht wie man den für int, long etc außerhalb der Klasse überladen kann... Im Augenblick benutze ich die nur innerhalb des Konstruktors.Also sowas (gekürzt):
class BMP_ifstream : std::ifstream { BMPFileHeader head; BMPInfoHeader info; // Keine Ahnung ob das richtig ist, hab noch nie was mit std::move() gemacht. Müsste dann nicht der original-ifstream ungültig sein? Wäre schlecht BMP_ifstream(std::ifstream && is) : std::ifstream(std::move(is)) { std::cerr << __func__ << "(std::ifstream && is): \n"; *this >> head >> info; debug(); } BMP_ifstream(std::string filename) : BMP_ifstream(std::ifstream(filename)) { } BMP_ifstream & operator>>(BMPFileHeader & head) { return (*this >> head.bfType >> head.bfSize >> head.bfReserved >> head.bfOffBits); } BMP_ifstream & operator>>(BMPInfoHeader & info) { return (*this >> info.biSize >> info.biWidth >> info.biHeight >> info.biPlanes >> info.biBitCount >> info.biCompression >> info.biSizeImage >> info.biXPelsPerMeter >> info.biYPelsPerMeter >> info.biClrUsed >> info.biClrImportant); } template <typename T> // für uint16_t, uint32_t usw... BMP_ifstream & operator>>(T & t) { // std::istream::sentry sentry(*this); // if (sentry) this->read( reinterpret_cast<char*>(&t), sizeof(T) ); return *this; } };Edit:
Ich hab mal alle Konstruktoren rausgeschmissen und dann sowas gemacht:class BMP_ifstream : public std::ifstream { BMP_ifstream(const char* filename,ios_base::openmode mode = ios_base::in) : std::ifstream(filename, mode) { std::cerr << __func__ << "(std::ifstream & is): \n"; *this >> head >> info; debug(); } ... };solange ich keine Ahnung von std::move habe

-
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
-
Ich hatte auch eher an sowas gedacht:
class BinaryReader { public: BinaryReader(std::istream& in) : m_in(in) { } template <class T, class = std::enable_if_t<std::is_integral<T>::value>> BinaryReader& operator>> (T& value) { m_in.read(reinterpret_cast<char*>(&value), sizeof(T)); return *this; } private: std::istream& m_in; }; class BMPImage { public: void readFromStream(std::istream& in) { // Read header BinaryReader b(in); b >> head.bfType >> head.bfSize >> head.bfReserved >> head.bfOffBits; b >> info.biSize >> info.biWidth >> info.biHeight >> info.biPlanes >> info.biBitCount >> info.biCompression >> info.biSizeImage >> info.biXPelsPerMeter >> info.biYPelsPerMeter >> info.biClrUsed >> info.biClrImportant; // TODO: Check header // TODO: Read data } private: BMPFileHeader head; BMPInfoHeader info; };Die
BinaryReaderKlasse kann man dann noch anderweitig nutzen wenn man binär Zeug aus einer Datei lesen möchte.
-
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.htmlDaher 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 funktioniertJa, 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.htmlDaher 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?
-
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) = 324Und 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 |..| 00000132Das 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.