2008-08-01 9 views
61

Das ist etwas, das ich viele Male pseudo gelöst habe und nie eine Lösung gefunden habe. Das ist bei mir geblieben. Das Problem besteht darin, einen Weg zu finden, N Farben zu generieren, die so unterscheidbar wie möglich sind, wobei N ein Parameter ist.Funktion zum Erstellen von Farbrädern

+0

Zuletzt habe ich überprüft [JFreeChart] (http://www.jfree.org/jfreechart/) hat diesen genauen Algorithmus und wie es Open Source ist, können Sie überprüfen, was es tut. Ich weiß, dass die Farben, die ich bekomme, nicht zufällig entlang eines Kreises oder einer Kugel angeordnet sind, sondern eher spezifisch gewählt sind. –

Antwort

22

Mein erster Gedanke zu diesem Thema ist, "wie generieren N Vektoren in einem Raum, der Abstand voneinander maximieren." Sie können sehen, dass das RGB (oder eine andere Skala, die Sie verwenden, die eine Basis im Farbraum bildet) nur Vektoren sind. Schauen Sie sich Random Point Picking10 an. Hoffe, das ist ein guter Anfang für dich! Sobald Sie eine Reihe von Vektoren haben, die einen Teil maximiert haben, können Sie sie in einer Hash-Tabelle oder etwas für später speichern, und nur zufällige Drehungen auf ihnen durchführen, um alle Farben zu erhalten, die Sie wünschen, die maximal voneinander getrennt sind!

Bearbeiten: Denken Sie über dieses Problem mehr, wäre es besser, die Farben in einem linearen Herrenhaus, möglicherweise (0,0,0) -> (255,255,255) lexikographisch abzubilden, und dann verteilen sie gleichmäßig. Ich weiß wirklich nicht, wie gut das funktioniert, aber es sollte, sagen wir einmal:

n = 10 wir wissen, wir haben 16777216 Farben (256^3). Wir können buckles algorithm 515 verwenden, um die lexikografisch indizierte Farbe zu finden. \frac {\binom {256^3} {3}} {n} * i. Wahrscheinlich müssen Sie den Algorithmus bearbeiten, um einen Überlauf zu vermeiden und wahrscheinlich kleinere Geschwindigkeitsverbesserungen hinzuzufügen.

+1

Das ist falsch, weil der RGB-Farbraum nicht wahrnehmbar einheitlich ist –

+0

Ich stimme zu, dass klingt logisch. RGB macht meist lila und orange Hybriden und relativ selten blaugrüne Hybriden ... die Farbskala ist einheitlich von Infrarot bis tiefblau, also müssen Sie gleichmäßig beabstandete Punkte wählen. brauche einen Regenbogen-basierten Algo. –

+0

Bitte beachten Sie das Uplooten/Befolgen der StackExchange Color Theory Website: https://area51.stackexchange.com/proposals/110687/color-theory –

1

Ich habe irgendwo gelesen das menschliche Auge kann nicht zwischen weniger als 4 Werte unterscheiden. Das ist etwas, das man im Auge behalten sollte. Der folgende Algorithmus kompensiert dies nicht.

Ich bin mir nicht sicher, dass dies genau das ist, was Sie wollen, aber das ist eine Möglichkeit, sich nicht wiederholende Farbwerte nach dem Zufallsprinzip zu generieren:

(Vorsicht, inkonsistente Pseudo-Code voraus)

//colors entered as 0-255 [R, G, B] 
colors = []; //holds final colors to be used 
rand = new Random(); 

//assumes n is less than 16,777,216 
randomGen(int n){ 
    while (len(colors) < n){ 
     //generate a random number between 0,255 for each color 
     newRed = rand.next(256); 
     newGreen = rand.next(256); 
     newBlue = rand.next(256); 
     temp = [newRed, newGreen, newBlue]; 
     //only adds new colors to the array 
     if temp not in colors { 
     colors.append(temp); 
     } 
    } 
} 

Eine Möglichkeit, dies für eine bessere Sichtbarkeit optimieren könnte, wäre der Abstand zwischen jeder neuen Farbe zu vergleichen und alle Farben im Array:

for item in color{ 
    itemSq = (item[0]^2 + item[1]^2 + item[2]^2])^(.5); 
    tempSq = (temp[0]^2 + temp[1]^2 + temp[2]^2])^(.5); 
    dist = itemSq - tempSq; 
    dist = abs(dist); 
} 
//NUMBER can be your chosen distance apart. 
if dist < NUMBER and temp not in colors { 
    colors.append(temp); 
} 

