2008-08-01 9 views
61

Esto es algo que he pseudo-resuelto muchas veces y nunca encontré una solución. Eso está atrapado en mí. El problema es encontrar una forma de generar N colores, que sean tan distinguibles como sea posible donde N es un parámetro.Función para crear ruedas de color

+0

La última vez que revisé [JFreeChart] (http://www.jfree.org/jfreechart/) tiene este algoritmo preciso y, como es de código abierto, puede ver lo que hace. Sé que los colores que obtengo no parecen estar espaciados aleatoriamente a lo largo de un círculo o esfera, sino más específicamente elegidos. –

Respuesta

22

Mi primer pensamiento sobre esto es "cómo generar N vectores en un espacio que maximiza la distancia el uno del otro". Puede ver que el RGB (o cualquier otra escala que use que forme una base en el espacio de color) son solo vectores. Eche un vistazo al Random Point Picking. Espero que este sea un buen comienzo para ti! Una vez que tienes un conjunto de vectores que están maximizados por una parte, puedes guardarlos en una tabla hash o algo para más adelante, y simplemente realizar rotaciones al azar en ellos para obtener todos los colores que deseas que estén separados el uno del otro.

Editar: Pensando en este problema más, que sería mejor para mapear los colores en una casa lineal, posiblemente, (0,0,0) -> (255,255,255) lexicográfico, y luego distribuirlos de manera uniforme. Realmente no sé qué tan bien funcionará esto, pero debería desde, digamos:

n = 10 sabemos que tenemos 16777216 colores (256^3). Podemos usar buckles algorithm 515 para encontrar el color lexicográficamente indexado. \frac {\binom {256^3} {3}} {n} * i. Probablemente tengas que editar el algoritmo para evitar el desbordamiento y probablemente agregar algunas mejoras menores de velocidad.

+1

Esto es incorrecto porque el espacio de color RGB no es perceptualmente uniforme –

+0

Estoy de acuerdo en que suena lógico. RGB produce principalmente híbridos morados y naranjas y relativamente rarey hace híbridos de color verde azulado ... la escala de colores es uniforme desde el infrarrojo hasta el azul intenso, por lo que debe elegir puntos equidistantes a lo largo del mismo. necesita un algo basado en el arcoíris. –

+0

Considere la posibilidad de votar/siguiendo el sitio StackExchange Color Theory: https://area51.stackexchange.com/proposals/110687/color-theory –

1

He leído en algún lado que el ojo humano no puede distinguir entre menos de 4 valores aparte. así que esto es algo a tener en cuenta. El siguiente algoritmo no compensa esto.

no estoy seguro de que esto es exactamente lo que quiere, pero esta es una manera de generar aleatoriamente los valores que no se repite de color:

(cuidado, inconsistentes pseudo-código) por delante

//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 poder optimizar este para una mejor visibilidad sería comparar la distancia entre cada nuevo color 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 SIGNIFICA nly desacelera tu algoritmo.

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

3

¿No es también un factor que ordenó configurar los colores?

Como si usa la idea de Dillie-Os, necesita mezclar los colores tanto como sea posible. 0 64 128 256 es de uno a otro. pero 0 256 64 128 en una rueda sería más "aparte"

¿Tiene esto sentido?

17

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

+0

Esta es probablemente la mejor solución, ya que es bastante sencilla. Sin embargo, hay otras fórmulas de diferencia de color a considerar, como CIE2000 o incluso CIECAM –

7

Algunos recursos relacionados:

ColorBrewer - Conjuntos de colores diseñados para ser máximamente distinguibles para su uso en los mapas.

Escaping RGBland: Selecting Colors for Statistical Graphics - Informe técnico que describe un conjunto de algoritmos para generar conjuntos de colores buenos (es decir, distinguibles al máximo) en el espacio de color hcl.

+1

Escapar RGBland es una referencia obligada para leer las referencias para elegir paletas de colores perceptualmente distinguibles. – Drake

6

Aquí hay un código para asignar colores RGB uniformemente 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; 
} 
+2

AFAIK es sencillo codificar el puerto de C++ a Java – ravenspoint

+0

no cuando no entiendo todos los cambios de bit, entre otras cosas:/ – CodeGuy

+0

He proporcionado URL que enlazan con explicaciones de lo que hace el código. – ravenspoint

1

Sé que esto una entrada antigua, pero lo encontré en la búsqueda de una solución PHP para el tema y finalmente vine con una solución sencilla:

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

Así que llamar 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.

+0

¿Puede explicar qué es "i" en este caso? La pregunta solicitó N números. ¿Cuál es el paramater "i"? – CodeGuy

+0

En 'random_color()', '$ i' es la" semilla "para generar el matiz, debe ser un número de 0 a' $ n', si no ingresa semilla (NULL), la función escoge una aleatoria. '$ n' es la cantidad de colores posibles para una saturación y brillo determinados, es decir, el número de colores en la paleta. Básicamente estamos dividiendo los 360 grados de matiz en '$ n' y usando' $ i' como multiplicador. En otras palabras, un mayor '$ n' le dará más colores, un' $ n' más bajo le dará menos colores pero más diferente el uno al otro. '$ i' identificará el color y siempre será el mismo si continúa usando esta función. Espero que eso ayude. – Mauro

+0

¡Ya veo! Gracias por la explicación. Una cosa más ... ¿alguna sugerencia de qué hacer si tengo un color de fondo y quiero estar lo más lejos posible de todos los colores? – CodeGuy

0

Para lograr "lo más distinguible", necesitamos utilizar 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 cuantizadas posibles y ejecute el algoritmo K-medias con k=N. Los centros/"medios" resultantes deberían ser aproximadamente más distinguibles entre sí.

Cuestiones relacionadas