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





Я где-то читал, что человеческий глаз не может различить менее 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 значения и добавлять цвет в массив в приведенном выше примере.
Разве это не фактор, в котором вы устанавливаете цвета?
Например, если вы используете идею Dillie-Os, вам нужно как можно больше смешивать цвета. 0 64 128 256 - от одного к другому. но 0 256 64 128 в колесе было бы больше "отдельно"
Имеет ли это смысл?
Моя первая мысль об этом - «как сгенерировать N векторов в пространстве, максимально удаленных друг от друга».
Вы можете видеть, что RGB (или любой другой масштаб, который вы используете, который формирует основу в цветовом пространстве) - это просто векторы. Взгляните на Выбор случайных точек. Как только у вас есть набор векторов, которые максимально увеличены друг от друга, вы можете сохранить их в хеш-таблице или что-то в этом роде для последующего использования и просто выполнить произвольное вращение над ними, чтобы получить все желаемые цвета, которые максимально отделены друг от друга!
Если подумать над этой проблемой, было бы лучше отобразить цвета линейным образом, возможно (0,0,0) → (255,255,255) лексикографически, а затем распределить их равномерно.
Я действительно не знаю, насколько хорошо это будет работать, но так должно быть, скажем так:
n = 10
мы знаем, что у нас есть 16777216 цветов (256 ^ 3).
Мы можем использовать Пряжки Алгоритм 515, чтобы найти лексикографически проиндексированный цвет. Вероятно, вам придется отредактировать алгоритм, чтобы избежать переполнения, и, возможно, добавить некоторые незначительные улучшения скорости.
Это неверно, потому что цветовое пространство RGB не является однородным по восприятию.
Согласен, это звучит логично. RGB в основном создает фиолетовые и оранжевые гибриды и относительно редко дает сине-зеленые гибриды ... цветовая шкала однородна от инфракрасного до темно-синего, поэтому необходимо выбирать точки, равномерно расположенные вдоль нее. нужен алгоритм на основе радуги.
Пожалуйста, рассмотрите возможность голосования / подписки на сайт StackExchange Color Theory: area51.stackexchange.com/proposals/110687/color-theory
Было бы лучше найти цвета, максимально далекие от «перцептуально однородного» цветового пространства, например CIELAB (используя евклидово расстояние между координатами L *, a *, b * в качестве метрики расстояния) с последующим преобразованием в цветовое пространство по вашему выбору. Единообразие восприятия достигается за счет настройки цветового пространства для аппроксимации нелинейностей в зрительной системе человека.
Это, вероятно, лучшее решение, поскольку оно довольно простое. Однако есть и другие формулы цветового различия, которые следует учитывать, например, CIE2000 или даже CIECAM.
Некоторые связанные ресурсы:
ColorBrewer - Наборы цветов, предназначенные для максимального различения для использования на картах.
Выход из RGBland: выбор цветов для статистической графики - технический отчет, описывающий набор алгоритмов для создания хороших (т.е. максимально различимых) наборов цветов в цветовом пространстве hcl.
Экранирование RGBland является обязательным к прочтению справочником по выбору воспринимаемых различимых цветовых палитр.
Вот код для равномерного распределения цветов 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;
}
AFAIK легко перенести код с C++ на Java.
нет, когда я не понимаю всех вещей, связанных с сдвигом битов, среди прочего: /
Я предоставил URL-адреса, которые ссылаются на объяснения того, что делает код.
что, если я хочу указать, чтобы цвета отличались от цвета фона, который я предоставляю?
Рассчитайте «расстояние» между сгенерированными цветами и цветом фона. Не используйте цвет, наиболее близкий к вашему фону.
Возможно, это немного сработает, но это даст плохие результаты, CIELAB лучше
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 - яркость.
Вы можете объяснить, что в данном случае означает «я»? Вопрос задан для N номеров. Что такое параметр "i"?
В random_color()$i является «семенем» для генерации оттенка, должно быть числом от 0 до $n, если вы не вводите начальное значение (NULL), функция выбирает случайное значение. $n - это количество возможных цветов для заданной насыщенности и яркости, то есть количества цветов в палитре. Мы в основном разделяем 360 градусов оттенка на $n и используем $i в качестве множителя. Другими словами, более высокий $n даст вам больше цветов, более низкий $n даст вам меньше цветов, но больше будет отличаться друг от друга. $i определит цвет и всегда будет таким же, если вы продолжите использовать эту функцию. Надеюсь, это поможет.
Я понимаю! Спасибо за объяснение. Еще одна вещь ... какие-либо предложения о том, что делать, если у меня есть цвет фона, и я хочу быть как можно дальше от него для всех цветов?
Вам нужно добавить 180 градусов к оттенку вашего цвета, сохраняя насыщенность и ценность. Задайте для этого новый вопрос, вставьте ссылку сюда, и я объясню дальше!
Для достижения «наиболее различимого» нам необходимо использовать перцептивное цветовое пространство, такое как Lab (или любое другое перцептивно линейное цветовое пространство), отличное от RGB. Кроме того, мы можем квантовать это пространство, чтобы уменьшить его размер.
Сгенерируйте полное трехмерное пространство со всеми возможными квантованными записями и запустите алгоритм K-средних с K=N. Полученные центры / «средства» должны быть примерно максимально различимы друг от друга.
В последний раз я проверял, что у JFreeChart есть этот точный алгоритм, и, поскольку это открытый исходный код, вы можете проверить, что он делает. Я знаю, что цвета, которые я получаю, не кажутся случайными расположенными по кругу или сфере, а скорее выбраны более конкретно.