Fragen zu Windows Bitmap einlesen #2
-
lemon03 schrieb:
Also sorry, wollte nicht aufmüpfig erscheinen.
Tust du nicht! Höchstens eigenwillig, aber das ist bis zu einem bestimmten Grad würd' ich mal sagen sogar was gutes. Alles ohne nachzufragen einfach zu akzeptieren wäre nicht unbedingt ein Zeichen von Intelligenz

lemon03 schrieb:
Ich würde gerne noch mal auf den Funktionszeiger zurück kommen, ob das hier möglich und sinnvoll ist. Vorgestellt habe ich mir das mit sozusagen Pseudocode etwa so:
int (*make_dec)(std::vector <unsigned char> const, std::size_t); //hier const richtig? uint16_t m_uint16(std::vector <unsigned char> const &bmp_data, std::size_t pos) ...Das
constist schon richtig, aber das "&" fehlt noch - das gehört ja schliesslich mit zum Typ. Weswegen ich es auch immerType& nameschreibe und nichtType &namewie du
=>
int (*make_dec)(std::vector <unsigned char> const&, std::size_t);Nur bringt dir das nicht viel. Damit deklarierst du eine Variable namens "make_dec" die den Typ hat: Zeiger auf: Funktion mit Signatur:
int fun(std::vector <unsigned char> const&, std::size_t);Du brauchst aber nicht eine solche Variable, du brauchst den Typ. Damit du ihn später in dem
pair<>verwenden kannst. Also z.B. einfach:typedef int (*make_int_function_ptr)(std::vector <unsigned char> const&, std::size_t);lemon03 schrieb:
struct Head //Kopf { static const auto Size = 4; std::array <std::pair <std::string, |Typ?|>, Size> bType { { { "bfType", m_uint16 }, //uint16_t { "bfSize", m_uint32 }, //uint32_t { "bfReserved", m_uint32 }, { "bfOffbits", m_uint32 } } }; std::array <int, Size> data; } head;Also |Typ?| hier ist einfach:
make_int_function_ptr.
Allerdings stimmt der Typ von&m_uint16etc. nicht mitmake_int_function_ptrüberein (unterschiedlicher Returnwert).
Also Hilfsfunktionen.
Oder evtl. mit Lambdas:struct Head //Kopf { static const auto Size = 4; std::array<std::pair<std::string, make_int_function_ptr>, Size> bType = { { { "bfType", [](std::vector<unsigned char> const& d, size_t p) -> int { return m_uint16(d, p); } }, { "bfSize", [](std::vector<unsigned char> const& d, size_t p) -> int { return m_uint32(d, p); } }, { "bfReserved", [](std::vector<unsigned char> const& d, size_t p) -> int { return m_uint32(d, p); } }, { "bfOffbits", [](std::vector<unsigned char> const& d, size_t p) -> int { return m_uint32(d, p); } }, } }; std::array <int, Size> data; } head;lemon03 schrieb:
head.data.at(i) = Funktionsaufruf mit <head.bType.at(i).second>;Das wäre dann
head.data.at(i) = head.bType[i].second(theVectorWithTheData, theOffsetInThatVector);Aber merkst du 'was? Woher willst du
theOffsetInThatVectornehmen?Also vielleicht doch nicht so der optimale Ansatz.
Natürlich kann man das auch lösen - gibt mehrere Varianten wie man das ohne all zu grosse Änderungen umbauen kann so dass der Offset verfügbar ist.Die Frage ist aber, ob das den ganzen Aufwand wert ist?
Wieso nicht einfach eine Funktion der du nen
vectoroder auch einfach nen Zeiger auf dieunsigned chars übergibst, und die dir neBitmapHeaderstruct zurückgibt? Und einfach die ganzenmake_this,make_thatFunktionen der Reihe nach aufruft um das zu machen. Wäre vermutlich viel einfacher.
-
ps:
Einfacher Trick wenn man die Function-Pointer Syntax nicht mag:typedef int make_int_function(std::vector <unsigned char> const&, std::size_t); typedef make_int_function* make_int_function_ptr;(Oder alternativ zum 2. typedef dann einfach überall
make_int_function*stattmake_int_function_ptrschreiben.)
-
Aye, na klasse für die ausführlichen Anmerkungen.
Zum
const&, dies war auch mein erster Impuls, weil mir das in dem Sinn logisch erschien, wollte dann aber nachfragen. Ich schreibe das sonst immer so, weil ich den Operator(?) sonst nur von Referenzen kannte. Also beivoid func(int &val) { val ++; }wäre es doch der Wert und nicht der Typ, der per Referenz übergeben wird?
Zu dem Problem mit dem
offsetals Übergabe musste ich mir schon Gedanken machen, weil es noch mehrere Notwendigkeiten im Code geben wird, den aktuellen offset zu kennen.
Dies wollte ich durch einenstd::tuplelösen. Nur habe ich noch Probleme dieses in ein array zu packen. Vorgestellt hatte ich mir das so:struct Head //Kopf { static const auto Size = 4; std::array <std::tuple <std::string, |Typ?|, int>, Size> bType; std::make_tuple //?? { { { "bfType", m_uint16, 2 }, //uint16_t //anzahl bytes { "bfSize", m_uint32, 4 }, //uint32_t { "bfReserved", m_uint32, 4 }, { "bfOffbits", m_uint32, 4 } } }; std::array <int, Size> data; } head;Der aktuelle offset wäre dann die vorherigen addierten Werte. Oder gleich so:
struct Head //Kopf { static const auto Size = 4; std::array <std::tuple <std::size_t, std::string, |Typ?|>, Size> bType; std::make_tuple //?? { { { 0, "bfType", m_uint16 }, //offset//name//uint16_t { 2, "bfSize", m_uint32 }, //uint32_t { 6, "bfReserved", m_uint32 }, { 10, "bfOffbits", m_uint32 } } }; std::array <int, Size> data; } head;Das zweite wäre natürlich einfacher.
Zu dem Funktionszeiger selbst, den muss ich erst mal sacken lassen
Allerdings scheint Deine letzte Idee mitund die dir ne BitmapHeader struct zurückgibt?
ganz interessant. Kannst Du mir zeigen, wie in etwas Du dies gemeint hast?
-
lemon03 schrieb:
Aye, na klasse für die ausführlichen Anmerkungen.
Zum
const&, dies war auch mein erster Impuls, weil mir das in dem Sinn logisch erschien, wollte dann aber nachfragen. Ich schreibe das sonst immer so, weil ich den Operator(?)Ui, frag mich nicht wie das "&" an der Stelle heisst, aber vermutlich mal nicht Operator. Weil es da nicht als Operator verwendet wird, sondern als Teil des Typs.
lemon03 schrieb:
sonst nur von Referenzen kannte.
Ja eh. Der "&" an der Stelle gibt ja auch an dass der Typ des Parameters "Referenz auf T" ist. Also "
T&".lemon03 schrieb:
Also bei
void func(int &val) { val ++; }wäre es doch der Wert und nicht der Typ, der per Referenz übergeben wird?
Natürlich wird der Wert per Referenz übergeben.
valist in diesem Beispiel eine Referenz auf einenint.
Und daher ist der Typ vonvaleben nicht "int", sondern "int&".lemon03 schrieb:
Zu dem Problem mit dem
offsetals Übergabe musste ich mir schon Gedanken machen, weil es noch mehrere Notwendigkeiten im Code geben wird, den aktuellen offset zu kennen.
Dies wollte ich durch einenstd::tuplelösen. Nur habe ich noch Probleme dieses in ein array zu packen. Vorgestellt hatte ich mir das so:struct Head //Kopf { static const auto Size = 4; std::array <std::tuple <std::string, |Typ?|, int>, Size> bType; std::make_tuple //?? { { { "bfType", m_uint16, 2 }, //uint16_t //anzahl bytes { "bfSize", m_uint32, 4 }, //uint32_t { "bfReserved", m_uint32, 4 }, { "bfOffbits", m_uint32, 4 } } }; std::array <int, Size> data; } head;Erm. Probier es doch einfach mal aus. Wie es mit pair geht hab ich dir gezeigt (nicht so, weil du die m_uintXX Funktionen hier nicht einfach so übergeben kannst - hab ich auch schon geschrieben). Mit Tuple wird es nicht viel anders sein, nen?
lemon03 schrieb:
Der aktuelle offset wäre dann die vorherigen addierten Werte. Oder gleich so:
struct Head //Kopf { static const auto Size = 4; std::array <std::tuple <std::size_t, std::string, |Typ?|>, Size> bType; std::make_tuple //?? { { { 0, "bfType", m_uint16 }, //offset//name//uint16_t { 2, "bfSize", m_uint32 }, //uint32_t { 6, "bfReserved", m_uint32 }, { 10, "bfOffbits", m_uint32 } } }; std::array <int, Size> data; } head;Das zweite wäre natürlich einfacher.
Oder z.B.
uint32_t read_uint32(std::vector<unsigned char> const& data, size_t& inout_offset) { uint32_t const result = ... wie gehabt inout_offset += 4; // Offset um so viele Bytes weiterschieben wie wir "konsumiert" haben return result; }lemon03 schrieb:
Allerdings scheint Deine letzte Idee mit
und die dir ne BitmapHeader struct zurückgibt?
ganz interessant. Kannst Du mir zeigen, wie in etwas Du dies gemeint hast?
Naja... nix spezielles, die naive, triviale Implementierung für sowas halt:
header read_header(std::vector<unsigned char> const& data, size_t& inout_offset) { header resut; resut.first_field = read_uint32(data, inout_offset); resut.second_field = read_uint16(data, inout_offset); resut.third_field = read_double(data, inout_offset); return resut; }Bzw. kannst du das natürlich auch wieder mit "manuellen" Offsets machen. Finde ich aber ... umständlicher.
-
Joh, danke. Lauter hübsche Gedankenanstöße. Mal schauen, was ich daraus mache

-
... 5 Jahre später ...
Hallo,
auf Deinen letzten Vorschlag konnte ich erst mal nicht näher eingehen, ich wollte es versuchen, wie ich es mir vorgestellt hatte. Ich habe einige Dinge vereinfacht (die Funktionen zb einfach zu
intgemacht), ich weiß nicht, ob man jetzt denkt, "meine Güte, der hat ja gar nichts verstanden"?Vielleicht kann man mich ja noch beraten, was nicht so gut ist/aussieht. Habe versucht Kommentare zu setzen, wo ich noch Fragen habe. Die Funktion
out_inFile()habe ich entfernt, weil sie ziemlich lang ist und hier nichts zu suchen hat. Hätte aber später noch ein, zwei Fragen dazu.Bin aber wie schon damals nur bis zur Farbtabelle. Wenn das alles korrekt ist, mache ich dann den Rest.
LG
//#include "output.h" #include <iostream> #include <fstream> #include <string> #include <vector> #include <iomanip> //#include <array> //#include <tuple> #include <cmath> typedef int(*make_dec)(std::vector <unsigned char> const&, std::size_t); //bildbyte, offset int uint16(std::vector<unsigned char> const& bmp_data, std::size_t pos); int uint32(std::vector<unsigned char> const& bmp_data, std::size_t pos); int int32(std::vector<unsigned char> const& bmp_data, std::size_t pos); typedef std::tuple<std::size_t, std::string, make_dec> bfTuple; //offset, bezeichnung, "typ" typedef std::tuple<unsigned char, unsigned char, unsigned char, unsigned char> tblTuple; //Farbtabelleneinträge B/G/R/0 struct Head //Kopf { static const auto Size = 4; std::array<bfTuple, Size> bfType { { std::make_tuple( 0, "bfType", uint16), //offset, bezeichnung, "typ" (hier uint16_t) std::make_tuple( 2, "bfSize", uint32), //uint32_t std::make_tuple( 6, "bfReserved", uint32), std::make_tuple(10, "bfOffbits", uint32) } }; std::array <int, Size> data; } head; struct Info //Eigenschaften { static const auto Size = 11; std::array<bfTuple, Size> bfType { { std::make_tuple( 14, "biSize", uint32), //uint32_t std::make_tuple( 18, "biWidth", int32), //int32_t std::make_tuple( 22, "biHeight", int32), std::make_tuple( 26, "biPlanes", uint16), //uint16_t std::make_tuple( 28, "biBitCount", uint16), std::make_tuple( 30, "biCompression", uint32), std::make_tuple( 34, "biSizeImage", uint32), std::make_tuple( 38, "biXPelsPerMeter", int32), std::make_tuple( 42, "biYPelsPerMeter", int32), std::make_tuple( 46, "biClrUsed", uint32), std::make_tuple( 50, "biClrImportant", uint32) } }; std::array <int, Size> data; } info; /* struct ColMask //Farbmaske { bool is_ = true; std::vector<char> entry; } colMsk; */ struct ColTable //Farbtabelle { bool is_ = true; //existiert Farbtabelle int size_ = 0; //Größe std::vector<tblTuple> entry; //Einträge } colTbl; //////////////////////////////////////////////////////////////////////////////////////// int uint16(std::vector <unsigned char> const& bmp_data, std::size_t pos) { const int u_val = bmp_data.at(pos + 0) + (static_cast<uint16_t>(bmp_data.at(pos + 1)) << 8); return u_val; } int uint32(std::vector <unsigned char> const& bmp_data, std::size_t pos) { const int u_val = bmp_data.at(pos + 0) + (static_cast<uint32_t>(bmp_data.at(pos + 1)) << 8) + (static_cast<uint32_t>(bmp_data.at(pos + 2)) << 16) + (static_cast<uint32_t>(bmp_data.at(pos + 3)) << 24); return u_val; } int int32(std::vector <unsigned char> const& bmp_data, std::size_t pos) { const int u_val = uint32(bmp_data, pos); if ((u_val >> 31) & 1) { return -static_cast<int32_t>(~u_val) - 1; } else { return u_val; } } ///////////////////////////////////////////////////////////////////////////////////// int make_colTbl_size(int biClrUsed, int biBitCount, bool& is_ ) { int colTbl_size = 0; if (biClrUsed == 0) { if (biBitCount == 1 || biBitCount == 4 || biBitCount == 8) colTbl_size = pow(2, biBitCount); else colTbl.is_ = false; } else colTbl_size = biClrUsed; return colTbl_size; } ///////////////////////////////////////////////////////////////////////////////////// void read_bmpData(std::string file_, std::vector <unsigned char>& bmp_data) { const std::string file_name = file_ + ".bmp"; char s_char; std::ifstream file (file_name.c_str(), std::ios::binary); if (!file) { std::string err_str = "!Fehler bei " + file_name; throw err_str; } while (file.get(s_char)) bmp_data.push_back(s_char); std::cout << "_File: " << file_name << '\n'; } //void out_inFile(std::string file_, std::vector<unsigned char> const& bmp_data, // std::array<int, head.Size>, std::array<int, info.Size>, int) ////////////////////////////////////////////////////////////////////////////////// int main() { std::string file_ = "test_me"; //std::string file_ = "WOLKEN"; //std::string file_ = "046"; //std::string file_ = "Cubis"; //std::string file_ = "example_topdown"; //std::string file_ = "topdown_2"; //std::string file_ = "gl_xs"; try { std::vector<unsigned char> bmp_data; read_bmpData(file_, bmp_data); //head for (auto i=0; i<head.Size; i++) head.data.at(i) = std::get<2>(head.bfType.at(i))(bmp_data, std::get<0>(head.bfType.at(i))); //info for (auto i=0; i<info.Size; i++) info.data.at(i) = std::get<2>(info.bfType.at(i))(bmp_data, std::get<0>(info.bfType.at(i))); //Kodierung int biCompression = info.data.at(5); if (biCompression != 0) //biCompression !=0 Bilddaten sind kodiert - Abbruch { std::string str_err = "Bilddaten sind kodiert\nkann im Moment nicht verarbeitet werden"; out_inFile(file_, bmp_data, head.data, info.data, colTbl.size_); throw str_err; } /*sollte ich hier wie bei biCompression extra neue Variablen deklarieren?*/ //int biClrUsed = info.data.at(9); //int biBitCount = info.data.at(4); //colTbl.size_ = make_colTbl_size(biClrUsed, biBitCount, colTbl.is_); //Farbtabelle colTbl.size_ = make_colTbl_size(info.data.at(9), info.data.at(4), colTbl.is_); std::size_t offset = 54; //magic number? for (auto i=0; i<colTbl.size_*4; i+=4) { colTbl.entry.push_back(std::make_tuple(bmp_data.at(offset+i+0), bmp_data.at(offset+i+1), bmp_data.at(offset+i+2), bmp_data.at(offset+i+3))); } out_inFile(file_, bmp_data, head.data, info.data, colTbl.size_); } catch(std::string str_err) { std::cerr << str_err << '\n'; } }
-
Ich hätte da noch ne kurze Zwischenfrage. Diese Übung soll ja kein Selbstzweck sein, sondern ich will später die Daten des Bild weiterverarbeiten.
Gebraucht werden nur die Farbtabelle, die Bilddaten und evtl die Farbmasken. Ich dachte mir, um dann darauf zuzugreifen, speichere ich die Daten in ein File. Hexadezimal zweistellig.
Wenn man dann die Datei in zB notepad++ öffnet, gibt es eine Riesenzeile voller Zeichen entsprechend der Größe. Geht das?
Oder gibt es da Grenzen bezüglich eines File, wie lang solche Zeichenketten ohne Umbruch sein können?
-
lemon03 schrieb:
Oder gibt es da Grenzen bezüglich eines File, wie lang solche Zeichenketten ohne Umbruch sein können?
Dateien sind Zeichenfolgen und haben sonst keine Struktur, Zeilenumbrüche sind auch nur Zeichen ohne jede Sonderbedeutung auf Dateiebene.
-

Die Frage ist bloss ob es Notepad++ ab einer bestimmten Zeilenlänge zerreisst.
Da ich Notepad++ nicht verwende kann ich dazu aber nix sagen.
(Ich verwende Notepad2, und von dem bin ich gewohnt dass es a) so ziemlich jedes File anzeigt ohne zu mucken und b) vor allen auch sehr grosse Dateien verhältnismässig flott öffnet & anzeigt -- so lange man genug RAM hat.)
-
Ja stimmt, danke. Hatte ich mir in der Zwischenzeit auch überlegt. Eine Bitmap im Editor zu öffnen ist ja auch möglich. Obwohl es sich an keine Konventionen einer zB Text-Datei kümmert. War da irgendwie eingleisig.
EDIT: Achhaje
Naja, ich werds ja selbst herausfinden werden 