Alexander Brock: Algorithmus für n unterschiedliche Farben

Hallo Freunde des gehobenen Forumsgenusses,

Ich suche einen Algorithmus, der mir n unterschiedliche Farben aus dem RGB-Farbraum liefert, die sich möglichst gut voneinander abheben (und nicht weiß sind, weiß ist schon der Hintergrund).

Nachdem ich auf "Neue Nachricht" geklickt hatte und einen Text schreiben wollte habe ich noch einmal ein bisschen überlegt und experimentiert und folgende Lösung gefunden (PHP):

  
function get_colors($number) {  
 $root = pow($number, 1/3);  
 #echo '<br />'.$root;  
 if (ceil($root)*pow(floor($root), 2) >= $number) {  
  $n1 = ceil($root);  
  $n2 = $n3 = floor($root);  
 }  
 elseif (pow(ceil($root), 2)*floor($root) >= $number) {  
  $n1 = $n2 = ceil($root);  
  $n3 = floor($root);  
 }  
 else  
  $n1 = $n2 = $n3 = ceil($root);  
  
 $counter = 0;  
 $result = array();  
 for ($i1 = 0; $i1 <= $n1; $i1++) {  
  for ($i2 = 0; $i2 <= $n2; $i2++) {  
   for ($i3 = 0; $i3 <= $n3; $i3++) {  
    if ($counter >= $number)  
  return $res;  
 $res[] = array('r' => $i1*floor(255/$n1), 'g' => $i2*floor(255/$n2), 'b' => $i3*floor(255/$n3));  
 $counter++;  
   }  
  }  
 }  
 #echo '<br />'.$n1.'<br />'.$n2.'<br />'.$n3;  
}  

Ausprobieren kann man das hier: http://alexanderbrock.de/temp/get_colors.php

Ich bin mit dieser Lösung allerdings nicht sonderlich glücklich,
kennt vielleicht jemand eine bessere oder sieht an meinem Code einen Denkfehler?

