Я пытаюсь создать алгоритм, который будет выводить набор различных значений цвета RGB, которые должны быть как можно более разными. Например:
следующий набор из 3-х цветов:
следующие 3 цвета будут:
Следующие цвета должны быть между новыми интервалами. По сути, моя идея состоит в том, чтобы пройти через систематические интервалы всего цветового спектра, подобные этому:
Набор из 13 цветов должен включать цвет между 1 и 7, продолжайте этот шаблон бесконечно.
В настоящее время я пытаюсь применить этот шаблон к алгоритму для значений RGB, поскольку мне это не кажется тривиальным. Я благодарен за любые подсказки, которые могут указать мне на решение.
HSL описан здесь en.wikipedia.org/wiki/HSL_and_HSV, и вы фактически выбрали 0/120/240 градусов в нижней части этой диаграммы для ваших первых 3 значений, а затем добавили 60/180/300 для ваших вторых 3 значений в нижней части этой диаграммы en.wikipedia.org/wiki/HSL_and_HSV#/media/…
@MarkSetchell спасибо за ваш вклад! Вместо этого я думал об использовании HSV, но мне все еще интересно, как решить эту проблему в пространстве RGB. Кроме того, вы правы в своем первом комментарии; высочайшее разнообразие может быть немного неоднозначным.
Какова цель? Ваш выбор будет плохим для контраста, настолько плохим, чтобы быстро передать информацию мозгу читателя. Поэтому я бы не рекомендовал решение в других комментариях (например, изменить только оттенок), но для карт.
Взгляните на это: Многоканальное растровое изображение в RGB просто берет видимый спектр и делит его на N длины волн/цвета, чтобы у вас было N различных цветов (которые также можно использовать в качестве основных цветов для многополосного рендеринга, поскольку они суммируются с белым, и их линейная комбинация позволяет добиться любого цвета).





