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 const ist schon richtig, aber das "&" fehlt noch - das gehört ja schliesslich mit zum Typ. Weswegen ich es auch immer Type& name schreibe und nicht Type &name wie 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_uint16 etc. nicht mit make_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 theOffsetInThatVector nehmen?

    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 vector oder auch einfach nen Zeiger auf die unsigned char s übergibst, und die dir ne BitmapHeader struct zurückgibt? Und einfach die ganzen make_this , make_that Funktionen 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* statt make_int_function_ptr schreiben.)



  • 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 bei

    void func(int &val)
        { val ++; }
    

    wäre es doch der Wert und nicht der Typ, der per Referenz übergeben wird?

    Zu dem Problem mit dem offset als Ü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 einen std::tuple lö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 mit

    und 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.
    val ist in diesem Beispiel eine Referenz auf einen int .
    Und daher ist der Typ von val eben nicht " int ", sondern " int& ".

    lemon03 schrieb:

    Zu dem Problem mit dem offset als Ü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 einen std::tuple lö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 int gemacht), 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?


  • Mod

    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 😉


Anmelden zum Antworten