# Überprüfen ob zwei Farben ähnlich sind



## The_S (20. Feb 2008)

Hi,

kennt jemand nen guten Algorithmus/Ansatz, mit dem ich überprüfen kann, ob zwei RGB-Farben für das menschliche Auge fast identisch aussehen? Das Einzige, was mir jetzt einfallen würde, wäre die Differenz aller Kanäle zusammenzuzählen und dann überprüfen, ob sich der Wert über/unterhalb eines Richtwertes befindet. Halte ich jetzt aber für nicht so effektiv  .

Danke!

[edit] Hintergrund ist der, dass ich n Zufallsfarben erstellen möchte, die sich möglichst gut voneinander abheben.


----------



## Oni (20. Feb 2008)

wenn die möglichst unterschiedlich sein sollen, dann nimm doch die komplementärfarben.


Farbe + Komplementärfarbe = Weiss

Weiss - Farbe = Komplementärfarbe

gilt natürlich nur für den RGB Farbraum


----------



## The_S (20. Feb 2008)

Glaub du hast mein Problem n bisschen falsch interpretiert  .

Ich hab wie gesagt nicht nur 2 Farben, die nicht ähnlich sein dürfen, sondern N Farben, die alle zueinander nicht ähnlich sein dürfen. Und wenn ich feststellen kann, ob 2 Farben zueinander nicht ähnlich sind, kann ich das auch für alle feststellen.

Jetzt klarer?

Aber danke!


----------



## Der Müde Joe (20. Feb 2008)

fällt mir gerade keine Routine ein, aber etwas hintergrundwissen

EDIT: wegeditiert...

..der muss mal wieder wegeditiert werden...!


----------



## 0x7F800000 (20. Feb 2008)

RGB ist doch einfach isomorph zu einem R³-vektorraum, da kannst du "abstände" zwischen zwei Farben beispielsweise mit der gewöhnlichen euklidischen Norm berechnen: also einfach mit dem Satz des Pythagoras für den dreidimensionalen Fall... Nun hast du im R³ also so einen Quader [0,255]x[0,255]x[0,255], wo alle verfügbare Farben drin enthalten sind. Jetzt musst du in diesem Würfel N Punkte wählen, die voneinander einen möglischst großen Abstand haben   

```
//abstansfunktion irgendwie aus dem satz des pythagoras basteln...
d(c1,c2)=sqrt((c1.red-c2.red)²+(c1.grün-c2.grün)²+(c1.blau-c2.blau)²)
```
Wie man jetzt in so einem Würfel N Punkte so anordnet ist eine verdammt gute frage, ich bezweifle, dass es da eine allgemeine Lösung gibt...

Wenn du diese farben nur selten auswählen musst (zB. einmal beim Programmstart, also nicht 100 mal pro Sekunde), würde _ich_ persönlich wie folgt verfahren: Du könntest so eine Art Simulation starten: Punkte werden anfangs zufällig im Würfel Plaziert, und diese stoßen sich alle gegenseitig ab (zB mit einer "Kraft" die proportional zu 1/r² ist: da sparst du dir das wurzelziehen) nach ein paar simulationsschritten stellt sich eine gleichgewichtslage ein, bei der die Punkte im würfel ziemlich optimal verteilt sind. Dann einfach die positionen auslesen, runden, als Farben interpretieren.

Ich hab's zwar noch nie ausprobiert, aber der Ansatz ist imho recht naheliegend:
Würde man zB N "elektronen" in ein Gebiet reinschmeissen, und dort irgendwie für "reibung" sorgen, würden diese sich dort nach einer weile recht optimal verteilen... habs irgendwo gelesen... kP, könntest das vielleicht ausprobieren... vielleicht hilfts was  :lol:


edit: komplementärfarben würde ich nicht empfehlen, in der Nähe von Grau, also (127,127,127) bleibt das komplement ebenfalls einfach nur grau, da kannst du nichts mehr unterscheiden.... geht eh nur für 2 farben...


----------



## Marco13 (20. Feb 2008)

RGB ist dafür ziemlich ungeeignet: Es gibt dort kein gutes "Maß" für die Ähnlichkeit. Wenn man z.B. die Manhattan-Distanz der Farbwerte verwendet, oder einen anderen "Abstand der Punkte im Farbraum", dann kann es sein, dass man zwei Farben mit Abstand "0.232" noch als "fast gleich" ansieht, und zwei andere Farben mit Abstand "0.121" als kaum unterscheidbar. 

Viel näher an der menschlichen Wahrnehmung ist das HSB-Modell. 
http://de.wikipedia.org/wiki/HSB-Farbraum
Dort kann man z.B. Farbton-Unterschiede SEHR stark gewichten, Helligkeitsunterschiede mittel, und Sättigungsunterschiede etwas geringer. Jedenfalls dürfte man damit leichter so ein Ähnlichkeitsmaß hinbekommen.


----------



## Marco13 (20. Feb 2008)

So als Nachtrag: Um möglichst unterschiedliche Farben zu generieren, würde sich dann anbieten, für den Farbton (Hue) die Farben zu nehmen, die rauskommen, wenn man auf den Farbkreis ein ... naja, vielleicht ein 8-Eck legt. Dann kann man noch jeweils mit 1.0 und 0.5 Helligkeit (oder evtl. Sättigung) große Unterschiede erreichen.