Статья в Википедии о различии цветов стоит прочитать, как и статья о «недорогом приближении» от CompuPhase, связанный с ним. Я буду основывать свою попытку на последнем.
Вы не указали язык, поэтому я напишу его на оптимизированном для нет Python (за исключением целочисленных оптимизаций, уже представленных в справочной статье), чтобы его можно было легко перевести на другие языки.
n_colors = 25
n_global_moves = 32
class Color:
max_weighted_square_distance = (((512 + 127) * 65025) >> 8) + 4 * 65025 + (((767 - 127) * 65025) >> 8)
def __init__(self, r, g, b):
self.r, self.g, self.b = r, g, b
def weighted_square_distance(self, other):
rm = (self.r + other.r) // 2 # integer division
dr = self.r - other.r
dg = self.g - other.g
db = self.b - other.b
return (((512 + rm) * dr*dr) >> 8) + 4 * dg*dg + (((767 - rm) * db*db) >> 8)
def min_weighted_square_distance(self, index, others):
min_wsd = self.max_weighted_square_distance
for i in range(0, len(others)):
if i != index:
wsd = self.weighted_square_distance(others[i])
if min_wsd > wsd:
min_wsd = wsd
return min_wsd
def is_valid(self):
return 0 <= self.r <= 255 and 0 <= self.g <= 255 and 0 <= self.b <= 255
def add(self, other):
return Color(self.r + other.r, self.g + other.g, self.b + other.b)
def __repr__(self):
return f"({self.r}, {self.g}, {self.b})"
colors = [Color(127, 127, 127) for i in range(0, n_colors)]
steps = [Color(dr, dg, db) for dr in [-1, 0, 1]
for dg in [-1, 0, 1]
for db in [-1, 0, 1] if dr or dg or db] # i.e., except 0,0,0
moved = True
global_move_phase = False
global_move_count = 0
while moved or global_move_phase:
moved = False
for index in range(0, len(colors)):
color = colors[index]
if global_move_phase:
best_min_wsd = -1
else:
best_min_wsd = color.min_weighted_square_distance(index, colors)
for step in steps:
new_color = color.add(step)
if new_color.is_valid():
new_min_wsd = new_color.min_weighted_square_distance(index, colors)
if best_min_wsd < new_min_wsd:
best_min_wsd = new_min_wsd
colors[index] = new_color
moved = True
if not moved:
if global_move_count < n_global_moves:
global_move_count += 1
global_move_phase = True
else:
global_move_phase = False
print(f"n_colors: {n_colors}")
print(f"n_global_moves: {n_global_moves}")
print(colors)
Сначала все цвета устанавливаются серыми, то есть помещаются в центр цветового куба RGB, а затем перемещаются в цветовом кубе таким образом, чтобы максимизировать минимальное расстояние между цветами.
Для экономии процессорного времени вместо самого расстояния используется квадрат расстояния, что потребовало бы вычисления квадратного корня.
Цвета перемещаются по одному, максимум на 1 в каждом из 3 направлений, к одному из смежных цветов, максимальному минимальному удалению от других цветов. При этом глобальное минимальное расстояние (приблизительно) максимизируется.
Фазы «глобального хода» нужны для того, чтобы преодолевать ситуации, когда ни один цвет не двигался бы, но принуждение всех цветов к перемещению в положение, не намного худшее, чем их текущее, приводит к тому, что целое находит лучшую конфигурацию с последующими регулярными ходами. . Лучше всего это можно увидеть с 3 цветами и без глобальных перемещений, изменив взвешенное квадратное расстояние до простого rd*rd+gd*gd+bd*bd: конфигурация становится
[(2, 0, 0), (0, 253, 255), (255, 255, 2)]
в то время как, добавив 2 глобальных хода, конфигурация становится ожидаемой
[(0, 0, 0), (0, 255, 255), (255, 255, 0)]
Алгоритм выдает только одно из нескольких возможных решений. К сожалению, поскольку используемая метрика не является евклидовой, невозможно просто перевернуть 3 измерения в 8 возможных комбинациях (т. е. заменить r→255-r и/или то же самое для g и/или b), чтобы получить эквивалентные решения. Вероятно, лучше всего ввести случайность в том порядке, в котором опробуются шаги перемещения цвета, и варьировать случайное начальное число.
Я не делал поправку на гамма монитора, потому что его цель как раз в том, чтобы изменить разнос яркостей, чтобы компенсировать разную чувствительность глаз при высокой и низкой яркости. Конечно, гамма-кривая экрана отличается от идеальной, и изменение гаммы (в зависимости от системы!) даст лучшие результаты, но стандартная гамма является хорошей отправной точкой.
Это результат работы алгоритма для 25 цветов:
Обратите внимание, что первые 8 цветов (нижняя строка и первые 3 цвета строки выше) расположены близко к углам куба RGB (они не являются углами в из-за неевклидовой метрики).
Сначала позвольте мне спросить, хотите ли вы остаться в sRGB и пройтись по каждой комбинации RGB?
ИЛИ (и это мое предположение) вам действительно нужны цвета, которые «наиболее далеки» друг от друга? Поскольку вы использовали термин "отчетливый", я расскажу о поиске цветовые отличия..
sRGB — это цветовое пространство, которое относится к вашему дисплею/выходу. И хотя гамма-кривая «вроде» однородна для восприятия, общее цветовое пространство sRGB — нет, оно предназначено скорее для моделирования дисплея, чем для человеческого восприятия.
Чтобы определить «максимальное расстояние» между цветами с точки зрения восприятия, вам нужна модель восприятия, либо с использованием цветового пространства, которое является однородным для восприятия, либо с использованием модели цветового восприятия (CAM).
Поскольку в результате вам просто нужны значения sRGB, то, вероятно, достаточно использовать однородное цветовое пространство, например CIELAB или CIELUV. Поскольку в них используются декартовы координаты, разница между двумя цветами в (L*a*b*) — это просто евклидово расстояние.
Если вы хотите работать с полярными координатами (т. е. с углом оттенка), вы можете пройти один шаг за пределы CIELAB в CIELCh.
Я предлагаю Сайт Брюса Линдблума для соответствующей математики.
Упрощенные шаги:
Преобразовать LAB в XYZ.
Преобразование XYZ в sRGB.
Добавьте гамма-кривую sRGB к каждому каналу.
Если вас меньше заботит единообразие восприятия, вы можете просто остаться в sRGB, хотя результаты будут менее точными. В этом случае все, что вам нужно сделать, это взять разницу каждого канала относительно 255 (т.е. инвертировать каждый канал):
Вот некоторые сравнения двух методов, рассмотренных выше:
Тот же начальный цвет, но с использованием CIELAB L* C* h* (и просто поворотом оттенка на 180 градусов, без настройки L*).
Вот в CIELAB LCh, просто поворот оттенка на 180:
И снова в LCh, но на этот раз еще и настраивая L* как (100 - Л*firstcolor)
Теперь вы заметите большое изменение угла оттенка на них — правда в том, что, хотя LAB «несколько однороден», он довольно волнистый в области синего.
Взгляните на цифры:
Они кажутся существенно разными по оттенку, цветности, a, b... но они создают одно и то же значение цвета HEX! Так что да, даже у CIELAB есть неточности (особенно у синего).
Если вам нужна большая точность, попробуйте CIECAM02
Некоторые идеи для размышления... 1) черный и белый - это два цвета, которые дальше всего отстоят друг от друга на цветовом кубе, но у вас их нет 2) расстояние зависит от наблюдателя, красно-зеленая дальтонизм очень распространена 3) вы Возможно, вам захочется взглянуть на цветовое пространство HSL и максимально увеличить насыщенность и значение, а затем просто разделить круг 360 оттенков на любое количество цветов, которое вы хотите - это более или менее то, что вы интуитивно уже делаете.