Стоит прочесть статью в Википедии о разнице в цвете , как и статью о «недорогом приближении» от CompuPhase, на которую она ссылается.Я буду основывать свою попытку на последнем.
Вы не указали язык, поэтому я напишу его на не оптимизированном Python (за исключением целочисленных оптимизаций, уже присутствующих в справочнике).article), чтобы его можно было легко перевести на другие языки.
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 глобальных движения, конфигурация становится ожидаемойone
[(0, 0, 0), (0, 255, 255), (255, 255, 0)]
Алгоритм дает только одно из нескольких возможных решений.К сожалению, поскольку используемая метрика не является евклидовой, невозможно просто перевернуть 3 измерения в 8 возможных комбинациях (то есть заменить r
→ 255-r
и / или то же самое для g
и / или b
) чтобы получить эквивалентные решения.Вероятно, лучше ввести случайность в том порядке, в котором выполняются шаги перемещения цвета, и варьировать случайное начальное число.
Я не исправил для гамма монитора , потому что его цель именноизменения расстояния яркости, чтобы компенсировать различную чувствительность глаз при высокой и низкой яркости.Конечно, кривая гаммы экрана отклоняется от идеальной, и модификация гаммы (в зависимости от системы!) Даст лучшие результаты, но стандартная гамма является хорошей отправной точкой.
Это результат выводаалгоритм для 25 цветов:
![output for 25 colors as 5 by 5 color patches](https://i.stack.imgur.com/Tmjf6.png)
Обратите внимание, что первые 8 цветов (нижний ряд и первые 3 цвета ряда выше) близки куглы куба RGB (они не при углах из-за неевклидовой метрики).