Pergunta

Isso é algo que já pseudo-resolvi muitas vezes e para o qual nunca encontrei uma solução.

O problema é encontrar uma maneira de gerar N cores, que sejam tão distinguíveis quanto possível onde N é um parâmetro.

Foi útil?

Solução

Meu primeiro pensamento sobre isso é "como gerar N vetores em um espaço que maximize a distância um do outro".

Você pode ver que o RGB (ou qualquer outra escala usada que forma a base do espaço de cores) são apenas vetores.Dê uma olhada Escolha aleatória de pontos.Depois de ter um conjunto de vetores maximizados, você pode salvá-los em uma tabela hash ou algo assim para mais tarde e apenas realizar rotações aleatórias neles para obter todas as cores desejadas e que estejam ao máximo separadas umas das outras!

Pensando mais neste problema, seria melhor mapear as cores de forma linear, possivelmente (0,0,0) → (255.255.255) lexicograficamente, e depois distribuí-las uniformemente.

Eu realmente não sei até que ponto isso vai funcionar, mas deveria, desde então, digamos:

n = 10

sabemos que temos 16777216 cores (256 ^ 3).

Podemos usar Algoritmo de Fivelas 515 para encontrar a cor indexada lexicograficamente.\frac {\binom {256^3} {3}} {n} * i.Você provavelmente terá que editar o algoritmo para evitar estouro e provavelmente adicionar algumas pequenas melhorias de velocidade.

Outras dicas

Seria melhor encontrar cores distantes ao máximo em um espaço de cores "perceptualmente uniforme", por ex.CIELAB (usando a distância euclidiana entre as coordenadas L*, a*, b* como sua métrica de distância) e depois convertendo para o espaço de cores de sua escolha.A uniformidade perceptiva é alcançada ajustando o espaço de cores para aproximar as não linearidades do sistema visual humano.

Alguns recursos relacionados:

ColorBrewer - Conjuntos de cores projetados para serem distinguíveis ao máximo para uso em mapas.

Escapando do RGBland:Selecionando Cores para Gráficos Estatísticos - Um relatório técnico que descreve um conjunto de algoritmos para gerar bens (ou seja,conjuntos de cores maximamente distinguíveis) no espaço de cores hcl.

Aqui está um código para alocar cores RGB uniformemente em torno de uma roda de cores HSL de luminosidade 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;
}

Não é também um fator a ordem em que você configura as cores?

Por exemplo, se você usar a ideia do Dillie-Os, você precisa misturar as cores o máximo possível.0 64 128 256 é de um para o outro.mas 0 256 64 128 em uma roda ficaria mais “distante”

Isso faz sentido?

Eu li em algum lugar que o olho humano não consegue distinguir entre menos de 4 valores de diferença.então isso é algo para se manter em mente.O algoritmo a seguir não compensa isso.

Não tenho certeza se isso é exatamente o que você deseja, mas esta é uma maneira de gerar aleatoriamente valores de cores não repetidos:

(cuidado, pseudocódigo inconsistente à frente)

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

Uma maneira de otimizar isso para obter melhor visibilidade seria comparar a distância entre cada nova cor e todas as cores da 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);
}

Mas essa abordagem desaceleraria significativamente o seu algoritmo.

Outra maneira seria descartar a aleatoriedade e percorrer sistematicamente cada 4 valores e adicionar uma cor a uma matriz no exemplo acima.

Eu sei que este é um post antigo, mas o encontrei enquanto procurava uma solução PHP para o tópico e finalmente encontrei uma solução simples:

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

Então basta chamar a função random_color() onde $i identifica a cor, $n o número de cores possíveis, $sat a saturação e $br o brilho.

Para alcançar o "mais distinguível", precisamos usar um espaço de cores perceptual como Lab (ou qualquer outro espaço de cores perceptualmente linear) e não RGB.Além disso, podemos quantizar este espaço para reduzir o tamanho do espaço.

Gere o espaço 3D completo com todas as entradas quantizadas possíveis e execute o algoritmo K-means com k=N.Os centros/"meios" resultantes devem ser aproximadamente mais distinguíveis uns dos outros.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top