Farbtiefe von 24Bit zu 16Bit



  • Gibt es dazu ein Algorithmus um das zu vermeiden wie den nächst passende Farbe berechnen oder an die Umgebungsfarbe anpassen?

    Ja, gibts, nennt sich Dithering. Floyd Steinberg ist ein einfacher error-difusion Dither. Liefert mittelprächtige Ergebnisse.

    Je nach Anwendung ist es aber u.U. sogar besser einen noch einfacheren Algorithmus zu verwenden, nämlich nen Matrix-Dither.
    Dazu bastelt man üblicherweise eine NxN Matrix, die z.B. so aussieht:

    7, 3, 6, 2
    1, 5, 0, 4
    6, 2, 7, 3
    0, 4, 1, 5
    

    Diese Matrix legt man dann über das Bild drüber, wie fliesen, also jeder Pixel in Eintrag in der Matrix, und eben "gekachelt", also die Matrix wiederholt sich immer. Dann addiert man einfach "R_neu = R + M[X modulo W][Y modulo H]" (wobei W und H die Höhe/Breite der Matrix sind und "modulo" eine Restklassendivision also "%" in C++), und analog für G und B.

    Wenn das Original-Bild Werte von 0-255 in z.B. Kanal R haben kann, dann kann R_neu von 0-262 gehen (der höchste Wert in der dither Matrix ist 7, 255+7=262), wobei ein 100% Rot nach dem aufaddieren der Matrix Werte von 255-262 haben kann.

    Als nächstes sieht man sich an auf welchen Wertebereich man nach dem Draufaddieren der Matrix runterquantisieren will. Nehmen wir mal die 5 Bit an, das wäre dann ein Bereich von 0-31.

    Zum Quantisieren auf 0-31 rechnest du dann einfach "R_neu = R * 31 / 255". Die einfachere, durch Shiften erreichbare Rechnung "R_neu = R * 32 / 256" bzw. durchgekürzt "R_neu = R / 8" ist nicht optimal, da so Werte im Weissbereich falsch abgebildet werden.

    ----

    Und zu Floyd Steinberg: der Algorithmus ist auch nicht wirklich wahnsinnig kompliziert, aber würde doch etwas länger zu erklären dauern. Davon abgesehen dass ich die genauen Details selbst noch nachlesen müsste. Kannst du aber sicher im Netz an 100 Stellen finden -- das ist einer dieser "den kennt jeder" Algorithmen ala Bresenham.
    Floyd Steinberg ist im übrigen für Standbilder ganz OK, für Animationen aber z.B. nicht zu gebrauchen.


  • Mod

    da haben sich dir einige fehler eingeschlichen

    hustbaer schrieb:

    Ja, gibts, nennt sich Dithering. Floyd Steinberg ist ein einfacher error-difusion Dither. Liefert mittelprächtige Ergebnisse.

    Je nach Anwendung ist es aber u.U. sogar besser einen noch einfacheren Algorithmus zu verwenden, nämlich nen Matrix-Dither.

    eigentlich ist jeder dither ein matrix-dither, was du hier beschriebst ist ordered dithering.

    Zum Quantisieren auf 0-31 rechnest du dann einfach "R_neu = R * 31 / 255". Die einfachere, durch Shiften erreichbare Rechnung "R_neu = R * 32 / 256" bzw. durchgekürzt "R_neu = R / 8" ist nicht optimal, da so Werte im Weissbereich falsch abgebildet werden.

    das stimmt leider nicht.
    beim hochrechnen muss man R_new=R*255/31 rechnen damit man gute resultate bekommt, beim runterrechnen muss man hingegen nur simpel durch 8 teilen. wuerde man R_neu = R * 31 / 255 rechnen, dann wuerde exakt nur 255 dem wert 31 zugewiesen, dafuer wuerde aber jedem anderen wert von 0 bis einschliesslich 30 9 werte zugewiesen bekommen. bei R/8 ist die aufteilung hingegen linear.



  • rapso schrieb:

    da haben sich dir einige fehler eingeschlichen

    hustbaer schrieb:

    Ja, gibts, nennt sich Dithering. Floyd Steinberg ist ein einfacher error-difusion Dither. Liefert mittelprächtige Ergebnisse.

    Je nach Anwendung ist es aber u.U. sogar besser einen noch einfacheren Algorithmus zu verwenden, nämlich nen Matrix-Dither.

    eigentlich ist jeder dither ein matrix-dither, was du hier beschriebst ist ordered dithering.

    Hupps. Ja, ok, soll sein 😃

    Zum Quantisieren auf 0-31 rechnest du dann einfach "R_neu = R * 31 / 255". Die einfachere, durch Shiften erreichbare Rechnung "R_neu = R * 32 / 256" bzw. durchgekürzt "R_neu = R / 8" ist nicht optimal, da so Werte im Weissbereich falsch abgebildet werden.

    das stimmt leider nicht.
    beim hochrechnen muss man R_new=R*255/31 rechnen damit man gute resultate bekommt, beim runterrechnen muss man hingegen nur simpel durch 8 teilen. wuerde man R_neu = R * 31 / 255 rechnen, dann wuerde exakt nur 255 dem wert 31 zugewiesen, dafuer wuerde aber jedem anderen wert von 0 bis einschliesslich 30 9 werte zugewiesen bekommen. bei R/8 ist die aufteilung hingegen linear.

    Das stimmt wohl.

    Wenn man einfach ohne dithering runterrechnet dann ja, dann ist R/8 die richtige Umrechnung.

    Wenn man allerdings vorher über ne fixe Matrix (oder auch Zufallszahlen) Werte von 0-7 draufrechnet, dann erweitert man den Wertebereich ja von 0-255 auf 0-262. Die richtige Umrechnung ist dann *31/255. Glaubs mir oder probiers aus.
    Ganz ganz sicher. Ehrenwort.

    Ich hätte vielleicht dazuschreiben sollen dass ich die Umrechnung auf mein Beispiel darüber beziehe, also wo eben Werte von 0 bis 7 vorher draufaddiert werden -- dachte mir aber das wäre klar.

    (abgesehen davon dass das alles natürlich sowieso nicht 100% korrekt ist weil man mit Gamma-korrigierten Werten arbeitet wo man mit Power-Levels arbeiten müsste, aber bei Umrechnung von 8 auf 5 Bit sieht man den Unterschied kaum)

    EDIT: mit dem "9 Ausgangswerte pro Ausgabewert" hast du natürlich recht, das ist halt ne blöde Limitierung beim ordered dither. Kann man sich auch höchstens über ne grössere Dither-Matrix helfen. Oder eben clippen, was ich persönlich aber für die weniger attraktive Lösung halte.


  • Mod

    hustbaer schrieb:

    Ich hätte vielleicht dazuschreiben sollen dass ich die Umrechnung auf mein Beispiel darüber beziehe, also wo eben Werte von 0 bis 7 vorher draufaddiert werden -- dachte mir aber das wäre klar.

    ja das ist echt nicht ersichtlich, nett waere es wenn du uns noch eklraerst wie man auf diese formel kommt. Ich haette simpel *31/263 gerechnet, was wohl erstmal eher nachvollziehbar ist.



  • [quote="rapso"]

    hustbaer schrieb:

    Ich haette simpel *31/263 gerechnet, was wohl erstmal eher nachvollziehbar ist.

    für mathe-genies ist *31/263 noch im kopf errechnbar und das in (für menschlicher zeit) ziemlich schneller zeit ... aber für den CPU wirds mit ohne floating "schwer" sein , oder?, also nen DWORD kann man da nicht benutzten

    wie schon gesagt wurde /8, bzw um 3bits "Logik shift right" (LSR), sollte dasfür die performace optimalste sein.

    ob das ganze nun als matritze oder DWORD-array behandelt wird (wobei eine matritze nix anderes ist - zumindest nicht im Speicher) ist nun "egal"

    das einzege problen was es hier zu lösen gibt ist doch: aus einen 32/24-bit wert einen 16-bit wert zuerstellen.

    ich glaube das der thread-starter sich schon seine lösung heraus gesucht hat - aber für die, die es noch nen bissel im gehirn spielen lassen wollen:

    32bit: AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB
    24bit: XXXXXXXXRRRRRRRRGGGGGGGGBBBBBBBB
    16bit: 0000000000000000RRRRRGGGGGGBBBBB
    die "sinnvollen" bits aus32/24 bit sind:
    ...... xxxxxxxxRRRRRrrrGGGGGGggBBBBBbbb (Groß=wichtig klein=unwichtig-> zu löschen9


  • Mod

    [quote="LinkeT"]

    rapso schrieb:

    hustbaer schrieb:

    Ich haette simpel *31/263 gerechnet, was wohl erstmal eher nachvollziehbar ist.

    hatte mich vertippt, meinte natuerlich 32/263. aber dennoch...

    für mathe-genies ist *31/263 noch im kopf errechnbar und das in (für menschlicher zeit) ziemlich schneller zeit

    emm ja und? sprechen wir hier nicht mehr uebers programmieren? oder waren all deine beispiele vorher fuer kopfrechnen gedacht?

    aber für den CPU wirds mit ohne floating "schwer" sein , oder?, also nen DWORD kann man da nicht benutzten

    wieso kannst du ein dword nicht mehr nutzen? soviel anders ist das ergebnis nicht als mit *31/255 oder meinst du im hinblick auf die range vom resultat? also dass mehr als 31 rauskommen kann?

    wie schon gesagt wurde /8, bzw um 3bits "Logik shift right" (LSR), sollte dasfür die performace optimalste sein.

    jo, und liefert die richtigen resultate im bereich von 0 bis 255

    ich glaube das der thread-starter sich schon seine lösung heraus gesucht hat - aber für die, die es noch nen bissel im gehirn spielen lassen wollen:

    32bit: AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB
    24bit: XXXXXXXXRRRRRRRRGGGGGGGGBBBBBBBB
    16bit: 0000000000000000RRRRRGGGGGGBBBBB
    die "sinnvollen" bits aus32/24 bit sind:
    ...... xxxxxxxxRRRRRrrrGGGGGGggBBBBBbbb (Groß=wichtig klein=unwichtig-> zu löschen9

    also da du meinen beitrag oben gequotet hast, muss ich dir sagen, dass dein ganzer text trotzdem nicht beantwortet, wie du auf *31/255 kommst, wenn du den bereich abdecken moechtest, den du von 0 bis 262 gesetzt hast.



  • 0-262 ?
    2^8=256 -> 0-255 was soll da der bereich von 256-262?!

    32/263=0,12167300.... -> FLOAT so weit ich weis muss dazu die FPU benutzt werden und somit weren mehere Befehle ausgeführt (im native code) -> es wird langsamer

    habs eben mal getestet, mit:

    int main()
    {
    	DWORD start;
    	DWORD ende;
    	DWORD t;
    	DWORD x;
    	for(DWORD x=0;x<10;x++)
    	{
    		start=timeGetTime();
    		for(DWORD i=0;i<10000000;i++)
    		{
    			t=x/8;
    			//t=x*32/263;
    		}
    		ende=timeGetTime();
    		ende=ende-start;
    		std::cout<<ende<<std::endl;
    	}
    	int y;
    	std::cin>>y;
    }
    

    im disassembly steht
    t=x/8;
    00411687 mov eax,dword ptr [x]
    0041168A shr eax,3
    0041168D mov dword ptr [t],eax

    und

    t=x*32/263;
    00411687 mov eax,dword ptr [x]
    0041168A shl eax,5
    0041168D xor edx,edx
    0041168F mov ecx,107h
    00411694 div eax,ecx
    00411696 mov dword ptr [t],eax

    Fazit
    /8 ist ca 4 mal schneller



  • Auch wenn für diese Division nicht die FPU (x*32/263 ist eine Ganzzahldivision) verwendet werden muß, hast du im Ergebnis recht - die Division durch 8 kann vom Compiler zu einem Shift-Right optimiert werden, die Division durch 263 nicht.


  • Mod

    LinkeT schrieb:

    0-262 ?
    2^8=256 -> 0-255 was soll da der bereich von 256-262?!

    liess bitte den thread durch bevor du sowas fragst.

    hustbaer schrieb:

    Wenn das Original-Bild Werte von 0-255 in z.B. Kanal R haben kann, dann kann R_neu von 0-262 gehen (der höchste Wert in der dither Matrix ist 7, 255+7=262), wobei ein 100% Rot nach dem aufaddieren der Matrix Werte von 255-262 haben kann.

    Fazit
    /8 ist ca 4 mal schneller

    Zum Quantisieren auf 0-31 rechnest du dann einfach "R_neu = R * 31 / 255". Die einfachere, durch Shiften erreichbare Rechnung "R_neu = R * 32 / 256" bzw. durchgekürzt "R_neu = R / 8" ist nicht optimal, da so Werte im Weissbereich falsch abgebildet werden.

    4mal schneller etwas falsches zu machen ist keine kunst.



  • Fazit /8 ist ca 4 mal schneller

    ich hab jetzt die genaue zyklenzahl der integerdivision aktueller prozessoren nicht im kopf aber wuerde mindestens faktor 20 schaetzen.
    darum erweitert man die faktoren so, dass man durch eine zweierpotenz dividiert.



  • hellihjb schrieb:

    Fazit /8 ist ca 4 mal schneller

    ich hab jetzt die genaue zyklenzahl der integerdivision aktueller prozessoren nicht im kopf aber wuerde mindestens faktor 20 schaetzen.
    darum erweitert man die faktoren so, dass man durch eine zweierpotenz dividiert.

    habs errechnet ist "nur" faktor 4

    waarum sollen verfälschungen im Weisbereich auftretten? dies passiert doch in jedem fall da es nun nicht mehr so genau ist. Um bei grün nicht zuviel an daten wegzu schneiden könnte man auch durch 4 teilen (SHR 2).

    255-248 -> 31
    247-240 -> 30

    ausser das es nicht mehr so genau ist seh ich keine weitere verfälschung


  • Mod

    LinkeT schrieb:

    waarum sollen verfälschungen im Weisbereich auftretten?

    Hustbaer schrieb:

    Wenn man allerdings vorher über ne fixe Matrix (oder auch Zufallszahlen) Werte von 0-7 draufrechnet, dann erweitert man den Wertebereich ja von 0-255 auf 0-262.

    somit erhaelst du von 248 bis 262 den werte >=31.



  • habs errechnet ist "nur" faktor 4

    wie das?



  • Also.

    1. Wie ich auf *31/255 komme?
      Da 255 nach Draufaddieren der Matrix 255-262 sein kann, 254 -> 254-261, 253 -> 253-260 etc. reicht es wenn nur 255 auf 31 abgebildet wird, alles andere auf <= 30, daher hab ich damals einfach *31/255 verwendet. Hat halbwegs richtig ausgehesen, ich hab nicht weiter darüber nachgedacht, deswegen bin ich bis jetzt dabei geblieben. Die ursprüngliche Forderung wird auch erfüllt, der grobe Fehler im Weissbereich ist weg, aber siehe 2)

    2. Bin jetzt aber draufgekommen (nachdem ich mir das alles noch etwas genauer angesehen habe), dass es sowieso nicht optimal ist zuerst die dither-Werte drauf zu addieren, und dann zu multiplizieren/dividieren -- man sollte es umgekehrt machen. Wenn man zuerst addiert und dann multipliziert/dividiert erhält man unregelmässiges "banding" (die Abstände zwischen den Bändern sind mal 39 Werte, dann wieder 31 etc.). Besser wäre wohl:

    v' = ((v * 249 + 124) / 256 + DITHER[x&3, y&3]) / 8
    

    Wenn man so rechnet ist der Abstand zwischen den Bändern immer 34 oder 35, vor dem ersten Band liegen 17 Werte und nach dem letzten nochmal 17 - schöner könnte es nicht sein.
    Also vorher den Bereich 0-255 auf 0-248 zusammenstauchen (da entsteht dann Banding, dafür regelmässig), und dann erst dithern (wo dann kein Banding mehr entsteht).

    1. Das Rummultiplizieren/Dividieren ist kein Problem, da man es einfach durch einen Table-Lookup ersetzt 😉
    v' = ((v * 249 + 124) / 256 + DITHER[x&3, y&3]) / 8
    wird also zu 
        v' = (LUT[v] + DITHER[x&3, y&3]) >> 3
    

    Das ist die Beste Umrechnung die ich bis jetzt gefunden habe.


  • Mod

    wenn man es schnell haben moechte

    r_new = (r>>3) ^ ( ((r&7)+ DITHER[x&3, y&3])>>3 );
    


  • der nutzen der lut ist fragwuerdig.
    tu' die dither-matrix doch fixedpoint, raeum' die 124 mit rein und shifte einmal am ende:

    (v*249 + dither[][]) >> 11;
    

    passt dann auch ganz gut in mmx.



  • hellihjb schrieb:

    der nutzen der lut ist fragwuerdig.
    tu' die dither-matrix doch fixedpoint, raeum' die 124 mit rein und shifte einmal am ende:

    (v*249 + dither[][]) >> 11;
    

    passt dann auch ganz gut in mmx.

    Ok, die Multiplikation tut sicher nicht sooo weh.



  • Hi,

    melde mich mal wieder nachdem ich einige Sachen mir durchgelesen und probiert habe.
    Also die Sache sieht jetzt inmoment so das ich versuche den Floyd Steinberg Dithering Algorithmus zu implementieren.

    Es sieht erstmal wie folgt aus:

    #define IMAGEBUFFER 128 // image buffer size: 128*3Bytes(RGB) = 384 Bytes
    
    typedef struct
    {
    	uint8_t B, G, R;
    }RGBTriple;
    
    typedef struct
    {
    	uint32_t width;
    	uint32_t height;
    	RGBTriple* pixels;
    }BMPImage;
    
    void writeBMPImage(uint8_t x, uint8_t y, uint16_t imageAddr);
    void floydDithering(BMPImage* image);
    

    IMAGEBUFFER ist deswegen da weil ich nicht genügend RAM zur Verfügung habe und ich das Bild in teilen laden muss eben in 384 Byte Stückchen. 🕶
    so und jetzt wird es hässlich: 😉

    Floyd Steinberg Dithering exemplarisch angewendet auf Rot:

    void floydDithering(BMPImage* image)
    {
    	int i=0, errorDiff=0;
    
    	for (i=0; i<IMAGEBUFFER; i++)
    	{
    	//RED
    		errorDiff = image->pixels[i].R - (image->pixels[i].R & 0xF8);
    
    		if (i+1 < IMAGEBUFFER)
    			image->pixels[i+1].R += (errorDiff*7) >> 4; // right (7/16)
    		if (i-1+image->width < IMAGEBUFFER)
    			image->pixels[i-1+image->width].R += (errorDiff*3) >> 4; // buttom-left (3/16)
    		if (i+image->width < IMAGEBUFFER)
    			image->pixels[i+image->width].R += (errorDiff*5) >> 4; // buttom (5/16)
    		if (i+1+image->width < IMAGEBUFFER)
    			image->pixels[i+1+image->width].R += (errorDiff*1) >> 4; // buttom-right (1/16)
    	}
    }
    

    Problem ich weiss nicht wie die error differenz genau berechnet wird man sagt ja es soll die Differenz der Orginial Farbe und der nächst gelegende Farbe sein. Ist dann meine Error Differenz dann korrekt berechnet worden?

    Dann hab ich noch eine Frage wo ich nicht weiss ob das am Algorithmus selber liegt oder vielleicht an der falschen Implentierung: Ich habe bei weissen Flächen immer ein Wechsel zwischen weiss und schwarz soll das so sein?

    Ich habe am Anfang ganz vergessen zu sagen das ich das Dithering auf sehr kleine Bilder anwende ca. 96x96 Pixel gross bringen dann die Algorithmen überhaupt noch was?

    Gruss,
    xmarvel



  • Äh. Grundsätzlich sieht das nicht ganz verkehrt aus. Allerdings solltest du ein paar Dinge beachten und evtl. überdeknen ob du wirklich Floyd Steinberg Dithering verwenden willst wenn du so wenig Speicher zur Verfügung hast.

    #1: Wenn du den Fehler durch 16 dividierst (bzw. mal 5/16tel etc. nimmst), dann solltest du beachten dass dabei Rundungsfehler entstehen -- bzw. die Kommastellen einfach weggeschnitten werden. Wenn als Ausgabe nur reines Schwarz bzw. reines Weiss zur Verfügung stehen, und das Bildmaterial welches reinkommt Werte von 0-255 hat, dann sind die Fehlerdifferenzen immer ordentlich gross, und die Rundungsfehler fallen nicht so stark auf. Wenn du dagegen von 24 Bit auf 16 Bit runterrechnest, dann sind die Fehler enstprechend klein, und "Fehler in der Fehlerberechung" fallen stärker auf.
    Beispiel: soll = 17, darstellbarer Wert = 16 -> Fehler ist 1. Fehler * 7 / 16 = 0. Blöd.

    Entweder du nimmst also 16 Bit integers her, und multiplizierst alles mit 16 oder gleich 32 bzw. 64 -- dann entstehen beim "Fehler aufteilen" entsprechend weniger Fehler.
    Oder aber du teilst den Fehler so auf dass wenigstens die Summe stimmt, also so inetwa:

    int error = ...;
    
        int error7 = (error * 7 + 8) / 16;
        int error5 = ((error - error7) * 5 + 4) / 9;
        int error3 = ((error - error7 - error5) * 3 + 2) / 4;
        int error1 = error - error7 - error5 - error3;
    

    Geht natürlich langsamer, da eine Division durch 9 dabei ist. Allerdings kannst du *5 /9 auch z.B. durch *71 / 128 ersetzen -- stimmt zwar nicht 100%, sollte aber reichen.
    Zumindest würde ich das mal ausprobieren, wenn die andere Option (uint16_t verwenden und mit mehr Präzision rechnen) aus irgendeinem Grund falchfällt.

    #2: Du solltest "gesättigt" addieren, sollte auch klar sein. Also nicht r = r + fehler, sondern r = min(max(r + fehler, min_r_value), max_r_value).

    #3: Du solltest nicht clippen, sondern runden. Also statt r = r & 0xF8 heisst das dann r = min((r + 4) & 0x1F8, 0xF8). Und weils hier gleich dazupasst: du solltest mit signed integers rechnen. Das Bild selbst darf dabei ruhig als unsigned Bytes vorliegen (wenn du "geästtigt" addierst kann im Framebuffer sowieso nie was landen was "den Rahmen sprengt"), bloss der Fehler kann negativ werden, das sollte man beachten.

    #4: Das grösste Problem: Du solltest die Fehler die du z.B. beim letzten Pixel in einer Zeile (wenn die Kachel schmäler ist als das ganze Bild), bzw. auch in der letzten Reihe einer "Kachel" aufteilen willst irgendwo puffern. Tust du das nicht (so wie in deinem Programm z.B. wo du den Fehler einfach "wegwirfst"), werden vermutlich auch bei "nur" 24 -> 16 Bit Dithering hässliche Streifen sichtbar werden, nämlich dort do du "den Fehler unterschlagen" hast.
    Eine einfache Möglichkeit wäre immer 2 komplette Zeilen in den Speicher zu laden. Eine Zeile ditherst du (A), auf die 2. kannst du den Fehler draufrechnen (B). Bist du mit einer Zeile fertig vertauscht du die Zeiger so dass B zu A wird (oder kopierst den Inhalt von B nach A), und lädst die nächste Zeile als neues B nach. Wenn es keine Zeile mehr nachzuladen gibt (also bevor du die letzte Zeile angehst), kannst du die alten Werte einfach in B stehen lassen, und die Fehler die du beim letzten Durchgang verteilst kannst du danach einfach wegwerfen.

    Sollte es nicht möglich sein immer wenigstens 2 Zeilen zu laden würde ich empfehlen nicht gerade einen Error Diffusion Dither ala Floyd Steinberg zu verwenden, sondern eher einen einfachen Ordered Dither. Sieht auch um Welten besser aus als ohne Dithering, geht schnell und einfach zu berechnen, und du musst eben nicht irgendwelche alten Fehlerwerte "aufheben", sondern kannst jeden Pixel einzeln bearbeiten. Das einzig Wichtige dabei ist dann dass du weisst wo im fertigen Bild er steht (X und Y), damit du den richtigen Wert aus der Dither Matrix auswählen kannst.

    So. Das sind zwar jetzt keine 100% definitiven Wahrheiten, aber zumindest die Sachen die ich mal ausprobieren würde. Sozusagen. Quasi.

    p.S.: noch was zum Thema Fehler berechnen, weil du gefragt hast:

    // lesen, quantisieren, fehler ausrechnen
        int red = image[x][y].red; // der Wert im Bild + ggf. bereits akkumulierte Fehler
        int red_out = min((red + 4) & 0x1F8, 0xF8); // der Wert den ich darstellen kann
        int red_error = red - red_out; // der Fehler "original Wert - darstellbaren Wert"
    
        // zurückschreiben & fehler verteilen
        image[x][y].red = red_out;
        image[x+1][y].red = (red_error * 7 + 8) / 16;
        //...
    


  • Hi,

    erstmal danke für eure Hilfe hab das leider nicht geschafft mit dem Floyd Steinberg Algorithmus zu programmieren da das etwas komplizierter wurde mit dem wenigen RAM, ich konnte zwar zwei Zeilen des Bildes jeweils noch Zwischenspeichern laden aber ich hatte ziemliche Probleme im Array Bereich zu bleiben. (Ich werd den Algorithmus aber sicherlich später nochmal testen auf ein normalen PC)
    Inoment benutze ich jetzt das Ordered Dither Matrix wie hustbaer empfohlen hat.
    Die unterschiede mit und ohne Dithering bei so kleinen Bilder fallen zwar nicht so gross aus wie erhofft aber es ist ok.

    Gruss,
    xmarvel


Anmelden zum Antworten