Farbsuche in Palette (nearest Color Index)

direkt den Code des neuen Jars ausgeführt sieht es mit FS, JJN und Stucki wahrlich blau aus,

für die HistColor-Klasse hatte ich schon beim ersten Eingriff gesagt, dass ich bei der
setARGB(int argb)-Methode die neuen Variablen r, g, b usw. aktualisieren musste,
warum auch immer, habe ich mir überhaupt nicht näher angeschaut

direkt darunter gibt es noch eine Methode set(HistColor c), die auch argb neusetzt,
dort fehlte bisher entsprechendes, aber besser nicht den Code kopieren, sondern diese Methode setARGB() aufrufen lassen,
schon sieht es mit FS, JJN und Stucki auch akzeptabel normal aus,
was immer das ist, wie so vieles nie angeschaut

war schon in Posting #13 dabei, war mir nicht aufgefallen weil es bei meinem Testfall (None, MedianCut) anscheinend nicht vorkam, keine Fehlerquelle,
all das ist extrem provisorisch kurz testweise eingebaut, so lange bis eine Variante funktionierte,

wenn es wirklich eingesetzt werden soll braucht es detaillierte Code-Einarbeitung,
z.B. eine Grundsatzentscheidung, ob Color weiterhin ein int argb für alles zusammen und einzelne get-Methoden hat,
dann auf für die Würfelindexe, oder ob r, g, b einzelne Variablen werden,
nicht beides wild durcheinander

Ich hab mir den Thread aus reiner Neugierde durchgelesen, aber nun stellt sich mir zu dem gezeigten Bild eine Frage. Vielleicht ist sie völlig irrelevant, aber ich will sie trotzdem mal stellen.

Hier vermute ich, dass man alle acht Nachbarquadrate, oder im dreidimensionalen alle 26 Nachbarwürfel ansehen muss.

Was passiert aber, wenn all diese Nachbarzellen leer sind? Dann müsste man die Suche ja noch weiter ausdehnen.

es darf eben keine leeren Zellen geben, am Anfang beim Aufbau der Struktur muss je Zelle der erhöhte Aufwand betrieben werden,
Kandidaten zu finden, egal wie weit entfernt,

aber nicht alle aus der Palette/ aus dem Gesamtraum, sondern nur ein paar davon,
ich hatte den nahesten Punkt egal wo vom Mittelpunkt der Zelle gewählt + alle weiteren mit einem gewissen zusätzlichen Abstand, Zellendiagonale

[QUOTE=Crian]Was passiert aber, wenn all diese Nachbarzellen leer sind? Dann müsste man die Suche ja noch weiter ausdehnen.[/QUOTE]Das ist genau der Punkt, der teilweise schief geht und zwar nur bei Originalfarben verfälschenden Dither-Algorithmen (Error-Smear). Im Demo funktioniert nur NONE zuverlässig.
@SlaterB : Ich habe nun überall wo argb verändert wird auch schön artig die anderen Werte mit geändert, besser gesagt, alle Änderunge zentral in die “setARGB()”-Methode laufen lassen. Wenn der Algo jetzt auf einen leeren Würfel stösst, wird die Farbe schlicht reflektiert, also nicht aus der Palette genommen. Das fällt auch nur recht selten auf, am schlimmsten bei z.B. Landschaftsphotos mit den Hauptfarben Blau und Grün und auch nur beim Population-Cut, denn der verwendet immer die volle Palette, während die anderen meistens eine Reserve bieten. Bei solchen Bildern genügt ein einziger roter Pixel um die Palette überlaufen zu lassen. Echt traurig, dass dieses Verfahren nicht 100%ig funktioniert.

jedes Mal muss ich mich mehr und mehr über deine Aussagen wundern

es gibt per Definition keine leeren Würfel!, nicht im meinen Code, wenn du das Verfahren änderst, dann kritisiere bitte nicht ‚dieses Verfahren‘,

‚Reflektion einer Farbe‘ kann es ebenso schlicht nicht geben, weil es nicht vorgesehen ist, undenkbar,


ich weiß nicht über welchen Code du nachdenkst, aber ‚meiner‘ ist doch nun wirklich nicht so kompliziert als dass man da diskutieren müsste,

schau dir nochmal den Code von #17 an oder in deinem Programm, es sind doch nur wenige übersichtliche Schritte
(wenn auch Code nicht schön, aber bei so einfachen Aufbau selbst dann klar)
und oft genug schon von mir in Worte gefasst, Ziel klar, selbst falls Code zufällig Fehler enthalten sollte

            for (int j = 0; j < r; j++)
                for (int k = 0; k < r; k++)
                {
                    Set<Col> cand = new HashSet<Col>();
                    addCandidates((i + 0.5) * r2, (j + 0.5) * r2, (k + 0.5) * r2, cand, palette);
                    Col[] candA = cand.toArray(new Col[cand.size()]);
                    Arrays.sort(candA);
                    room**[j][k] = candA;
                }

