Вопрос

Это то, что я много раз псевдодолгал и так и не нашел решения.

Проблема состоит в том, чтобы придумать способ генерировать N цвета, которые максимально различимы там, где N является параметром.

Это было полезно?

Решение

Моя первая мысль по этому поводу - "как сгенерировать N векторов в пространстве, которые максимально удаляются друг от друга".

Вы можете видеть, что RGB (или любая другая используемая вами шкала, которая служит основой в цветовом пространстве) - это всего лишь векторы.Взгляните на Случайный Выбор Точки.Как только у вас будет набор векторов, которые максимально удалены друг от друга, вы можете сохранить их в хэш-таблице или во что-то еще на потом и просто выполнять случайные вращения над ними, чтобы получить все желаемые цвета, которые максимально удалены друг от друга!

Подумав об этой проблеме подробнее, было бы лучше отобразить цвета линейным образом, возможно (0,0,0) → (255,255,255) лексикографически, а затем распределить их равномерно.

Я действительно не знаю, насколько хорошо это сработает, но так и должно быть, поскольку, скажем так:

n = 10

мы знаем, что у нас есть 16777216 цветов (256 ^ 3).

Мы можем использовать Алгоритм Застежек 515 чтобы найти лексикографически индексированный цвет.\frac {\binom {256^3} {3}} {n} * i.Вероятно, вам придется отредактировать алгоритм, чтобы избежать переполнения, и, возможно, добавить некоторые незначительные улучшения скорости.

Другие советы

Было бы лучше всего найти максимально удаленные цвета в "перцептивно однородном" цветовом пространстве, напримерCIELAB (используя евклидово расстояние между координатами L *, a *, b * в качестве показателя расстояния), а затем преобразуйте в выбранное вами цветовое пространство.Единообразие восприятия достигается путем настройки цветового пространства таким образом, чтобы оно приближалось к нелинейностям зрительной системы человека.

Некоторые связанные ресурсы:

Устройство для приготовления пива - Наборы цветов, разработанные таким образом, чтобы быть максимально различимыми для использования на картах.

Убегающий RGBland:Выбор цветов для статистической графики - Технический отчет, описывающий набор алгоритмов для генерации хороших (т.е.максимально различимый) набор цветов в цветовом пространстве hcl.

Вот некоторый код для равномерного распределения цветов RGB вокруг цветового круга HSL заданной яркости.

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

Разве это не также влияет на то, в каком порядке вы настраиваете цвета?

Например, если вы используете идею Dillie-Os, вам нужно как можно больше смешивать цвета.0 64 128 256 - это переход от одного к следующему.но 0 256 64 128 в колесе было бы более "обособленным".

Есть ли в этом смысл?

Я где-то читал, что человеческий глаз не может различить менее 4 значений друг от друга.так что это то, о чем следует помнить.Следующий алгоритм этого не компенсирует.

Я не уверен, что это именно то, что вы хотите, но это один из способов случайной генерации неповторяющихся значений цвета:

(будьте осторожны, впереди несогласованный псевдокод)

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

Один из способов оптимизировать это для лучшей видимости - сравнить расстояние между каждым новым цветом и всеми цветами в массиве:

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

Но такой подход значительно замедлил бы работу вашего алгоритма.

Другим способом было бы отказаться от случайности и систематически перебирать каждые 4 значения и добавлять цвет к массиву в приведенном выше примере.

Я знаю, что это старый пост, но я нашел его, когда искал PHP-решение для этой темы, и, наконец, нашел простое решение:

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

Поэтому просто вызовите функцию random_color(), где $ i определяет цвет, $ n - количество возможных цветов, $ sat - насыщенность и $ br - яркость.

Чтобы добиться "наибольшей различимости", нам нужно использовать цветовое пространство восприятия, такое как Lab (или любое другое линейное цветовое пространство восприятия), а не RGB.Кроме того, мы можем квантовать это пространство, чтобы уменьшить его размер.

Сгенерируйте полное 3D-пространство со всеми возможными квантованными записями и запустите алгоритм K-средних с k=N.Результирующие центры/"средние значения" должны быть приблизительно наиболее отличимыми друг от друга.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top