Aber dieser Ansatz würde significa Verlangsamen Sie langsam Ihren Algorithmus.

Ein anderer Weg wäre, die Zufälligkeit zu verwerfen und systematisch alle 4 Werte zu durchlaufen und einem Array im obigen Beispiel eine Farbe hinzuzufügen.

3

Ist es nicht auch ein Faktor, der die Farben anordnet?

Wenn Sie die Idee von Dillie-Os verwenden, müssen Sie die Farben so gut wie möglich mischen. 0 64 128 256 ist von einem zum nächsten. aber 0 256 64 128 in einem Rad wäre mehr "auseinander"

Macht das Sinn?

17

Es wäre am besten, Farben zu finden, die in einem "wahrnehmungsmäßig gleichförmigen" Farbraum, z. CIELAB (euklidischer Abstand zwischen L *, a *, b * -Koordinaten als Entfernungsmaß) und dann Umwandlung in den Farbraum Ihrer Wahl. Perzeptuelle Einheitlichkeit wird erreicht, indem der Farbraum so eingestellt wird, dass er den Nichtlinearitäten im menschlichen visuellen System angenähert wird.

+0

Dies ist wahrscheinlich die beste Lösung, da sie recht einfach ist. Es gibt jedoch auch andere Farbunterschiedsformeln wie CIE2000 oder CIECAM. –

7

Einige verwandten Quellen an:

ColorBrewer - Sets von Farben auf Karten für die Verwendung so konzipiert, maximal unterscheidbar.

Escaping RGBland: Selecting Colors for Statistical Graphics - Ein technischer Bericht, der einen Satz von Algorithmen zum Erzeugen guter (d. H. Maximal unterscheidbarer) Farbsätze im hcl-Farbraum beschreibt.

+1

Escape RGBland ist ein Muss, um Referenzen für die Auswahl perzeptuell unterscheidbarer Farbpaletten zu lesen. – Drake

6

Hier ist ein Code, um RGB-Farben gleichmäßig um ein HSL-Farbrad mit bestimmter Leuchtkraft zuzuweisen.

class cColorPicker 
{ 
public: 
    void Pick(vector<DWORD>&v_picked_cols, int count, int bright = 50); 
private: 
    DWORD HSL2RGB(int h, int s, int v); 
    unsigned char ToRGB1(float rm1, float rm2, float rh); 
}; 
/** 

    Evenly allocate RGB colors around HSL color wheel 

    @param[out] v_picked_cols a vector of colors in RGB format 
    @param[in] count number of colors required 
    @param[in] bright 0 is all black, 100 is all white, defaults to 50 

    based on Fig 3 of http://epub.wu-wien.ac.at/dyn/virlib/wp/eng/mediate/epub-wu-01_c87.pdf?ID=epub-wu-01_c87 

*/ 

void cColorPicker::Pick(vector<DWORD>&v_picked_cols, int count, int bright) 
{ 
    v_picked_cols.clear(); 
    for(int k_hue = 0; k_hue < 360; k_hue += 360/count) 
     v_picked_cols.push_back(HSL2RGB(k_hue, 100, bright)); 
} 
/** 

    Convert HSL to RGB 

    based on http://www.codeguru.com/code/legacy/gdi/colorapp_src.zip 

*/ 

DWORD cColorPicker::HSL2RGB(int h, int s, int l) 
{ 
    DWORD ret = 0; 
    unsigned char r,g,b; 

    float saturation = s/100.0f; 
    float luminance = l/100.f; 
    float hue = (float)h; 

    if (saturation == 0.0) 
    { 
     r = g = b = unsigned char(luminance * 255.0); 
    } 
    else 
    { 
     float rm1, rm2; 

     if (luminance <= 0.5f) rm2 = luminance + luminance * saturation; 
     else      rm2 = luminance + saturation - luminance * saturation; 
     rm1 = 2.0f * luminance - rm2; 
     r = ToRGB1(rm1, rm2, hue + 120.0f); 
     g = ToRGB1(rm1, rm2, hue); 
     b = ToRGB1(rm1, rm2, hue - 120.0f); 
    } 

    ret = ((DWORD)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16))); 

    return ret; 
} 