der Würfel/ Zellenraum wird befüllt, JEDE Zelle erhält ihre Kandidaten aus der Palette,
nix bleibt leer, nix bekommt irgendwelche Sonderfarben von Palette abweichend oder sonstwie komisches,
hängt natürlich von der Methode addCandidates() ab, gleich hin da:

         Col mid = new Col((int)r, (int)g, (int)b);
        int smallestError = Integer.MAX_VALUE;
        int maxError = Integer.MAX_VALUE;
        for (Col color2 : pal)    {
            int currentError = distanceSquared(mid, color2);
            if (smallestError > currentError)   {
                smallestError = currentError;
                maxError = smallestError + radius;
            }
            if (currentError < maxError)  {
                cand.add(color2);
            }
        }
        Iterator<Col> iter = cand.iterator(); // vorher viele eingesammelt, nun wieder streichen was zuviel ist
        while (iter.hasNext()) {
            Col next = iter.next();
            int currentError = distanceSquared(mid, next);
            if (currentError > maxError)  {
                iter.remove();
            }
        }
     }

nach der bekannten Suche wird der naheste vom Mittelpunkt genommen, mit radius evtl. noch ein paar mehr auch ziemlich nahe,
wenn man mal nicht Fehler in so kleinen Code annimmt, dann können maximal wegen zu kleinen radius eigentliche benötigte weitere Palettenfarben fehlen,
aber weniger als 1 Farbe ist unmöglich, eine wird immer gefunden

schließlich nur noch die Verwendung der Zellen in der Suche,
Code schön kurz unter Aussparung der bekannten, ähnlichen Such-Methode nearestColor wie zuvor:

        {
            check += nearestColor(cs, room[cs.ri][cs.gi][cs.bi]).hashCode();
        }

hier noch im Test lediglich mit Checksumme statt Farbübernahme, aber das sind ja nur Details, auf deren Korrektheit man eben achten muss,

im wesentlichen gilt jedenfalls: für jedes Pixel die Suche im zugehörigen Würfel/ Zelle ausführen, die kann nicht leer sein, und wieder wird garantiert eine Farbe davon gewählt,
garantiert ist eine Palettenfarbe das Ergebnis,
unmöglich (außer durch triviale Fehler wie Vergessen der Übernahme des Ergebnisses) kann ein Pixel/ eine Suchfarbe unveränderlich bleiben,
es sei denn die Palettenfarbe ist genau eine Bildfarbe, direkter Treffer, Distanz 0

es gibt wahrlich genug allgemein am Verfahren zu verbessern,
und auf vorhandene Code-Fehler kann man immer stoßen und sie korrigieren,
aber deine Punkte sind regelmäßig undenkbar, wenn ich mich jetzt nicht völlig irre, wie kommt das?

wenn du schon anscheinend keine genauen Vorstellungen von dem hast was ich habe
(etwa: keine Würfel/ Zellen leer! immer noch oft genug zu wiederholen, hätte ich nie gedacht)
dann schreibe doch wenigstens nicht so überzeugt, dass etwas nicht funktioniert,
nutze doch lieber die Frageform, ‚wie soll es in Situation Xy ablaufen?‘ usw.

Dann frag’ ich doch mal…
Welcher Würfel beinhaltet im Extremfall den einzigen roten Punkt, welcher schon bei der Pallettenerstellung auf der Strecke blieb, weil der Rest des Bildes nur Grün- und Blautöne hat? Für ein solches Extrem sind sogar die Würfeldiagonalen als Radien zu wenig. Da findet sich keine Tabelle in der Nähe, das ist das Problem was ich grad habe.
Allerdings versuche ich grade, die Palette in jedem dieser Würfel komplett bei zu behalten und nur nach den verschiedenen Würfelmittelpunkten zu sortieren. Ich denke mal, dann wären auch wieder 16 * 16 * 16 Würfel genug.

ich bin nicht ganz sicher, was du damit meinst,

beziehst du dich auf ein theoretisches Problem, einen ‚Extremfall‘, wie in einen Würfel eine weit entfernte Palettenfarbe hineinkommen könnte?

wie gesagt wird als erstes ganz normal der naheste Punkt gesucht, dass kann auch am ganz anderen Ende sein,
erst von dem ausgehend gilt der radius zusätzlich, denn ein Punkt auf der anderen Seite etwas weiter entfernt könnte noch besser passen,
es wird also nicht nur innerhalb des festen radius X um den Mittelpunkt gesucht, sondern beliebig weit


