Ein- und Ausgabebibliotheken für Grafikformate?
-
Was für ein Framework nutzt Du für die Softwareentwicklung? Je nachdem was Du verwendest, gibt es schon Libraries im Framework, um Pixel Bilder bzw. Vektorbilder zu laden.
-
Hi(gh)!
Sehr populär scheint auch stb zu sein (in Debian als libstb0). Das sieht mir wie eine Header-Only-Bibliothek zu sein die eine Reihe von Bildformaten laden und speichern kann. Offenbar bringt diese Bibliothek eigene Implementierungen der Bildformate mit, so dass es keine Abhängigkeiten zu den Referenzbibliotheken gibt. Die könnte am simpelsten zu verwenden sein.
Ich habe mir jetzt libstb0 und die zugehörige Header-Datei libstb-dev installiert... ein erstes kleines Testprogramm:
// yaigat - Yadgar's Artificial Intelligence Graphics Analysis Tool #include <stdlib.h> #include <iostream> #include <string.h> #include <stb_image.h> using namespace std; int main() { cout << "Test!" << endl; return 0; }wurde leider nicht kompiliert, es gab eine Fehlermeldung:
yaigat.cc:6:10: fatal error: stb_image.h: Datei oder Verzeichnis nicht gefunden
6 | #include <stb_image.h>
| ^~~~~~~~~~~~~Der Header ist laut https://packages.debian.org/bullseye/libstb-dev korrekt - aber in welchem Verzeichnis liegt er lokal?
Bis bald im Khyberspace!
Yadgar
-
Laut deinem eigenem Link sollte das aber
<stb/stb_image.h>sein. Ansonsten: Deine Dateien auf deinem Computer danach durchsuchen?
-
@SeppJ sagte in Ein- und Ausgabebibliotheken für Grafikformate?:
Laut deinem eigenem Link sollte das aber
<stb/stb_image.h>sein. Ansonsten: Deine Dateien auf deinem Computer danach durchsuchen?Ja, ich habe es jetzt gefunden, die Zeile 6 ersetzt durch
#include <stb/stb_image.h>...und weiß seither, dass sämtliche C-Header standardmäßig in /usr/include liegen, die C++-Bibliotheken in /usr/include/C++/10/! Sehr instruktiv, danke für den Tipp!
Bis bald im Khyberspace!
Yadgar
Now playing: Karlheinz Stockhausen: Kontra-Punkte (Ensemble Recherche)
-
@Yadgar sagte in Ein- und Ausgabebibliotheken für Grafikformate?:
Ja, ich habe es jetzt gefunden, die Zeile 6 ersetzt durch
#include <stb/stb_image.h>...und weiß seither, dass sämtliche C-Header standardmäßig in /usr/include liegen, die C++-Bibliotheken in /usr/include/C++/10/! Sehr instruktiv, danke für den Tipp!
Das Wichtige ist nicht
/usr/include, da liegen fast alle systemweiten Header, sondern dass das Paket die Header nach/usr/include/stb/installiert. Compiler auf Linux sind typischerweise so konfiguriert, dass sie standardmäßig/usr/includeals eines der Default-Includeverzeichnisse verwenden. Um von diesem aus/usr/include/stbzu erreichen, muss man natürlich im Quellcode einen relativen Pfadstb/stb_image.hangeben. Mitg++ -v ...bekommst du das auch angezeigt. Da gibt GCC dann am Ende z.B. so was aus:#include "..." search starts here: #include <...> search starts here: /usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16 /usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/x86_64-pc-linux-gnu /usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/backward /usr/lib/gcc/x86_64-pc-linux-gnu/16/include /usr/local/include /usr/include End of search list.Wenn der Compiler also ein relatives
#include <stb/stb_image.h>sieht, dann hängt er das an all diese Verzeichnisse an und nimmt das erste, wo er die Datei findet:/usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/stb/stb_image.h /usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/x86_64-pc-linux-gnu/stb/stb_image.h /usr/lib/gcc/x86_64-pc-linux-gnu/16/../../../../include/c++/16/backward/stb/stb_image.h /usr/lib/gcc/x86_64-pc-linux-gnu/16/include/stb/stb_image.h /usr/local/include/stb/stb_image.h /usr/include/stb/stb_image.hUnd das letzte Verzeichnis ist genau das, wo das
libstb-dev-Paket den Header installiert hat.So am Rande:
/usr/include/C++/10(ich sehe du verwendest GCC 10
) ist übrigens nicht unbedingt nicht immer das Include-Verzeichnis für die C++-Header. Die werden beim Build von GCC konfiguriert und können z.B. auch relativ zum g++-Program sein, wie bei einer anderen portablen GCC-Toolchain die ich verwende:/toolchains/dlo-toolchain-x86_64-linux-musl/bin/../lib/gcc/i386-dlo-elf/16.2.0/include/c++Das passt sich dann immer an das Verzeichnis an, in dem GCC installiert wurde und ermöglicht, den Compiler auch neben dem System-Compiler in einem beliebigen Verzeichnis zu installieren. System-Compiler (also die, die mit der Distribution kommen) sind aber typischerweise für feste, absolute Include-Verzeichnisse wie
/usr/includekonfiguriert.Das nur am Rande, falls du in Zukunft noch andere nicht gefundene Header debuggen willst. Schau die am besten an, was
gcc -v ...oderg++ -v ...als Include-Verzeichnisse angeben und ob er den Header damit auch finden kann.Natürlich kannst du auch eigene Suchverzeichnisse hinzufügen. Wenn du z.B. mit
g++ -I/usr/include/stbkompilierst, dann funktioniert auch
#include <stb_image.h>. Lass es aber besser bei#include <stb/stb_image.h>. Das ist portabler, weil es nicht den expliziten Include-Pfade erfordert.
-
Hi(gh)!
Danach suchte ich natürlich die Ladefunktion(en) für die einzelnen Dateiformate... und wurde vom Quellcode in stb_image.h einfach nur erschlagen! Google war dann ausnahmsweise doch mein Freund (obwohl mir Ecosia ja sympathischer ist...), ich suchte nach "stb_image.h tutorial" und fand dann diesen netten kleinen Codeschnipsel hier: https://psgraphics.blogspot.com/2015/06/a-small-image-io-library-stbimageh.html
Das war ja wirklich einfacher, als ich es mir hätte träumen lassen... sofort implementiert und mit einer 1-Pixel-Datei getestet:
// yaigat - Yadgar's Artificial Intelligence Graphics Analysis Tool #define STBI_NO_BMP #define STBI_NO_PSD #define STBI_NO_HDR #define STBI_NO_PIC #define STBI_NO_PNM #include <stdlib.h> #include <iostream> #include <string.h> #define STB_IMAGE_IMPLEMENTATION #include <stb/stb_image.h> #define STB_IMAGE_WRITE_IMPLEMENTATION #include <stb/stb_image_write.h> using namespace std; int main() { const string auml = "ä"; const string Auml = "Ä"; const string ouml = "ö"; const string Ouml = "Ö"; const string uuml = "ü"; const string Uuml = "Ü"; const string szlig = "ß"; int x, y, n; unsigned char *data = stbi_load("0000.gif", &x, &y, &n, 0); cout << "Breite: " << x << endl; cout << "H" << ouml << "he: " << y << endl; unsigned int s = sizeof(data); unsigned i; for (i=0; i<s; i++) cout << (unsigned int)data[i] << endl; for (i=0; i<=2; i++) data[i]/=2; stbi_write_png("0000.png", x, y, 3, data, 0); return 0; }Ausgabe:
Breite: 1
Höhe: 1
106
119
109
255
254
127
0
0Die ersten drei Bytes des char-Arrays sind die RGB-Werte des Pixels, wie ich mich mich umgehend per GIMP überzeugen konnte... und was die übrigen fünf Bytes bedeutet sollte aus der Dokumentation von stb_image.h hervorgehen! Wenn ich die erläuternden Kommentare im Quelltext richtig verstanden habe, erzeugen alle Importfunktionen dieselbe Array-Struktur: Pixel mit Kanälen pro Pixel entsprechend des beim Laden ausgelesenen Wertes n (1 bis 4, also Graustufen, Graustufen plus Alpha, RGB oderRGB plus Alpha) und nach den Pixeln diese ominösen fünf Bytes - interessanterweise schwankt Byte Nr. 4 von Programmlauf zu Programmlauf zwischen 252 und 255!
Das Schreiben ergibt (hier) wiederum ein einwandfreies PNG-File, die Farbwerte sind ebenfalls stabil - bzw. werdenin dieser Version korrekt halbiert! Was will man mehr?
Bis bald im Khyberspace!
Yadgar
-
@Yadgar Ist ja schön, dass das so simpel ist. Das wünscht man sich oft, wenn man einfach nur mit Pixeldaten hantieren will.
Die "ominösen 5 Bytes" kann ich dir erklären: die 255 ist der Alpha-Wert des einen Pixel. 255 bedeutet "völlig deckend". Die anderen 4 Bytes kommen von dem
s = sizeof(data);Damit bestimmst du nämlich die Größe des
unsigned char*(Pointer) und der ist auf einem 64-Bit-System üblicherweise immer 8
Versuch es mal mit:
s = x * y * n;... das sollte dem was du vermutlich vorhast näher kommen.
-
Hi(gh)!
@Finnegan sagte in Ein- und Ausgabebibliotheken für Grafikformate?:
@Yadgar Ist ja schön, dass das so simpel ist. Das wünscht man sich oft, wenn man einfach nur mit Pixeldaten hantieren will.
Die "ominösen 5 Bytes" kann ich dir erklären: die 255 ist der Alpha-Wert des einen Pixel. 255 bedeutet "völlig deckend". Die anderen 4 Bytes kommen von dem
s = sizeof(data);Damit bestimmst du nämlich die Größe des
unsigned char*(Pointer) und der ist auf einem 64-Bit-System üblicherweise immer 8
Wenn ich mir n (das ist die Anzahl der Kanäle) ausgeben lasse wird 4 angezeigt! Klar, der in *char gespeicherte Adresswert kann sich von Lauf zu Lauf leicht ändern, was das Byte Nr. 4 erklären würde... dann kann *char aber auch nicht 8 Bytes groß sein, sondern nur 4 (Bytes Nr. 4 bis 7)!
Versuch es mal mit:
s = x * y * n;... das sollte dem was du vermutlich vorhast näher kommen.Gut, als Nächstes dann eine etwas(!) größere GIF-Datei!
-
@Yadgar sagte in Ein- und Ausgabebibliotheken für Grafikformate?:
Wenn ich mir n (das ist die Anzahl der Kanäle) ausgeben lasse wird 4 angezeigt! Klar, der in *char gespeicherte Adresswert kann sich von Lauf zu Lauf leicht ändern, was das Byte Nr. 4 erklären würde... dann kann *char aber auch nicht 8 Bytes groß sein, sondern nur 4 (Bytes Nr. 4 bis 7)!
Ich tu mich echt schwer zu verstehen, welchem Missverständnis du hier unterliegst.
sizeof(char*) == sizeof(void*) == sizeof(data)(dataaus deinem Programm). Du bestimmst nicht die "Größe der Daten", sondern die Größe des Pointers, der auf diese Daten zeigt. Der muss 8 sein, weil du diese Zahl alssals Eintrittsbedingung deiner Ausgabeschleife verwendest und 8 Elemente angezeigt werden (die Schleife läuft von0biss - 1, also0bis7, was 8 Elemente sind). Die Ersten 4 angezeigten Elemente stammen aus der geladenen Datei, die restlichen 4 sind Datenmüll, weil das ein Buffer Overflow ist und du uninitialisierten Speicher liest, der beliegbige Daten beinhalten kann. Nur die ersten 4 bytes wurden vonstbi_loadin dem Speicher abgelegt. Die restlichen 4 waren bereits vorher da (oder sind eventuell irgendwelche Metadaten vonstb, was ich aber für eher unwahrscheinlich halte).
-
Hmm, klassische Anfängerfehler... Wieso muss man dann eigentlich gleich mit Grafik, Image Processing und einer eigenen Lib anfangen - anstatt erstmal die Basics zu lernen? Erinnert mich an Vibe Coding... das scheint nun in zu sein. Wenn ich hoch hinaus will, fange ich ja auch nicht gleich mit dem Himalaya an.