Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > de.comp.lang.iso-c++ > #2018 > unrolled thread
| Started by | Jörg "Yadgar" Bleimann <yazdegird@gmx.de> |
|---|---|
| First post | 2017-02-11 14:52 +0100 |
| Last post | 2017-02-14 11:36 +0100 |
| Articles | 6 — 3 participants |
Back to article view | Back to de.comp.lang.iso-c++
Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Jörg "Yadgar" Bleimann <yazdegird@gmx.de> - 2017-02-11 14:52 +0100
Re: Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Stefan Reuther <stefan.news@arcor.de> - 2017-02-12 11:00 +0100
Re: Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Jörg "Yadgar" Bleimann <yazdegird@gmx.de> - 2017-02-12 21:43 +0100
Re: Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Jörg "Yadgar" Bleimann <yazdegird@gmx.de> - 2017-02-12 20:34 +0100
Re: Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Markus Schaaf <mschaaf@elaboris.de> - 2017-02-14 01:03 +0100
Re: Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... Jörg "Yadgar" Bleimann <yazdegird@gmx.de> - 2017-02-14 11:36 +0100
| From | Jörg "Yadgar" Bleimann <yazdegird@gmx.de> |
|---|---|
| Date | 2017-02-11 14:52 +0100 |
| Subject | Selbstgestrickter Floyd-Steinberg-Filter sieht seltsam aus... |
| Message-ID | <o7n4v0$emd$1@news.albasani.net> |
Hi(gh)!
Hi(gh)!
Mittlerweile habe ich es tatsächlich geschafft, im Rahmen meines
Kommandozeilen-Bildbearbeitungsprogramms "YIP" (Yadgar's Image Processor
- ImageMagick für Arme...) so etwas wie Floyd-Steinberg-Rasterung zu
programmieren, nach dem Artikel "Floyd-Steinberg" in der englischen
Wikipedia... in Pseudocode wird der Algorithmus dort so dargestellt:
for each y from top to bottom
for each x from left to right
oldpixel := pixel[x][y]
newpixel := find_closest_palette_color(oldpixel)
pixel[x][y] := newpixel
quant_error := oldpixel - newpixel
pixel[x + 1][y ] := pixel[x + 1][y ] + quant_error * 7 / 16
pixel[x - 1][y + 1] := pixel[x - 1][y + 1] + quant_error * 3 / 16
pixel[x ][y + 1] := pixel[x ][y + 1] + quant_error * 5 / 16
pixel[x + 1][y + 1] := pixel[x + 1][y + 1] + quant_error * 1 / 16
Bei mir in C++ sieht das so aus:
void floydsteinberg(vector<vector<pixel> > &img, vector<rgb> &pal)
{
unsigned short h = img.size();
unsigned short w = img[0].size();
unsigned short r, c, i;
unsigned short p = pal.size();
rgb t0, t1, t2, t3, t4, closest=pal.at(0);
rgb triple, dist;
float newred, newgreen, newblue;
for (r=0; r<h; r++)
{
for (c=0; c<w; c++)
{
img[r].at(c).get_all(triple);
t0 = triple;
t1 = {-1, -1, -1};
t2 = {-1, -1, -1};
t3 = {-1, -1, -1};
t4 = {-1, -1, -1};
if (c < w-1)
{
img[r].at(c+1).get_all(triple);
t1 = triple;
}
if (c > 0 && r < h-1)
{
img[r+1].at(c-1).get_all(triple);
t2 = triple;
}
if (r < h-1 )
{
img[r+1].at(c).get_all(triple);
t3 = triple;
}
if (c < w-1 && r < h-1 )
{
img[r+1].at(c+1).get_all(triple);
t4 = triple;
}
for (i=0; i<p; i++)
{
if (coldist(t0, pal.at(i)) < coldist(t0, closest))
closest = pal.at(i);
}
img[r].at(c).set_all(closest.red, closest.green, closest.blue);
dist.red = t0.red - closest.red;
dist.green = t0.green - closest.green;
dist.blue = t0.blue - closest.blue;
if (t1.red > -1)
{
img[r].at(c+1).get_all(triple);
newred = triple.red + dist.red*0.4375;
newgreen = triple.green + dist.green*0.4375;
newblue = triple.blue + dist.blue*0.4375;
img[r].at(c+1).set_all(mround(newred), mround(newgreen),
mround(newblue));
}
if (t2.red > -1)
{
img[r+1].at(c-1).get_all(triple);
newred = triple.red + dist.red*0.1875;
newgreen = triple.green + dist.green*0.1875;
newblue = triple.blue + dist.blue*0.1875;
img[r+1].at(c-1).set_all(mround(newred), mround(newgreen),
mround(newblue));
}
if (t3.red > -1)
{
img[r+1].at(c).get_all(triple);
newred = triple.red + dist.red*0.3125;
newgreen = triple.green + dist.green*0.3125;
newblue = triple.blue + dist.blue*0.3125;
img[r+1].at(c).set_all(mround(newred), mround(newgreen),
mround(newblue));
}
if (t4.red > -1)
{
img[r+1].at(c+1).get_all(triple);
newred = triple.red + dist.red*0.0625;
newgreen = triple.green + dist.green*0.0625;
newblue = triple.blue + dist.blue*0.0625;
img[r+1].at(c+1).set_all(mround(newred), mround(newgreen),
mround(newblue));
}
}
}
cout << "Floyd-Steinberg-Rasterung wird berechnet!" << endl;
}
"pixel" ist eine selbst entwickelte Klasse, "rgb" ein struct-Objekt für
Farbtripel.
Das Programm kompiliert problemlos (bis auf ein paar Warnungen wegen nur
in C++11 verfügbarer Funktionen), es gibt auch keinen Laufzeitfehler...
aber wenn ich mir die gerasterte Bilddatei (von RGB nach
1-bit-Schwarzweiß) ansehe, frage ich mich, was da falsch gelaufen ist -
wie Floyd-Steinberg-Schwarzweiß z. B. in GIMP sieht es nämlich nicht aus!
Hier das Original:
http://www.rock-o-data.de/fotos/Yadgar.png
... und das hier:
http://www.rock-o-data.de/fotos/Yadgar_monochrome.png
ist die schwarzweiße gerasterte Version!
Bis bald im Khyberspace!
Yadgar
[toc] | [next] | [standalone]
| From | Stefan Reuther <stefan.news@arcor.de> |
|---|---|
| Date | 2017-02-12 11:00 +0100 |
| Message-ID | <o7pf7i.4f8.1@stefan.msgid.phost.de> |
| In reply to | #2018 |
Am 11.02.2017 um 14:52 schrieb Jörg "Yadgar" Bleimann:
> dist.red = t0.red - closest.red;
> dist.green = t0.green - closest.green;
> dist.blue = t0.blue - closest.blue;
> if (t1.red > -1)
> {
> img[r].at(c+1).get_all(triple);
> newred = triple.red + dist.red*0.4375;
> newgreen = triple.green + dist.green*0.4375;
> newblue = triple.blue + dist.blue*0.4375;
> img[r].at(c+1).set_all(mround(newred), mround(newgreen),
> mround(newblue));
> }
[...]
> Das Programm kompiliert problemlos (bis auf ein paar Warnungen wegen nur
> in C++11 verfügbarer Funktionen), es gibt auch keinen Laufzeitfehler...
> aber wenn ich mir die gerasterte Bilddatei (von RGB nach
> 1-bit-Schwarzweiß) ansehe, frage ich mich, was da falsch gelaufen ist -
> wie Floyd-Steinberg-Schwarzweiß z. B. in GIMP sieht es nämlich nicht aus!
Das war jetzt ziemlich viel Code, aber ein entscheidender Teil fehlt:
wie ist denn die rgb-Klasse definiert?
Da "meine" RGB-Klassen eigentlich immer uint8_t-Member haben, vermute
ich in dem oben zitierten Code sowas wie unsigned-Überläufe.
Ansonsten: kleines Bild (3x3?) nehmen, Code großräumig verprinten und
per Hand nachrechnen. Debuggen halt... Und nicht vergessen, das Ergebnis
hinterher als Unittest abzuspeichern :)
Stefan
[toc] | [prev] | [next] | [standalone]
| From | Jörg "Yadgar" Bleimann <yazdegird@gmx.de> |
|---|---|
| Date | 2017-02-12 21:43 +0100 |
| Message-ID | <o7qhd0$4n8$1@news.albasani.net> |
| In reply to | #2019 |
Am 12.02.2017 um 11:00 schrieb Stefan Reuther: > Da "meine" RGB-Klassen eigentlich immer uint8_t-Member haben, vermute > ich in dem oben zitierten Code sowas wie unsigned-Überläufe. Ich habe jetzt die pixel-Klasse komplett auf short int (statt unsigned char) umgestellt - am Ergebnis ändert sich leider nichts! Bis bald im Khyberspace! Yadgar
[toc] | [prev] | [next] | [standalone]
| From | Jörg "Yadgar" Bleimann <yazdegird@gmx.de> |
|---|---|
| Date | 2017-02-12 20:34 +0100 |
| Message-ID | <o7qdd5$ccr$1@news.albasani.net> |
| In reply to | #2019 |
Hi(gh)!
Am 12.02.2017 um 11:00 schrieb Stefan Reuther:
> Am 11.02.2017 um 14:52 schrieb Jörg "Yadgar" Bleimann:
> Das war jetzt ziemlich viel Code, aber ein entscheidender Teil fehlt:
> wie ist denn die rgb-Klasse definiert?
rgb ist keine Klasse, sondern eine (globale) structure:
struct rgb // global!
{
int red;
int green;
int blue;
};
>
> Da "meine" RGB-Klassen eigentlich immer uint8_t-Member haben, vermute
> ich in dem oben zitierten Code sowas wie unsigned-Überläufe.
Du meinst wahrscheinlich die "pixel"-Klasse... dort habe ich tatsächlich
unsigned char-Elemente verwendet, um Speicherplatz zu sparen:
class pixel
{
public:
pixel(); // Standard-Konstruktor
pixel (int, int, int); // Allgemeiner Konstruktor
~pixel(); // Destruktor
void set_all(int, int, int);
void set_all(unsigned char, unsigned char, unsigned char);
void set_red(int);
void set_red(unsigned char);
void set_green(int);
void set_green(unsigned char);
void set_blue(int);
void set_blue(unsigned char);
// void get_all(rgb&);
void get_all(rgb&);
unsigned char get_red();
unsigned char get_green();
unsigned char get_blue();
void invert();
void rgb2grey();
void rgb2grey(float, float, float);
float getvalue();
private:
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char round(float);
};
Hätte ich besser short int genommen?
> Ansonsten: kleines Bild (3x3?) nehmen, Code großräumig verprinten und
Großräumig verprinten? Also an möglichst vielen Stellen
Kontroll-Ausgaben einfügen?
> per Hand nachrechnen. Debuggen halt... Und nicht vergessen, das Ergebnis
> hinterher als Unittest abzuspeichern :)
Das leuchtet ein...
Bis bald im Khyberspace!
Yadgar
[toc] | [prev] | [next] | [standalone]
| From | Markus Schaaf <mschaaf@elaboris.de> |
|---|---|
| Date | 2017-02-14 01:03 +0100 |
| Message-ID | <o7thdg$qt2$1@dont-email.me> |
| In reply to | #2018 |
Am 11.02.2017 um 14:52 schrieb Jörg "Yadgar" Bleimann: > Mittlerweile habe ich es tatsächlich geschafft, im Rahmen meines > Kommandozeilen-Bildbearbeitungsprogramms "YIP" (Yadgar's Image Processor > - ImageMagick für Arme...) so etwas wie Floyd-Steinberg-Rasterung zu > programmieren, nach dem Artikel "Floyd-Steinberg" in der englischen > Wikipedia... in Pseudocode wird der Algorithmus dort so dargestellt: > > for each y from top to bottom > for each x from left to right > oldpixel := pixel[x][y] > newpixel := find_closest_palette_color(oldpixel) > pixel[x][y] := newpixel > quant_error := oldpixel - newpixel > pixel[x + 1][y ] := pixel[x + 1][y ] + quant_error * 7 / 16 > pixel[x - 1][y + 1] := pixel[x - 1][y + 1] + quant_error * 3 / 16 > pixel[x ][y + 1] := pixel[x ][y + 1] + quant_error * 5 / 16 > pixel[x + 1][y + 1] := pixel[x + 1][y + 1] + quant_error * 1 / 16 > > Bei mir in C++ sieht das so aus: > [...] > Das Programm kompiliert problemlos (bis auf ein paar Warnungen wegen nur > in C++11 verfügbarer Funktionen), es gibt auch keinen Laufzeitfehler... > aber wenn ich mir die gerasterte Bilddatei (von RGB nach > 1-bit-Schwarzweiß) ansehe, frage ich mich, was da falsch gelaufen ist - Aus Deiner Unterhaltung mit Stefan entnehme ich, dass das Problem wahrscheinlich folgendes ist: (Zitat Wikipedia) "For optimal dithering, the counting of quantization errors should be in sufficient accuracy to prevent rounding errors from affecting the result." Ich würde also dazu raten, für pixel(x+1,y) und die Zeile pixel(left..right+1,y+1) temporären Speicher mit Fließkommapräzision vorzusehen, oder ein Festkommaformat mit Skalierungsfaktor 16. Man könnte den Algorithmus auch abwandeln und statt "nach vorn zu speichern", "nach hinten schauen". MfG
[toc] | [prev] | [next] | [standalone]
| From | Jörg "Yadgar" Bleimann <yazdegird@gmx.de> |
|---|---|
| Date | 2017-02-14 11:36 +0100 |
| Message-ID | <o7umkr$d2o$1@news.albasani.net> |
| In reply to | #2022 |
Hi(gh)! On 14.02.2017 01:03, Markus Schaaf wrote: > "For optimal dithering, the counting of quantization errors should be in > sufficient accuracy to prevent rounding errors from affecting the result." > > Ich würde also dazu raten, für pixel(x+1,y) und die Zeile > pixel(left..right+1,y+1) temporären Speicher mit Fließkommapräzision > vorzusehen, oder ein Festkommaformat mit Skalierungsfaktor 16. Man könnte > den Algorithmus auch abwandeln und statt "nach vorn zu speichern", "nach > hinten schauen". Das hat sich dann auch als Ursache für die Artefakte herausgestellt - allerdings reichte es, die Klasse "pixel" von unsigned char auf short int (16 bit) umzustellen, die Farbfehler dürften auf Überläufe zurückzuführen sein. Jetzt (Link: http://www.rock-o-data.de/fotos/Yadgar_monochrome_correct.png) sieht es annehmbar aus, wenn es auch nicht bitgleich zu dem ist, was die Floyd-Steinberg-Funktion von GIMP fabriziert... eventuell könnte es daran liegen, dass ich nur von links nach rechts, nicht ochsenwendig abtaste! Als Nächstes kommt eine Funktion zum horizontalen und vertikalen Spiegeln von Bildern, da die Ladefunktion von TGAs mit Koordinatenursprung oben links ausgeht... (fragt mich nicht nach anderen Grafikformaten! Unkomprimiertes TGA ist das einzige, was ich bis jetzt begriffen habe!) Bis bald im Khyberspace! Yadgar Now playing: Slaughter on Tenth Avenue (Synergy)
[toc] | [prev] | [standalone]
Back to top | Article view | de.comp.lang.iso-c++
csiph-web