viel eher klingt es dann aber doch nach einem konkreten Durchlauf, bei dem unerklärlicherweise ein rote Farbe ins Bild kommt?
aus dem Stehgreif kann ich das auch nicht erklären, könntest du den Code dazu bzw. die Einstellungen für das bisherige Jar nennen und Hinweis, wo der rote Punkt dort im Bild zu finden ist falls nicht überdeutlich?

ansonsten auch immer interessant selber zu debuggen (ich preise an, was ich dann tun würde :wink: ),
mit if den Einzelfall herausgreifen, für diesen ganz genau die Schritte anschauen, die gemacht werden,
welcher Würfel ist dazu gewählt, welche Kandidatenfarben sind darin, welche davon wird ausgewählt usw.,

vielleicht auch erst ein Fehler in der nachgelagerten Verarbeitung, ‚Fehlerstreuung‘ usw.?

Unerklärlich ist dieses Rot nicht. Einerseits kann es im Original vorhanden sein und Opfer der Reduzierung geworden sein, andererseits kann es durch die Fehlerstreuung (obwohl das eher unwahrscheinlich ist) berechnet worden sein. Zumindest taucht ein solcher Punkt kaum in der Palette auf und deswegen auch in keinem der Würfel. Wenn jetzt aber der Algo auf diese Farbe kommt, so landet er in einem Würfel, wo nicht mal die gewählten Radien (bei dir 6,75 * Kantenlänge) ausreichen würden. Der Radius müsste demnach immer so groß gewählt werden, dass sich die beiden am weitesten auseinander liegenden Würfel noch überschneiden, sprich, die Kantenlänge des gesamten Würfels, womit man schon wieder viel zu viele Kanditaten in einen Würfel bekäme.
Ich habe das mal mit 44 Würfeln und 2Kantenlänge eines Teilwürfels als Radius illustriert.

Ein entsprechendes Bild müsstest du dir selber machen, ich habe da nur eine private Aufnahme für herhalten lassen, also etwas mit viel Grün und Blau (Urlaubsfotos z.B.) und da einen bzw unmerklich viele Pixel rot gefärbt. In der Palette befanden sich tatsächlich nur grüne und blaue Farben und die roten sorgten am schluss dafür, dass in dem reduzierten Bild mindestens eine Farbe mehr auftauchte, als erlaubt war (Vorzugsweise bei Popularity-Cut).

also doch das theoretische, ich habe es in der Antwort zuvor bewußt etwas kürzer gehalten,
aber es steht da praktisch auch schon, ebenso wie oft zuvor :wink:

wie auch bei der Antwort zu Crian Frage

einmal mehr:
von der Zelle bei rot aus vom Mittelpunkt wird die naheste Palettenfarbe beliebiger Entfernung gesucht,
irgendeiner der grünen anscheinend,

erst von diesem Fund aus gilt der kleine Radius, erst von dort aus noch etwas mehr,
sei beispielsweise die Zelle 16 breit, der Radius (ungeachtet aller Quadrierungen) großzügig 30, die grünen ~1000 entfernt und die blauen ~1500,
dann wird z.B. 1042 als minimale Distanz für die rote Zelle gefunden, sowie alle weiteren Farben bis zu 1042+30 = 1072 Entfernung dazu,
denn für Suchpunkte in der Zelle muss nicht der naheste Fund vom Mittelpunkt aus der einzig richtige sein


wenn du auch einen konkreten Fall hast, bitte ich dich immer noch ihn zu posten, sofern du an einer Lösung durch mich interessiert bist,
muss ja kein privates Bild sein sondern irgendein Testbild,

ich kann selber kaum anfangen danach zu suchen, weil ich ja überzeugt bin, dass ein Fehler ausgeschlossen ist, was bringt mir viel Mühe und dann vielleicht doch kein Fehler,
und weil ich auch nicht alle deine Programmteile kenne die auch Einfluss haben können

Hahaha… darf ich fragen, wo du transparente Farben ablegst? Mom, ich sag’s dir - in deinen Würfeln, weil du den Alpha-Kanal gar nicht prüfst. Wenn sich also eine transparente Farbe in der Palette befindet, diese aber nicht 0 war, wird sie auch durch andere Fehlfarben gefunden und so bekommt man irgendwann genau eine Farbe zu viel. Ich habe die transparente Farbe deswegen komplett 0 gesetzt und bei “addCandidates()” dafür gesorgt, dass sie übersprungen wird. Seitdem keine Probleme mehr, bis auf die Kleinigkeit, dass beide Suchmethoden tatsächlich nicht kongruent arbeiten.