Um zu EINER Farbe eine möglichst unterschiedliche zu finden, würde man für den Hue einfach den Wert nehmen, der auf dem Farbkreis "gegenüber" der ursprünglichen Farbe liegt. Ein bißchen aufpassen muss man aber immer: Das HSB-Modell hat unten eine Spitze, d.h. mit "Helligekeit 0" sehen alle Farben gleich aus :wink:


----------



## Der Müde Joe (20. Feb 2008)

naja..HSV ist ok..noch besser wären die vom CIELAB

http://de.wikipedia.org/wiki/Lab-Farbraum

WIKI dazu:

Die Koordinaten des L*a*b*-Farbortes orientieren sich an den physiologischen Eigenschaften der menschlichen Wahrnehmung (Farbwahrnehmung), sie basieren nur mittelbar auf physikalischen Farbvalenzen. (Farbmetrik)


----------



## 0x7F800000 (20. Feb 2008)

hm... ich finds ne recht interessante frage, wie man denn sowas nun anstellen soll...

habe hier folgendes zusammengebastelt (lässt sich direkt reinkopieren, kompillieren, ausführen, ist vollständig)


```
import java.awt.*;
import javax.swing.*;

public class ColorRandomizer {

	public static Color[] getRandomColors(int N){
		
		//proportionalitätskonstanten. C für abstoßung, R für reibung, dt= zeitsprünge...
		final double C=1,R=-0.1, dt=10;
		double interactionRadiusSq=2*Math.pow(3d/(4*Math.PI*N), 2d/3d)*65536;
		//N zufällige Punkte im Würfel [0,255]³ erzeugen
		Vector3D[] x=new Vector3D[N];
		Vector3D[] v=new Vector3D[N];
		Vector3D[] a=new Vector3D[N];
		
		//anfängliche positionen, geschwindigkeiten und beschleunigungen initialisieren
		for(int i=0; i<N; i++){
			x[i]=new Vector3D(Math.random()*255, Math.random()*255,Math.random()*255);
			v[i]=a[i]=Vector3D.NULL;
		}
		
		Vector3D connection; //verbindungsvektor zwischen zwei punkten
		double connectionLengthSq; //länge quadriert
		//simulation 
		for(double t=0; t<10000; t+=dt){
			//gesamtbeschleunigungen mit superpositionsprinzip ausrechnen
			for(int k=0; k<N; k++){
				a[k]=new Vector3D(0,0,0);
					for(int j=0; j<N; j++){
						if(k!=j){
							connection=x[k].sub(x[j]);
							connectionLengthSq=connection.getSqLength();
							if(connectionLengthSq<interactionRadiusSq){
								a[k]=a[k].add(connection.mul(C/connectionLengthSq));
							}
						}
					}
				//geschwindigkeiten aktualisieren (coulomb+reibungskräfte berücksichtigen)
				v[k]=v[k].add((a[k].add(v[k].mul(R))).mul(dt));
			}
			
			//einfach alle punkte um v*dt verschieben
			for(int k=0; k<N; k++){
				x[k]=x[k].add(v[k].mul(dt));
				//guggen, dass alles im würfel bleibt (bissl hässlich :-/ )
				if(x[k].x<0){ x[k].x=0; }else if(x[k].x>255){x[k].x=255;}
				if(x[k].y<0){ x[k].y=0; }else if(x[k].y>255){x[k].y=255;}
				if(x[k].z<0){ x[k].z=0; }else if(x[k].z>255){x[k].z=255;}
			}
			
		}
		
		//so, jetzt sollte sich da was brauchbares eingependelt haben
		Color[] result=new Color[N];
		for(int i=0; i<N; i++){
			result[i]=new Color((int)(x[i].x),(int)(x[i].y),(int)(x[i].z));
		}
		
		return result;
	}
	
	
	public static void main(String[] args){
		int N=30;
		
		Color[] colors=getRandomColors(N);
		
		JFrame f=new JFrame("zufallsfarben");
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setBounds(10,200,1000,70);
		f.setVisible(true);
		

		Graphics g=f.getGraphics();
		
		for(int warumSehIchNIX=0; warumSehIchNIX<10000; warumSehIchNIX++){
		for(int i=0; i<N; i++){
			g.setColor(colors[i]);
			g.fillRect((int)(1000*i/(float)N), 0, 1000/N, 100);
		}
		}
		
		for(int i=0; i<N; i++){
			System.out.println(colors[i]);
		}
	}
	
}
```

leider scheinen sich diese farbwerte immer auf dem rand des Würfels zu verteilen, das find ich jetzt ein wenig doof  :autsch: bei wenigen farben (bis 8) sucht das programm sich, wie erwartet, die "ecken" des würfels aus, also farben mit werten 0 oder 255... sonst funktioniert es nicht so toll wie erhofft... kA, was haltet ihr denn davon?  :bahnhof:

edit: eeeheheh, hab da grad noch einen guten einfall  :idea:  :bae: moment...

