16-bit Daten in PNG Grafik



  • Hallo,

    ich wuerde gerne 2-dimensionale 16-bit Daten in eine PNG-Datei schreiben, um sie mir anschauen zu koennen (mittels libpng unter Linux in einem C-Programm). Laut Dokumentation kann die libpng dies auch. Allerdings finde ich zum Schreiben nur die Funktion "void png_write_image (png_structp png_ptr, png_bytepp image);"
    Dabei ist png_bytepp ein Zeiger-Zeiger und png_byte das gleiche wie "unsigned char". Allerdings hat "unsigned char" nur 8bit und keine 16bit.
    Im Moment kopiere ich meine Daten mit

    for (h=0; h < height; ++h) {
            image_rows[h] = malloc(width*sizeof(*(image_rows[h])));
            for (w=0; w < width; ++w) {
                *(image_rows[h] + w) =  * (png_bytep)(data + h*width+w);
            }
        }
    

    um dann image_rows and png_write_image zu uebergeben. Doch wenn ich mir z.B. die Zahlenwerte der erste Zeile vom image_rows ansehe, springen sie nach dem Wert 255 wieder auf 0 zurueck, was ich eben fuer "unsigned char" auch so erwarte.

    Kann mir jemand erklaeren, wie ich mit libpng eine 16-bit grayscale Grafik erstelle, um meine 16bit Daten zu visualisieren?

    Vielen Dank,
    Tim



  • Es ist eine Weile her, dass ich direkt mit libpng gearbeitet habe und möglicherweise erzähle ich nun totalen Mist, aber soweit ich mich erinnere, musst Du im PNG-Header zunächst mal angeben, dass es sich um ein 16-Bit PNG handelt. Dann wird bei png_write_image entsprechend immer ein Byte-Tupel als Pixel interpretiert, so dass deine Zeile also eine Größe von 2*Spalten Bytes hat. So war es zumindest bei 32 Bit PNGs, wo je 4 Bytes für einen Pixel standen.



  • Hallo TdZ,

    die Tiefe habe ich vorher mit png_set_IHDR auf 16 gesetzt. Das scheint auch halbwegs korrekt zu funktionieren.
    Allerdings verstehe ich nicht, wie ich meine Daten in image_rows hineinbekomme.

    Wenn ich es ueber Zuweisung mache wie im Codebeispiel, dann geht der Compiler ja davon aus, dass image_rows[h][w] vom Type png_byte ist. Das ist in /usr/include/pngconf.h als 'unsigned char' dargestellt, d.h., der Speicher wird dann nicht korrekt bestueckt, damit png_write_image je zwei byte pro Pixel benutzen kann.

    Ein aehnliches Problem ergibt sich, wenn ich (und so wird es auch in 'man libpng' suggeriert) den Zeiger auf die Daten setze, z.B.

    image_rows[h] = data*h*width;
    

    Das funktioniert nicht, weil 'data' vom Type unsigned int ist, also 4Byte lang und nicht 2.

    Andere Moeglichkeit waere fuer mich, eine andere Bibliothek zu benutzen. Ich hatte es schon mit libgd versucht, die auch ein Interface zu PNG anbietet, doch habe ich dort keinen Weg gefunden, die Pixeltiefe von 8 auf 16bit zu erhoehen.
    libtiff ist sehr unpraktisch, weil gimp tiff nur mit 8bit unterstuetzt, nicht mit 16bit. Gibt es noch eine Bibliothek, mit der ich einfach ein 16bit grayscale Bild erzeugen kann?



  • Ich kenne LibPNG nicht, vermute aber, dass sie die Bilddaten als eindimensionales Feld von 16bit Werten erwartet? In diesem Fall würde ich es so machen:

    unsigned short* data = (unsigned short*)malloc(height * width * sizeof(unsigned short)); //short-Array, so kann man die 16Bit-Werte direkt schreiben
    
    ...
    
     data[y * width + x] = wert; //setze Wert an Stelle (x, y)
    

    Bei der Übergabe von data an png_write_image() musst du data natürlich zu (png_bytep) casten...



  • Genau, so meinte ich das auch. Der Pointer selbst ist das natürlich byteweise angeordnet, du kannst aber auch anders darauf zugreifen. Das JimmydaMage hat das ganz anschaulich gezeigt, wie ich finde.

    Wenn Du aber sowieso noch andere Bildformate unterstützen möchtest, dann kannst Du auch Wrapper wie FreeImage oder DevIL benutzen. Auch SDL_image ist vielleicht einen Blick wert, zumal die alle noch über Konvertierungsmechanismen in andere Formate (z.B. auch GL-Texturen) verfügen.

    Ich hab' eigentlich nur einmal libpng direkt verwenden müssen und das war für ein Demo-Projekt auf der PSP. Würde ich aber mittlerweile wahrscheinlich auch anders machen.



  • Der Vorschlag von JimmydaMage, erst die Daten in short (2byte = 16bit) umzuwandeln, hat funktioniert. Zumindest fuer die Daten selber (die auch tatsaechlich 16bit Dynamik haben). Fuer ein Testbild 400x200, bei dem ich in jeder Reihe den Wert auf den Spaltenwert gesetzt habe (d.h. data["*"][w] = w),
    springen die Werte bei 256 doch wieder auch 0 zurueck. Das ist aber grade nicht mein Hauptanliegen, und ich moechte auch nicht andere Formate unterstuetzen, da ich nur nach einem Weg gesucht habe, die Daten zu visualisieren.

    Halte ich zwar fuer eine fehleranfaellige Loesung, aber fuer meine Zwecke reichts.

    Danke,
    Tim



  • Ja ist ja auch klar. 1 Byte = 2^8 Möglichkeiten = 256 Werte. Du hast aber 2 Byte bei deinem 16 Bit Bild = 2^16 Möglichkeiten = 256^2 Werte. Du musst also schon Dein Bild-Array ebenfalls im 16-Bit Fortmat anlegen, sonst wird das nichts.


Anmelden zum Antworten