Ergebnisse bei Floyd-Steinberg (65536 aus 224340 Originalfarben)

Median-Cut neu: 60224 Farben
Median-Cut alt: 59335 Farben

Population-Cut neu: 61919 Farben
Population-Cut alt: 58763 Farben

Frage mich, woher das nu wieder kommt. naja, mal schauen, wie lang das gut geht.

Edit:
Die letzte Version der “addCandidates()”-Methode sieht zumindest so aus und damit funktioniert es bisher:

				Set<HistColor> cand, List<HistColor> pal) {

			HistColor mid = new HistColor((int) r, (int) g, (int) b);
			int smallestError = Integer.MAX_VALUE;
			int maxError = Integer.MAX_VALUE;
			for (HistColor color2 : pal) {
// Dieser Teil scheint wichtig, zumindest, wenn Bilder mit Alphakanal verwendet werden.
				if(color2.getAlpha() == 0) {
					continue;
				}
// Wichtig Ende
				int currentError = mid.distanceSquared(color2);
				if (smallestError > currentError) {
					smallestError = currentError;
					maxError = smallestError + radius;
				}
				if (currentError < maxError) {
					cand.add(color2);
				}
			}
			Iterator<HistColor> iter = cand.iterator();
			while (iter.hasNext()) {
				HistColor next = iter.next();
				int currentError = mid.distanceSquared(next);
				if (currentError > maxError) {
					iter.remove();
				}
			}
		}```

was eine transparente Farbe ist und was dann passieren soll weiß ich gar nicht wirklich,

hätte ich noch öfter drauf hinweisen können, andererseits ist der Code nun wirklich selbsterklärend,
einfach anschauen, auch im Vergleich zu deinen früher, dann siehst du was alles wegfiel/ dazukam,
und dann ist ja noch das Modell, zu überlegen welche Indexe würden dazu berechnet usw.,

wenn du dir über transparente Farben bewußt bist, dann auch bedenken was hier damit passiert,
alle meine Aussagen beziehen sich natürlich nur auf die Farben, die ins Modell passen, die r, g und b haben (und bisher auch nur maximal je 8 Bit, bei mehr Bit wieder neu)

ein Test in jedem Fall der richtige Weg, anschauen, herausfinden dass die transparente Farbe das Problem ist und lösen,
so einfach gehts


eine transparente Farbe in der Palette? ist das sinnvoll, nicht eher zu verhindern sofern möglich?

        return transparencyIndex;
    }```
hast du ja ganz am Anfang in der Suchmethode, als Test für die Suchfarbe color1, das sicher auch wieder zu bedenken, 
ist transparencyIndex der Index einer extra vorhandenen Transparenz-Farbe in der Palette?
alles Details, die ich gar nicht richtig kenne

Ja natürlich ist eine transparente Farbe in der Palette sinnvoll, irgendwo muss man doch den Transparency-Index anbringen, sofern eine im Originalbild vorhanden war. Einen kompletten Alpha-Kanal schließe ich durch “premultiply()” aus. Farben, die 100% transparent sind, setze ich auf in allen Kanälen inkl. Alpha auf 0 (vorher hatte ich nur den Alpha-Kanal 0 setzen müssen) alle anderen multipliziere ich mit dem Alpha-Kanal und setze den Alpha-Kanal dann auf 1. In der “addCandidates()”-Methode wurde ein vorhandener Alpha-Kanal übersehen und die “Farbe” den Würfeln zugeteilt, wo sie anschliessend auch als normale Farbe identifiziert und als zusätzliche Farbe gewählt werden konnte. Bei der Ursprünglichen Suche hatte ich diesen Alpha-Kanal deswegen vorher abgefragt und bei 0 schlicht die transparente Farbe (also 0) zurückgegeben. In der “distanceSquared()”-Methode hatte ich ihn auch nochmal abgefragt und je nach Übereinstimmung (0 oder 1) Integer.MAX_VALUE oder 0 zurückgegeben. Das alles hattest du in deinem Code ausgeklammert. Der Fehler war also nicht in der Farbdistanz zu suchen, sondern (anscheinend) nur im Alpha-Kanal.

Irgendwie war das Tab hier die ganze Zeit noch offen, weil ich es eigentlich interessant fand, aber (noch) nicht alles durchgelesen hatte… aber… das hier hat mich irgendwie daran erinnert: http://codegolf.stackexchange.com/questions/33172/american-gothic-in-the-palette-of-mona-lisa-rearrange-the-pixels . (Trotzdem mach’ ich das Tab jetzt erstmal zu…)