unsigned char cColorPicker::ToRGB1(float rm1, float rm2, float rh) 
{ 
    if  (rh > 360.0f) rh -= 360.0f; 
    else if (rh < 0.0f) rh += 360.0f; 

    if  (rh < 60.0f) rm1 = rm1 + (rm2 - rm1) * rh/60.0f; 
    else if (rh < 180.0f) rm1 = rm2; 
    else if (rh < 240.0f) rm1 = rm1 + (rm2 - rm1) * (240.0f - rh)/60.0f;  

    return static_cast<unsigned char>(rm1 * 255); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    vector<DWORD> myCols; 
    cColorPicker colpick; 
    colpick.Pick(myCols, 20); 
    for(int k = 0; k < (int)myCols.size(); k++) 
     printf("%d: %d %d %d\n", k+1, 
     (myCols[k] & 0xFF0000) >>16, 
     (myCols[k] & 0xFF00) >>8, 
     (myCols[k] & 0xFF)); 

    return 0; 
} 
+2

AFAIK ist es einfach, Code von C++ zu Java – ravenspoint

+0

zu portieren, nicht wenn ich nicht alle bitverschiebenden Sachen verstehe, unter anderem:/ – CodeGuy

+0

Ich habe URLs zur Verfügung gestellt, die zu Erklärungen verlinken, was der Code tut. – ravenspoint

1

Ich weiß, dass dies eine alte Post, aber ich fand es während der Suche nach einer PHP-Lösung für das Thema und kam schließlich mit einer einfachen Lösung:

function random_color($i = null, $n = 10, $sat = .5, $br = .7) { 
    $i = is_null($i) ? mt_rand(0,$n) : $i; 
    $rgb = hsv2rgb(array($i*(360/$n), $sat, $br)); 
    for ($i=0 ; $i<=2 ; $i++) 
     $rgb[$i] = dechex(ceil($rgb[$i])); 
    return implode('', $rgb); 
} 

function hsv2rgb($c) { 
    list($h,$s,$v)=$c; 
    if ($s==0) 
     return array($v,$v,$v); 
    else { 
     $h=($h%=360)/60; 
     $i=floor($h); 
     $f=$h-$i; 
     $q[0]=$q[1]=$v*(1-$s); 
     $q[2]=$v*(1-$s*(1-$f)); 
     $q[3]=$q[4]=$v; 
     $q[5]=$v*(1-$s*$f); 
     return(array($q[($i+4)%6]*255,$q[($i+2)%6]*255,$q[$i%6]*255)); //[1] 
    } 
} 

So rufen Sie die random_color() Funktion in der $ i identifiziert die Farbe, $ n die Anzahl der möglichen Farben, $ sat die Sättigung und $ br die Helligkeit.

+0

Können Sie erklären, was "i" in diesem Fall ist? Die Frage wurde nach N-Nummern gestellt. Was ist der "Ich" -Parameter? – CodeGuy

+0

Auf 'random_color()' ist '$ i' der" Seed "zum Erzeugen des Farbtons, sollte eine Zahl von 0 bis' $ n' sein, wenn Sie keinen Seed (NULL) eingeben, wählt die Funktion einen zufälligen aus. "$ n" ist die Menge möglicher Farben für eine gegebene Sättigung und Helligkeit, d. h. die Anzahl von Farben in der Palette. Wir teilen im Grunde die 360-Grad-Farben in $ n und verwenden $ i als Multiplikator. Mit anderen Worten, ein höheres '$ n' wird Ihnen mehr Farben geben, ein niedrigeres' $ n' wird Ihnen weniger Farben geben, aber mehr voneinander. '$ i' identifiziert die Farbe und ist immer dieselbe, wenn Sie diese Funktion weiterhin verwenden. Ich hoffe das hilft. – Mauro

+0

Ich sehe! Danke für die Erklärung. Noch eine Sache ... irgendwelche Vorschläge, was zu tun ist, wenn ich eine Hintergrundfarbe habe und ich möchte für alle Farben so weit wie möglich davon entfernt sein? – CodeGuy

0

Um "höchst unterscheidbar" zu erreichen, müssen wir einen wahrnehmbaren Farbraum wie Lab (oder irgendeinen anderen wahrnehmbar linearen Farbraum) und nicht RGB verwenden. Außerdem können wir diesen Raum quantisieren, um die Größe des Raums zu reduzieren.

Generieren Sie den gesamten 3D-Raum mit allen möglichen quantisierten Einträgen und führen Sie den K-Means-Algorithmus mit k=N aus. Die resultierenden Zentren/"Mittelwerte" sollten ungefähr am meisten voneinander unterschieden werden.

Verwandte Themen