Gruß
Alexander Brock

  1. Hello out there!

    Ich suche einen Algorithmus, der mir n unterschiedliche Farben aus dem RGB-Farbraum liefert, die sich möglichst gut voneinander abheben (und nicht weiß sind, weiß ist schon der Hintergrund).

    Was heißt „sich möglichst gut voneinander abheben“? In ihrem Farbton? In ihrer Helligkeit? (Und es gibt Menschen, die Farbtöne nicht unterscheiden können, welche sich für andere gut voneinander abheben.)

    Zum Verstehen eines Algorithmus sind sprechende Variablenbezeichnungen sehr hilfreich. Was zum Geier ist $n1, $n2, $n3? Warum heißen die nicht $r, $g, $b (gern auch $rot, $gruen, $blau)?

    See ya up the road,
    Gunnar

    --
    “Remember, in the end, nobody wins unless everybody wins.” (Bruce Springsteen)
  2. echo $begrüßung;

    Ich bin mit dieser Lösung allerdings nicht sonderlich glücklich,

    Da wird es noch mehr Leute geben. Neben Gunnars angesprochene Variablennamen wäre eine Kommentierung des Codes für Außenstehende und auch für dich selbst (haben wir nicht alle ein bisschen Alzheimer?) wichtig. Dabei sollte nicht kommentiert werden, was der Code macht ("Hier wird 1 und 1 zusammengezählt") sondern warum er das macht, bzw. das was er machen soll, damit man dann eventuelle Widersprüche in der Ausführung erkennen kann.

    echo "$verabschiedung $name";

  3. Hi,

    Ausprobieren kann man das hier: http://alexanderbrock.de/temp/get_colors.php

    Bei 10 verschiedenen Farben liegen einige aber recht eng zusammen...

    Ich bin mit dieser Lösung allerdings nicht sonderlich glücklich,
    kennt vielleicht jemand eine bessere

    ja. Nutze den HSV-Farbraum. Das heißt: Du hast einen Farbraum, der beispielsweise den Farbton beinhaltet - von 0 bis 255. Den kannst du dann einfach durch die Anzahl teilen... Helligkeit auf 127 oder 255, Sättigung auf 127 oder 255 (ausprobieren), und umwandeln... Dazu findet sich sicherlich irgendwo ein Script im Internet.

    E7

  4. Hallo Freunde des gehobenen Forumsgenusses,

    Ich habe jetzt den Code kommentiert und den Variablen aussagekräftigere Namen gegeben.

    „sich möglichst gut voneinander abheben“ heißt, dass ich Punkte auf eine weise Fläche malen möchte und Normalsterbliche diese Punkte einigermaßen auseinander halten bzw. einer Liste mit Farben zuordnen können sollen.

      
    function get_colors($number) {  
     $root = pow($number, 1/3);  
     if (ceil($root)*pow(floor($root), 2) >= $number) {  
      $red_number = ceil($root);  
      $green_number = $blue_number = floor($root);  
     }  
     elseif (pow(ceil($root), 2)*floor($root) >= $number) {  
      $red_number = $green_number = ceil($root);  
      $blue_number = floor($root);  
     }  
     else  
      $red_number = $green_number = $blue_number = ceil($root);  
    /*  
    Dieser Teil der Funktion ermittelt für jeden Farbkanal einzeln, in wie viele Teile die 255 Werte  
    des jeweiligen Farbkanals mindestens zerlegt werden müssen, damit die Funktion die geforderte Anzahl  
    Farben durch Permutationen erzeugen kann.  
     */  
      
     $counter = 0;  
     $result = array();  
     for ($red_counter = 0; $red_counter <= $red_number; $red_counter++) {  
      for ($green_counter = 0; $green_counter <= $green_number; $green_counter++) {  
       for ($blue_counter = 0; $blue_counter <= $blue_number; $blue_counter++) {  
        if ($counter >= $number)  
      return $res;  
     $res[] = array('r' => $red_counter*floor(255/$red_number), 'g' => $green_counter*floor(255/$green_number), 'b' => $blue_counter*floor(255/$blue_number));  
     $counter++;  
    /*  
    Dieser Teil berechnet die Permutationen und bricht ab, wenn genügend erzeugt wurden.  
    */  
       }  
      }  
     }  
    }  
      
    
    

    Gruß
    Alexander Brock

    1. Hallo Alexander,

      ich möchte vorausschicken, dass ich mich noch nie richtig mit Farblehre auseinandergesetzt habe, Deinen Ansatz als einen "logischen" Ansatz ansehe, dessen Praxistauglichkeit man zuerst ermitteln muss. Ich fürchte, der Ansatz ist zu einfach.

      Ich habe jetzt den Code kommentiert

      Dazu an geeigneter Stelle mehr.

      und den Variablen aussagekräftigere Namen gegeben.

      Ich fände 'value' besser als 'number', noch besser gar kein Postfix.

      Es folgt die Aufgabe der Funktion, diese schreibt man gern in einen Kommentar.

      „sich möglichst gut voneinander abheben“ heißt, dass ich Punkte auf eine weise Fläche malen möchte und Normalsterbliche diese Punkte einigermaßen auseinander halten bzw. einer Liste mit Farben zuordnen können sollen.

      function get_colors($number) {

      Kommentare zu Abschnitten einer Funktion schreibt man üblicherweise über den Abschnitt, nicht darunter.

      /*
      Dieser Teil der Funktion ermittelt für jeden Farbkanal einzeln, in wie viele Teile die 255 Werte
      des jeweiligen Farbkanals mindestens zerlegt werden müssen, damit die Funktion die geforderte Anzahl
      Farben durch Permutationen erzeugen kann.
      */

      Hier machst Du etwas falsch, Deine Werte fallen zu groß aus. Wenn Du Dir die von Dir erzeugten Farben ansiehst, so muss Dir doch auffallen, dass die Farben vorwiegend im blaugrünen Bereich angesiedelt sind, dass Rot unterrepäsentiert ist. Schaust Du Dir die Werte an, so muss Dir auffallen, dass

      a) Ein "\n" hinter jedem DIV-Element angebracht wäre :-)
      b) der Rot-Anteil nicht ausgeschöpft wird.

      Nun zu Deinem Fehler. Du zerlegst jeden Farbteil in n(farbe) Intervalle, darauf ist Dein n ausgelegt. Tatsächlich durchläufst Du nicht Intervalle, sondern Intervallgrenzen - und dies sind eben n(farbe)+1.

      $root = pow($number, 1/3);

      Diese Fallunterscheidung wäre meiner Meinung nach kommentierungswürdig.
      Warum machst Du das? Das ist nicht auf Anhieb ersichtlich. D.h. erläutere Deine Berechnungsgrundlage.

      if (ceil($root)*pow(floor($root), 2) >= $number) {
        $red_number = ceil($root);
        $green_number = $blue_number = floor($root);
      }
      elseif (pow(ceil($root), 2)*floor($root) >= $number) {
        $red_number = $green_number = ceil($root);
        $blue_number = floor($root);
      }
      else
        $red_number = $green_number = $blue_number = ceil($root);

      Nächster Abschnitt: Ort des Kommentars, siehe oben.

      /*
      Dieser Teil berechnet die Permutationen und bricht ab, wenn genügend erzeugt wurden.
      */
      $counter = 0;
      $result = array();
      for ($red_counter = 0; $red_counter <= $red_number; $red_counter++) {

      Hier kannst Du es sehen. Du lässt die Variable von 0 bis zum ermittelten Wert laufen (inklusive). Bei den folgenden Schleifen natürlich auch.

      for ($green_counter = 0; $green_counter <= $green_number; $green_counter++) {
         for ($blue_counter = 0; $blue_counter <= $blue_number; $blue_counter++) {
          if ($counter >= $number)
        return $res;
      $res[] = array('r' => $red_counterfloor(255/$red_number), 'g' => $green_counterfloor(255/$green_number), 'b' => $blue_counter*floor(255/$blue_number));
      $counter++;
         }
        }
      }
      }

        
      Als weitere Idee, damit Du von den dunkellastigen Farben wegkommst: Fange bei 255 als Farbwert je Kanal an und ziehe ab, statt bei 0 anzufangen und zu addieren. Dass Du den ersten Wert 255, 255, 255 weglassen musst, sehe ich als selbstverständlich an.  
        
      Danach (bereits mit meinem ersten Vorschlag) solltest Du die gleichmäßige Verteilung der Farbkanalwerte erreichen. Ob dies in der Praxis gute Farben ergibt, wirst Du dann sehen. Verbreitete Programme zeigen, dass dies offensichtlich nicht trivial ist.  
      Zur Farblehre könntest Du Dich an Andreas Lindig wenden, für seinen Farbfinder hat er nach meinem Wissen intensiv recherchiert.  
        
        
      Freundliche Grüße  
        
      Vinzenz
      
  5. Hallo,

    Ausprobieren kann man das hier: http://alexanderbrock.de/temp/get_colors.php

    ich hab jetzt noch nicht verstanden, was man da testen kann. Bitte Beschriftung. Wenn ich z.B. "ff0000" oder "#ff0000" eingebe, bekomme ich einen schwarzen Balken - ja und?

    Gruß, Andreas

    --
    kennst Du schon die Zitatesammlung?
  6. Ich suche einen Algorithmus, der mir n unterschiedliche Farben aus dem RGB-Farbraum liefert, die sich möglichst gut voneinander abheben (und nicht weiß sind, weiß ist schon der Hintergrund)

    dabei mußt Du auch bedenken, daß sich alle Farben auch gut vom Hintergrund abheben müssen - zusätzlich. Das ist eine klassische Doppelbelastung, wie sie sonst nur vollzeitarbeitende, alleinerziehende Mütter kennen ,-)

    zum Thema "n" ist noch anzumerken, daß mit steigender Zahl die Kontraste zwangsläufig sinken. Ich habe für eine Schautafel mal eine Reihe von Icons gebraucht und bin mit reiner Farbunterscheidung schnell an Grenzen gestoßen (6 bis max. 10). Ich habe dann 6 Farben und drei verschiedene Formen kombiniert, um auf 18 eindeutige Icons zu kommen, die man auch über eine weitere Entfernung noch zuordnen kann. Ich weiß ja nicht, ob diese Zuordnung bei Dir eine Rolle spielt.

    Mach Dir eine Seite mit n Kästchen (Divs o.ä.) und teste erstmal mit dem Farbfinder. Dann können wir über die Ergebnisse mal weiterdiskutieren.

    Gruß, Andreas

    --
    kennst Du schon die Zitatesammlung?
    1. Hallo Freunde des gehobenen Forumsgenusses,

      dabei mußt Du auch bedenken, daß sich alle Farben auch gut vom Hintergrund abheben müssen - zusätzlich.

      Is klar, war bisher aber kein Problem.
      Ich habe jetzt eine automatische Kontrastpreizung eingebaut, das hat dann teilweise zu weißen Farben geführt, also habe ich eine Methode geschrieben, die diese weißen Farben wieder aussortiert.

      zum Thema "n" ist noch anzumerken, daß mit steigender Zahl die Kontraste zwangsläufig sinken. Ich habe für eine Schautafel mal eine Reihe von Icons gebraucht und bin mit reiner Farbunterscheidung schnell an Grenzen gestoßen (6 bis max. 10). Ich habe dann 6 Farben und drei verschiedene Formen kombiniert, um auf 18 eindeutige Icons zu kommen, die man auch über eine weitere Entfernung noch zuordnen kann. Ich weiß ja nicht, ob diese Zuordnung bei Dir eine Rolle spielt.

      Ja, es geht darum Punkte (Menschen) auf eine Landkarte zu malen und die verschiedenfarbigen Punkte den Namen zuzuordnen. Der Tip mit den unterschiedlichen Formen ist sehr gut, das werde ich gleich mal ausprobieren. Was für Formen hast du verwendet? ich probier mal mit Kreis, Dreieck und Quadrat herum, wenn man dann noch zwischen gefüllten und nicht gefüllten Formen unterscheidet hat man 6 Formen.

      ich hab jetzt noch nicht verstanden, was man da testen kann. Bitte Beschriftung.

      Man kann eingeben, wie viele Farben man haben möchte.

      Wenn ich z.B. "ff0000" oder "#ff0000" eingebe, bekomme ich einen schwarzen Balken - ja und?

      Das liegt daran, dass (int)"ff0000" 0 ist ;-)

      Hier ist die neue Version:
      http://alexanderbrock.de/temp/get_colors2.php

      Quellcode:
      http://alexanderbrock.de/temp/paintings.class.php

      Gruß
      Alexander Brock

      1. Hier ist die neue Version:
        http://alexanderbrock.de/temp/get_colors2.php

        hmm..., sieht übel aus. Ich bekomme bei 10 Farben zwei fast gleiche Grüns, zwei fast gleiche Cyans, ein Blau und ein Violett, die nicht weit entfernt sind usw. - also keine gute Verteilung.

        Angenommen, wir lassen die gebrochenen Farben mal beiseite, dann brauchst Du immer zwei Kanäle zum Mischen und der dritte bleibt bei 0 - es sei denn, die anderen sind _beide_ schon  bei 255, dann kannst Du mit dem dritten zu Weiß hin aufhellen. Dann mußt Du Deine Farbanzahl erstmal aufteilen: z.B. bei 9 Farben bekommt der Bereich RG(0) 3 Farben, der Bereich R(0)B 3 und der Bereich (0)GB 3. Dann kannst Du Schleifen laufen lassen - naja, da gäbe es jetzt -zig Ansätze. Auf jeden Fall mußt Du bedenken, daß gleichmäßige Farbschritte nicht entstehen, wenn die Farbzahlen gleichmäßig voranschreiten, sondern im niedrigen Bereich nahe 0 brauchst Du große Schritte und weiter oben nahe 255 kleine.

        Wenn Du Dich nicht in die Farbmaterie einarbeiten willst, empfehle ich den weltmeistgenutzten Farbfinder ;-) und damit eine Tabelle zu erstellen. Sie könnte die maximal benötigte Anzahl Farben in schöner Abstufung enthalten und dann als Matrix Verwendung finden:

        +---+---+---+
        | 1 | 2 | 3 |
        +---+---+---+
        | 4 | 5 | 6 |
        +---+---+---+
        | 7 | 8 | 9 |
        +---+---+---+

        diese Matrix enhält 9 Farben. Brauchst Du z.B. 3 Farben, nimmst Du Nr. 4, 5, 6. Brauchst Du 5, nimmst Du 1, 3, 5, 7, 9 usw. Sodaß Du immer die Verteilung in der Tabelle gleichmäßig hälst. Das ist mit Sicherheit leichter zu berechnen.

        Quellcode:...

        kein Bock zum Lesen ;-)

        Gruß, Andreas

        --
        kennst Du schon die Zitatesammlung?
        1. Hallo Freunde des gehobenen Forumsgenusses,

          Hier ist die neue Version:
          http://alexanderbrock.de/temp/get_colors2.php

          hmm..., sieht übel aus. Ich bekomme bei 10 Farben zwei fast gleiche Grüns, zwei fast gleiche Cyans, ein Blau und ein Violett, die nicht weit entfernt sind usw. - also keine gute Verteilung.

          Die beiden fast gleichen Cyans sind 0,253,253 und 126,253,253; seltsam dass die so nahe zusammen liegen.

          Angenommen, wir lassen die gebrochenen Farben mal beiseite, dann brauchst Du immer zwei Kanäle zum Mischen und der dritte bleibt bei 0 - es sei denn, die anderen sind _beide_ schon  bei 255, dann kannst Du mit dem dritten zu Weiß hin aufhellen. Dann mußt Du Deine Farbanzahl erstmal aufteilen: z.B. bei 9 Farben bekommt der Bereich RG(0) 3 Farben, der Bereich R(0)B 3 und der Bereich (0)GB 3. Dann kannst Du Schleifen laufen lassen

          Genau das mache ich.

          Auf jeden Fall mußt Du bedenken, daß gleichmäßige Farbschritte nicht entstehen, wenn die Farbzahlen gleichmäßig voranschreiten, sondern im niedrigen Bereich nahe 0 brauchst Du große Schritte und weiter oben nahe 255 kleine.

          Wie soll ich das denn skalieren (logarithmisch, quadratisch, ...)?

          Im Moment sind auf meiner Karte aber nur 8 Leute und ich habe jetzt 6 Formen, bin also hinreichend glücklich mit meiner Lösung :-)

          Gruß
          Alexander Brock

          1. Die beiden fast gleichen Cyans sind 0,253,253 und 126,253,253; seltsam dass die so nahe zusammen liegen.

            gar nicht so seltsam: Cyan ist eine sehr helle Farbe. Daher ist der Weg bis zum Weiß nicht mehr sehr lang (ähnlich wie bei Gelb). Diesen kurzen Weg bei knapp einem Drittel zu teilen (139 wäre ein Drittel) macht keinen großen Schritt aus.

            In den meisten Farbtabellen für Webanwendungen findet man diese Fehler, daß einige Farben viel zu dicht liegen und andere zu weit auseinander. Im Selfhtml-Helferlein ist das auch so. Das basiert immmer auf dem Denken, daß man einfach nur die Zahlreihen gleichmäßig teilen müsste.

            Wie soll ich das denn skalieren (logarithmisch, quadratisch, ...)?

            ähh..., alles schon wieder vergessen ;-) Ich glaube, es heißt logarithmisch.

            Im Moment sind auf meiner Karte aber nur 8 Leute

            und dafür machst Du so'n Aufwand mit Farbprogramm und haste nicht gesehn? ;-)

            Gruß, Andreas

            --
            kennst Du schon die Zitatesammlung?
            1. Hallo Freunde des gehobenen Forumsgenusses,

              Im Moment sind auf meiner Karte aber nur 8 Leute

              und dafür machst Du so'n Aufwand mit Farbprogramm und haste nicht gesehn? ;-)

              Ich berechne außerdem den Punkt, zu dem die Leute durchschnittlich am wenigsten weit weg wohnen, außerdem wollte ich mal mit PHP malen :-)

              Gruß
              Alexander Brock

              1. außerdem wollte ich mal mit PHP malen :-)

                ah, ist aufwändig gell?

                Gruß, Andreas

                --
                kennst Du schon die Zitatesammlung?
                1. Hallo Freunde des gehobenen Forumsgenusses,

                  außerdem wollte ich mal mit PHP malen :-)

                  ah, ist aufwändig gell?

                  Findest du? Ich finde es sehr komfortabel (verglichen mit MS Paint ;-))

                  Das Lustige daran ist eigentlich die Automatisierung, ich erstelle da sehr viele unterschiedliche Bilder, die von Hand zu erstellen sehr viel aufwändiger wäre.

                  Gruß
                  Alexander Brock

      2. Was für Formen hast du verwendet?

        hehe, Pinguine :-)

        Gruß, Andreas

        --
        kennst Du schon die Zitatesammlung?
  7. Hallo Alexander

    Ich bin mit dieser Lösung allerdings nicht sonderlich glücklich,
    kennt vielleicht jemand eine bessere oder sieht an meinem Code einen Denkfehler?

    Den Code zerpflücke ich nicht, ist mir als PHP-Dau zu aufwendig. ;-)

    Meine Gedanken dazu sind Folgende:

    • 3 Farbkanäle ergeben einen dreidimensionalen Farbraum
        (R, G, B entspricht X, Y, Z mit jeweils 0 bis #ff)
    • der erste Punkt dieses Raumes weiß (#ffffff) ist gegeben
    • jede weitere Farbe soll sich möglichst gut von allen anderen abheben

    Also wie berechne ich im dreidimensionalen Raum (Würfel) jeweils die
    nächsten Punkte, die den größten Abstand zu allen bereits bestimmten haben?

    2. Punkt wäre gegenüberliegend also schwarz (#000000)
    Jetzt sind die beiden gegenüberliegenden Punkte (Ecken) gegeben

    Die nächsten Punkte wären dann jeweils die restlichen Ecken dieses Würfels.

    Auf Wiederlesen
    Detlef

    --
    - Wissen ist gut
    - Können ist besser
    - aber das Beste und Interessanteste ist der Weg dahin!
  8. Hallo,
    ich bin mir nicht sicher, ob meine Denkweise irgendwo einen knax hat, aber:
    Die Hex-Schreibweise der Zahlen (#FF24EE oä) kann man ganz simpel in das Dezimalsystem umwandeln. Dann erhalte ich für FFFFFF (höchster Wert) 16777215.
    Wenn du nun dieses 16777215 durch n-1 teils, hast du den abstand, der zwischen 2 Farben liegen muss. Das ganze noch in Hex verwandeln un gut is.
    Bsp:
    n = 10
    => 16777215 / 9 = 1864135
    => farbe 1 = dec 0, hex 0
    => farbe 2 = dec 1864135, hex 1C71C7
    => farbe 3 = dec 3728270, hex 38E38E
    => ....

    Die frage ist nun nur noch, ob sich die Farben, die in ihrer Dezimalschreibweise am meisten unterscheiden, sich auch optisch am meisten unterscheiden.

    werbeklaus