algorithm - Función para crear ruedas de color
language-agnostic colors (8)
¿No es también un factor en qué orden configuras los colores?
Al igual que si usa la idea 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"
¿Esto tiene sentido?
Esto es algo que pseudo-solucioné muchas veces y nunca he encontrado una solución.
El problema es encontrar una forma de generar
N
colores, que sean lo más distinguibles posible donde
N
es un parámetro.
Algunos recursos relacionados:
ColorBrewer : conjuntos de colores diseñados para distinguirse al máximo para su uso en mapas.
Escapar de RGBland: selección de colores para gráficos estadísticos : un 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.
Aquí hay un código para asignar colores RGB uniformemente alrededor de una rueda de colores 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;
}
He leído en alguna parte que el ojo humano no puede distinguir entre menos de 4 valores separados. Esto es algo a tener en cuenta. El siguiente algoritmo no compensa esto.
No estoy seguro de que esto sea exactamente lo que desea, pero esta es una forma de generar aleatoriamente valores de color no repetidos:
(cuidado, pseudocódigo inconsistente 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 optimizar esto 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 ralentizaría significativamente su algoritmo.
Otra forma sería desechar la aleatoriedad y revisar sistemáticamente cada 4 valores y agregar un color a una matriz en el ejemplo anterior.
Mi primer pensamiento sobre esto es "cómo generar N vectores en un espacio que maximice la distancia entre ellos".
Puede ver que el RGB (o cualquier otra escala que use que forme una base en el espacio de color) son solo vectores. Echa un vistazo a la selección aleatoria de puntos . Una vez que tenga un conjunto de vectores maximizados, puede guardarlos en una tabla hash o algo para más tarde, ¡y simplemente realizar rotaciones aleatorias en ellos para obtener todos los colores que desee que estén separados 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 de manera uniforme.
Realmente no sé qué tan bien funcionará, pero debería hacerlo desde entonces, digamos:
n = 10
Sabemos que tenemos 16777216 colores (256 ^ 3).
Podemos usar el algoritmo de hebillas 515 para encontrar el color indexado lexicográficamente. . Probablemente tendrá que editar el algoritmo para evitar el desbordamiento y probablemente agregar algunas mejoras menores de velocidad.
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 deben ser aproximadamente los más distinguibles entre sí.
Sé que es una publicación antigua, pero la encontré mientras buscaba una solución PHP para el tema y finalmente llegué con 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.
Sería mejor encontrar colores máximamente distantes en un espacio de color "perceptivamente uniforme", por ejemplo, CIELAB (usando la distancia euclidiana entre las coordenadas L *, a *, b * 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.