Domanda

Questo è qualcosa che ho pseudo-risolto molte volte e per il quale non ho mai trovato una soluzione.

Il problema è trovare un modo per generare N colori, che siano quanto più distinguibili possibile dove N è un parametro.

È stato utile?

Soluzione

Il mio primo pensiero al riguardo è "come generare N vettori in uno spazio che massimizzi la distanza l'uno dall'altro".

Puoi vedere che l'RGB (o qualsiasi altra scala che usi che costituisce una base nello spazio colore) sono solo vettori.Dare un'occhiata a Scelta casuale dei punti.Una volta che hai una serie di vettori massimizzati, puoi salvarli in una tabella hash o qualcosa del genere per dopo, ed eseguire semplicemente rotazioni casuali su di essi per ottenere tutti i colori che desideri che siano massimamente separati l'uno dall'altro!

Riflettendo maggiormente su questo problema, sarebbe meglio mappare i colori in maniera lineare, possibilmente (0,0,0) → (255,255,255) lessicograficamente, e poi distribuirli uniformemente.

Non so davvero quanto bene funzionerà, ma dovrebbe farlo poiché, diciamo:

n = 10

sappiamo di avere 16777216 colori (256 ^ 3).

Possiamo usare Algoritmo delle fibbie 515 per trovare il colore indicizzato lessicograficamente.\frac {\binom {256^3} {3}} {n} * i.Probabilmente dovrai modificare l'algoritmo per evitare l'overflow e probabilmente aggiungere alcuni piccoli miglioramenti alla velocità.

Altri suggerimenti

Sarebbe meglio trovare i colori più distanti in uno spazio cromatico "percettivamente uniforme", ad es.CIELAB (utilizzando la distanza euclidea tra le coordinate L*, a*, b* come metrica della distanza) e quindi convertendola nello spazio colore di tua scelta.L'uniformità percettiva si ottiene modificando lo spazio colore per approssimare le non linearità del sistema visivo umano.

Alcune risorse correlate:

ColorBrewer - Set di colori progettati per essere il più distinguibili possibile per l'uso sulle mappe.

In fuga da RGBland:Selezione dei colori per la grafica statistica - Una relazione tecnica che descrive una serie di algoritmi per generare beni (ad es.massimamente distinguibili) set di colori nello spazio colore hcl.

Ecco del codice per allocare i colori RGB in modo uniforme attorno a una ruota dei colori HSL di luminosità specificata.

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;
}

Non è importante anche l'ordine in cui imposti i colori?

Ad esempio, se usi l'idea di Dillie-O devi mescolare i colori il più possibile.0 64 128 256 va dall'uno all'altro.ma 0 256 64 128 in una ruota sarebbe più "a parte"

Ha senso ciò?

Ho letto da qualche parte che l'occhio umano non riesce a distinguere tra meno di 4 valori di distanza.quindi questo è qualcosa da tenere a mente.Il seguente algoritmo non compensa questo.

Non sono sicuro che questo sia esattamente quello che vuoi, ma questo è un modo per generare casualmente valori di colore non ripetuti:

(attenzione, pseudo-codice incoerente in anticipo)

//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);
      }
   }
}

Un modo per ottimizzarlo per una migliore visibilità sarebbe confrontare la distanza tra ogni nuovo colore e tutti i colori nell'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);
}

Ma questo approccio rallenterebbe notevolmente il tuo algoritmo.

Un altro modo sarebbe eliminare la casualità ed esaminare sistematicamente ogni 4 valori e aggiungere un colore a un array nell'esempio sopra.

So che questo è un vecchio post ma l'ho trovato mentre cercavo una soluzione PHP all'argomento e alla fine ho trovato una soluzione semplice:

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] 
    } 
}

Quindi basta chiamare la funzione random_color() dove $i identifica il colore, $n il numero di colori possibili, $sat la saturazione e $br la luminosità.

Per ottenere il "più distinguibile" dobbiamo utilizzare uno spazio colore percettivo come Lab (o qualsiasi altro spazio colore percettivamente lineare) e non RGB.Inoltre, possiamo quantizzare questo spazio per ridurre la dimensione dello spazio.

Genera lo spazio 3D completo con tutte le possibili voci quantizzate ed esegui l'algoritmo K-medie k=N.I centri/"mezzi" risultanti dovrebbero essere approssimativamente più distinguibili l'uno dall'altro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top