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