Pregunta

Esto es algo que he pseudorresuelto muchas veces y nunca he encontrado una solución.

El problema es encontrar una manera de generar N colores, que sean lo más distinguibles posible donde N es un parámetro.

¿Fue útil?

Solución

Mi primer pensamiento sobre esto es "cómo generar N vectores en un espacio que maximice la distancia entre sí".

Puedes ver que el RGB (o cualquier otra escala que uses y que forme una base en el espacio de color) son solo vectores.Echa un vistazo a Selección aleatoria de puntos.Una vez que tenga un conjunto de vectores maximizados, puede guardarlos en una tabla hash o algo así para más adelante, y simplemente realizar rotaciones aleatorias en ellos para obtener todos los colores que desee que estén separados al máximo entre sí.

Pensando más en este problema, sería mejor mapear los colores de manera lineal, posiblemente (0,0,0) → (255,255,255) lexicográficamente, y luego distribuirlos uniformemente.

Realmente no sé qué tan bien funcionará esto, pero debería funcionar, ya que digamos:

n = 10

sabemos que tenemos 16777216 colores (256^3).

Nosotros podemos usar Algoritmo de hebillas 515 para encontrar el color indexado lexicográficamente.\frac {\binom {256^3} {3}} {n} * i.Probablemente tendrás que editar el algoritmo para evitar el desbordamiento y probablemente agregar algunas mejoras menores en la velocidad.

Otros consejos

Lo mejor sería encontrar colores máximamente distantes en un espacio de color "perceptualmente uniforme", p.CIELAB (usando la distancia euclidiana entre las coordenadas L*, a*, b* como métrica de distancia) y luego convirtiéndola al espacio de color de su elección.La uniformidad perceptiva se logra ajustando el espacio de color para aproximarse a las no linealidades del sistema visual humano.

Algunos recursos relacionados:

colorcervecería - Conjuntos de colores diseñados para que se distingan al máximo para su uso en mapas.

Escapando de RGBlandia:Seleccionar colores para gráficos estadísticos - Un informe técnico que describe un conjunto de algoritmos para generar un bien (es decir,máximamente distinguibles) conjuntos de colores en el espacio de color hcl.

Aquí hay un código para asignar colores RGB de manera uniforme alrededor de una rueda de color HSL de luminosidad especificada.

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

¿No es también un factor el orden en que configuras los colores?

Por ejemplo, si usas la idea de Dillie-O, necesitas mezclar los colores tanto como sea posible.0 64 128 256 es de uno a otro.pero 0 256 64 128 en una rueda estaría más "separado"

¿Esto tiene sentido?

Leí en alguna parte que el ojo humano no puede distinguir entre menos de 4 valores de diferencia.entonces esto es algo a tener en cuenta.El siguiente algoritmo no compensa esto.

No estoy seguro de que esto sea exactamente lo que quieres, pero esta es una forma de generar aleatoriamente valores de color no repetidos:

(cuidado, pseudocódigo inconsistente más adelante)

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

Una forma de optimizar esto para una mejor visibilidad sería comparar la distancia entre cada color nuevo y todos los colores de la matriz:

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

Pero este enfoque ralentizaría significativamente su algoritmo.

Otra forma sería eliminar la aleatoriedad y revisar sistemáticamente cada 4 valores y agregar un color a una matriz en el ejemplo anterior.

Sé que esta es una publicación antigua, pero la encontré mientras buscaba una solución PHP para el tema y finalmente encontré una solución simple:

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

Entonces simplemente llame a la función random_color() donde $i identifica el color, $n el número de colores posibles, $sat la saturación y $br el brillo.

Para lograr "lo más distinguible" necesitamos usar un espacio de color perceptual como Lab (o cualquier otro espacio de color perceptualmente lineal) y no RGB.Además, podemos cuantificar este espacio para reducir el tamaño del espacio.

Genere el espacio 3D completo con todas las entradas cuantificadas posibles y ejecute el algoritmo K-means con k=N.Los centros/"medios" resultantes deberían ser aproximadamente lo más distinguibles entre sí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top