edit2: so, habs jetzt nachgebessert, jetzt wird auch das innere des verfügbaren farbwürfels gut ausgefüllt...

edit3: hoppla, hab ja ganz vergessen, dass Vector3D mein eigener mist ist, schuldigung^^   


```
//3D-Vector that represents a point in cartesian coords

public class Vector3D {
	
	//xyz
	double x,y,z;
	
	//constructors
	public Vector3D(){
		x=y=z=0.0;
	}
	
	public Vector3D(double _x,double _y,double _z){
		x=_x; y=_y; z=_z;
	}
	
	public Vector3D(Vector3D v){
		x=v.x; y=v.y; z=v.z;
	}
	
	//getLength, getSqrLength
	public double getLength(){
		return Math.sqrt(x*x+y*y+z*z);
	}
	
	public double getSqLength(){
		return x*x+y*y+z*z;
	}
	
	// v+v v-v 
	public Vector3D add(Vector3D v){
		return new Vector3D(x+v.x, y+v.y, z+v.z);
	}
	
	public Vector3D sub(Vector3D v){
		return new Vector3D(x-v.x, y-v.y, z-v.z);
	}
	
	//v*d v/d
	public Vector3D mul(double d){
		return new Vector3D(x*d, y*d, z*d);
	}
	
	public Vector3D div(double d){
		return new Vector3D(x/d, y/d, z/d);
	}
	
	//scalar and cross products
	public double dot(Vector3D v){
		return x*v.x+y*v.y+z*v.z;
	}
	
	public Vector3D cross(Vector3D v){
		return new Vector3D(y*v.z-z*v.y,
							z*v.x-x*v.z,
							x*v.y-y*v.x);
	}
	
	//getCos getSin getAngle
	public double getCos(Vector3D v){
		return this.dot(v)/Math.sqrt(this.getSqLength()*v.getSqLength());
	}
	
	public double getSin(Vector3D v){
		return Math.sqrt(this.cross(v).getSqLength()/(this.getSqLength()*v.getSqLength()));
	}
	
	public double getAngle(Vector3D v){
		return Math.acos(this.getCos(v));
	}
	
	//some useful static constants and functions
	public static final Vector3D NULL=new Vector3D(0,0,0);
	public static final Vector3D eX=new Vector3D(1,0,0);
	public static final Vector3D eY=new Vector3D(0,1,0);
	public static final Vector3D eZ=new Vector3D(0,0,1);
}
```
so, jetzt dürfte es laufen^^


----------



## 0x7F800000 (20. Feb 2008)

Wenn mir jetzt irgendwer erklären könnte, wie ich denn nun von RGB in dieses L*a*b umrechne, und vor allem: wie geht es umgekehrt...  :###  :autsch:


----------



## The_S (21. Feb 2008)

Uff ... da habt ihr ja ganz schön viel zusammen getragen :-D . Danke dafür, werd ich mir mal alles in Ruhe durchlesen.


----------



## tincup (21. Feb 2008)

Andrey hat gesagt.:
			
		

> Wenn mir jetzt irgendwer erklären könnte, wie ich denn nun von RGB in dieses L*a*b umrechne, und vor allem: wie geht es umgekehrt...  :###  :autsch:



Zumindest die Hinrichtung ist bei Wiki beschrieben:
http://de.wikipedia.org/wiki/Lab-Farbraum#Umrechnung_von_RGB_zu_Lab















Andersrum müsste jetzt mal einer seine algebraischen Fähigkeiten oder sein Computeralgebra-Programm benutzen.  :wink: (also entsprechend die Matrix invertieren)


----------



## Marco13 (21. Feb 2008)

Andrey hat gesagt.:
			
		

> ```
> Graphics g=f.getGraphics();
> 
> for(int warumSehIchNIX=0; warumSehIchNIX<10000; warumSehIchNIX++){
> ```


Ggf. wird die drei mal gestellte Frage durch die Zeile darüber schon beantwortet :wink:


----------



## 0x7F800000 (21. Feb 2008)

Marco13 hat gesagt.:
			
		

> Andrey hat gesagt.:
> 
> 
> 
> ...



jaja, schon klar, aber ich kenne bisher keine kürzere, hässlichere und dreckigere möglichkeit irgendetwas auf dem bildschirm darzustellen.  :autsch:  :bae:  Aber ich nehme mal schwer an dass der Hobbit_Im_Blutrausch weiss wie er alles sauber und doppelt gepuffert zeichnet, allgemeine fragen zum zeichnen stehen hier eh nicht zur debatte, es sollte halt irgendwie laufen, und möglichst wenig von der eigentlich interessanten funktion ablenken. Aber du hast natürlich recht Marco13, ich hätte irgendetwas wie 


> "Code nicht für Programmierer unter 18 geeignet"


 oder 


> "Dieser Stunt wurde vom speziell ausgebildeten Personal erstellt, versuchen Sie es nicht zuhause nachzumachen!"


dahinschreiben sollen  :lol:


----------



## The_S (22. Feb 2008)

Ich denk mal, ich habe jetzt genügend Ansätze um was ordentliches zu basteln. Danke an alle


----------

