2008-08-01 5 views
61

Это то, что я неоднократно решает и никогда не нашел решения. Это застряло со мной. Проблема заключается в том, чтобы придумать способ генерации цветов N, которые являются как можно более различимыми, где N является параметром.Функция для создания цветных колес

+0

Последнее, что я проверил [JFreeChart] (http://www.jfree.org/jfreechart/), имеет этот точный алгоритм, и поскольку он с открытым исходным кодом, вы можете проверить, что он делает. Я знаю, что цвета, которые я получаю, не кажутся случайным образом разнесены по кругу или сфере, а скорее выбраны более конкретно. – 2009-09-30 18:00:26

ответ

22

Моя первая мысль об этом - «как генерировать N векторов в пространстве, которое максимизирует расстояние друг от друга». Вы можете видеть, что RGB (или любой другой масштаб, который вы используете, который формирует основу в цветовом пространстве) - это просто векторы. Взгляните на Random Point Picking. Надеюсь, это хорошее начало для вас! После того, как у вас есть набор векторов, которые максимизируют часть, вы можете сохранить их в хеш-таблице или что-то в дальнейшем и просто выполнить произвольные вращения на них, чтобы получить все нужные вам цвета, которые максимально отличаются друг от друга!

Edit: Думая об этой проблеме более, было бы лучше, чтобы отобразить цвета в линейном имении, возможно, (0,0,0) -> (255255255) лексикографически, а затем распределить их равномерно. Я действительно не знаю, как хорошо это сработает, но это должно с тех пор, скажем:

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

+1

Это неверно, потому что цветовое пространство RGB не воспринимается единообразно – 2015-01-13 17:24:11

+0

Я согласен с тем, что звучит логично. RGB в основном делает фиолетовые и оранжевые гибриды, а относительно редкие - сине-зелеными гибридами ... цветовая гамма однородна от инфракрасного до глубокого синего, поэтому приходится выбирать точки, равномерно расположенные вдоль нее. нужен радужный альго. – 2015-09-14 12:46:06

+0

Пожалуйста, подумайте о том, чтобы продолжить/следовать за сайтом StackExchange Color Theory: https://area51.stackexchange.com/proposals/110687/color-theory – 2017-06-22 08:14:49

1

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

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

(Берегитесь, противоречивый псевдокод впереди)

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

Но этот подход significa чтобы замедлить ваш алгоритм.

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

3

Разве это не фактор, который прикажет вам настроить цвета?

Как если бы вы использовали идею Дилли-О, вам нужно как можно больше смешать цвета. 0 64 128 256 от одного к другому. но 0 256 64 128 в колесе будет более «раздельно»

Это имеет смысл?

17

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

+0

Это, вероятно, лучшее решение, так как это довольно просто. Однако есть другие формулы цветоделения, которые следует учитывать, например, CIE2000 или даже CIECAM. – 2015-01-13 17:25:47

7

Некоторые ресурсы:

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

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

+1

Экранирование RGBland - это обязательное условие для чтения справки для выбора чувствительных к восприятию цветовых палитр. – Drake 2013-07-19 09:16:22

6

Вот некоторый код, который выделяет цвета 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; 
} 
1

Я знаю, что это старый пост, но я нашел его, ища 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 saturation и $ br - яркость.

0

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

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