Создайте набор цветов RGB с максимальным разнообразием

Я пытаюсь создать алгоритм, который будет выводить набор различных значений цвета RGB, которые должны быть как можно более разными. Например:

следующий набор из 3-х цветов:

  1. (255, 0, 0) [красный]
  2. (0, 255, 0) [Зеленый]
  3. (0, 0, 255) [Синий]

следующие 3 цвета будут:

  1. (255, 255, 0) [Желтый]
  2. (0, 255, 255) [Голубой]
  3. (255, 0, 255) [Пурпурный]

Следующие цвета должны быть между новыми интервалами. По сути, моя идея состоит в том, чтобы пройти через систематические интервалы всего цветового спектра, подобные этому:

Создайте набор цветов RGB с максимальным разнообразием

Набор из 13 цветов должен включать цвет между 1 и 7, продолжайте этот шаблон бесконечно.

В настоящее время я пытаюсь применить этот шаблон к алгоритму для значений RGB, поскольку мне это не кажется тривиальным. Я благодарен за любые подсказки, которые могут указать мне на решение.

Некоторые идеи для размышления... 1) черный и белый - это два цвета, которые дальше всего отстоят друг от друга на цветовом кубе, но у вас их нет 2) расстояние зависит от наблюдателя, красно-зеленая дальтонизм очень распространена 3) вы Возможно, вам захочется взглянуть на цветовое пространство HSL и максимально увеличить насыщенность и значение, а затем просто разделить круг 360 оттенков на любое количество цветов, которое вы хотите - это более или менее то, что вы интуитивно уже делаете.

Mark Setchell 28.05.2019 11:30

HSL описан здесь en.wikipedia.org/wiki/HSL_and_HSV, и вы фактически выбрали 0/120/240 градусов в нижней части этой диаграммы для ваших первых 3 значений, а затем добавили 60/180/300 для ваших вторых 3 значений в нижней части этой диаграммы en.wikipedia.org/wiki/HSL_and_HSV#/media/…

Mark Setchell 28.05.2019 11:41

@MarkSetchell спасибо за ваш вклад! Вместо этого я думал об использовании HSV, но мне все еще интересно, как решить эту проблему в пространстве RGB. Кроме того, вы правы в своем первом комментарии; высочайшее разнообразие может быть немного неоднозначным.

T A 28.05.2019 12:08

Какова цель? Ваш выбор будет плохим для контраста, настолько плохим, чтобы быстро передать информацию мозгу читателя. Поэтому я бы не рекомендовал решение в других комментариях (например, изменить только оттенок), но для карт.

Giacomo Catenazzi 28.05.2019 14:07

Взгляните на это: Многоканальное растровое изображение в RGB просто берет видимый спектр и делит его на N длины волн/цвета, чтобы у вас было N различных цветов (которые также можно использовать в качестве основных цветов для многополосного рендеринга, поскольку они суммируются с белым, и их линейная комбинация позволяет добиться любого цвета).

Spektre 29.05.2019 09:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
1 474
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Статья в Википедии о различии цветов стоит прочитать, как и статья о «недорогом приближении» от 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 возможных комбинациях (т. е. заменить r255-r и/или то же самое для g и/или b), чтобы получить эквивалентные решения. Вероятно, лучше всего ввести случайность в том порядке, в котором опробуются шаги перемещения цвета, и варьировать случайное начальное число.

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

Это результат работы алгоритма для 25 цветов:

output for 25 colors as 5 by 5 color patches

Обратите внимание, что первые 8 цветов (нижняя строка и первые 3 цвета строки выше) расположены близко к углам куба RGB (они не являются углами в из-за неевклидовой метрики).

Сначала позвольте мне спросить, хотите ли вы остаться в sRGB и пройтись по каждой комбинации RGB?

ИЛИ (и это мое предположение) вам действительно нужны цвета, которые «наиболее далеки» друг от друга? Поскольку вы использовали термин "отчетливый", я расскажу о поиске цветовые отличия..

Моделируйте свое восприятие

sRGB — это цветовое пространство, которое относится к вашему дисплею/выходу. И хотя гамма-кривая «вроде» однородна для восприятия, общее цветовое пространство sRGB — нет, оно предназначено скорее для моделирования дисплея, чем для человеческого восприятия.

Чтобы определить «максимальное расстояние» между цветами с точки зрения восприятия, вам нужна модель восприятия, либо с использованием цветового пространства, которое является однородным для восприятия, либо с использованием модели цветового восприятия (CAM).

Поскольку в результате вам просто нужны значения sRGB, то, вероятно, достаточно использовать однородное цветовое пространство, например CIELAB или CIELUV. Поскольку в них используются декартовы координаты, разница между двумя цветами в (L*a*b*) — это просто евклидово расстояние.

LAB diffrence equation

Если вы хотите работать с полярными координатами (т. е. с углом оттенка), вы можете пройти один шаг за пределы CIELAB в CIELCh.


Как это сделать

Я предлагаю Сайт Брюса Линдблума для соответствующей математики.

Упрощенные шаги:

  1. Линеаризуйте sRGB, удалив гамма-кривую из каждого из трех цветовых каналов.
  2. Преобразуйте линеаризованные значения в CIE XYZ (используйте D65, без адаптации)
  3. Преобразовать XYZ в L* a* b*
  4. Найди противоположное:
    а. Теперь найдите «противоположный» цвет, проведя линию через 0, сделав линию равной по расстоянию от обеих сторон нуля. ИЛИ
    б. ALT: Сделайте еще одно преобразование из LAB в CIELCh, затем найдите противоположное, повернув оттенок на 180 градусов. Затем конвертируйте обратно в LAB.
  5. Преобразовать LAB в XYZ.

  6. Преобразование XYZ в sRGB.

  7. Добавьте гамма-кривую sRGB к каждому каналу.

Оставаться в sRGB?

Если вас меньше заботит единообразие восприятия, вы можете просто остаться в sRGB, хотя результаты будут менее точными. В этом случае все, что вам нужно сделать, это взять разницу каждого канала относительно 255 (т.е. инвертировать каждый канал):

В чем разница различий?

Вот некоторые сравнения двух методов, рассмотренных выше:

Для начального цвета #0C5490 sRGB Difference Method:

Тот же начальный цвет, но с использованием CIELAB L* C* h* (и просто поворотом оттенка на 180 градусов, без настройки L*).

Начальный цвет #DFE217, метод различия sRGB:

Вот в CIELAB LCh, просто поворот оттенка на 180:

И снова в LCh, но на этот раз еще и настраивая L* как (100 - Л*firstcolor)

Теперь вы заметите большое изменение угла оттенка на них — правда в том, что, хотя LAB «несколько однороден», он довольно волнистый в области синего.

Взгляните на цифры:

Они кажутся существенно разными по оттенку, цветности, a, b... но они создают одно и то же значение цвета HEX! Так что да, даже у CIELAB есть неточности (особенно у синего).

Если вам нужна большая точность, попробуйте CIECAM02

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