Вопрос

Мотивация

Я хотел бы найти способ взять произвольный цвет и осветлить его на несколько оттенков, чтобы я мог программно создать красивый градиент от одного цвета к более светлой версии.Градиент будет использоваться в качестве фона в пользовательском интерфейсе.

Возможность 1

Очевидно, что я могу просто разделить значения RGB и увеличить их по отдельности на определенную величину.Действительно ли это то, чего я хочу?

Возможность 2

Моей второй мыслью было преобразовать RGB в HSV / HSB / HSL (оттенок, насыщенность, значение / Яркость / Освещенность), немного увеличить яркость, немного уменьшить насыщенность, а затем преобразовать ее обратно в RGB.Будет ли это иметь желаемый эффект в целом?

Это было полезно?

Решение

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

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

Как Сказал Ведж, вы хотите умножить, чтобы сделать вещи ярче, но это работает только до тех пор, пока один из цветов не станет насыщенным (т.е.просмотров 255 или больше).На этом этапе вы можете просто зафиксировать значения до 255, но вы будете незаметно изменять оттенок по мере того, как будете становиться светлее.Чтобы сохранить оттенок, вы хотите сохранить соотношение (средний-самый низкий) / (самый высокий-самый низкий).

Вот две функции в Python.Первый реализует наивный подход, который просто фиксирует значения RGB до 255, если они превышают допустимые.Второй метод перераспределяет избыточные значения, чтобы сохранить оттенок нетронутым.

def clamp_rgb(r, g, b):
    return min(255, int(r)), min(255, int(g)), min(255, int(b))

def redistribute_rgb(r, g, b):
    threshold = 255.999
    m = max(r, g, b)
    if m <= threshold:
        return int(r), int(g), int(b)
    total = r + g + b
    if total >= 3 * threshold:
        return int(threshold), int(threshold), int(threshold)
    x = (3 * threshold - total) / (3 * m - total)
    gray = threshold - x * m
    return int(gray + x * r), int(gray + x * g), int(gray + x * b)

Я создал градиент, начиная со значения RGB (224,128,0) и умножая его на 1,0, 1,1, 1,2 и т.д.до 2.0.Верхняя половина - это результат использования clamp_rgb а нижняя половина - это результат с redistribute_rgb.Я думаю, легко заметить, что перераспределение переполнений дает гораздо лучший результат без необходимости покидать цветовое пространство RGB.

Lightness gradient with clamping (top) and redistribution (bottom)

Для сравнения, вот такой же градиент в цветовых пространствах HLS и HSV, реализованный в Python colorsys модуль.Только тот L компонент был изменен, и для полученных значений RGB было выполнено сжатие.Результаты аналогичны, но требуют преобразования цветового пространства для каждого пикселя.

Lightness gradient with HLS (top) and HSV (bottom)

В C#:

public static Color Lighten(Color inColor, double inAmount)
{
  return Color.FromArgb(
    inColor.A,
    (int) Math.Min(255, inColor.R + 255 * inAmount),
    (int) Math.Min(255, inColor.G + 255 * inAmount),
    (int) Math.Min(255, inColor.B + 255 * inAmount) );
}

Я использовал это повсюду.

Класс ControlPaint в Системе.Windows.Пространство имен Forms имеет статические методы Light и Dark:

public static Color Dark(Color baseColor, float percOfDarkDark);

Эти методы используют частную реализацию HLSColor.Я бы хотел, чтобы эта структура была общедоступной и находилась в системе.Рисование.

В качестве альтернативы, вы можете использовать GetHue, GetSaturation, GetBrightness для цветовой структуры, чтобы получить компоненты HSB.К сожалению, я не нашел обратного преобразования.

Преобразуйте его в RGB и выполните линейную интерполяцию между исходным цветом и целевым цветом (часто белым).Итак, если вам нужно 16 оттенков между двумя цветами, вы делаете:


for(i = 0; i < 16; i++)
{
  colors[i].R = start.R + (i * (end.R - start.R)) / 15;
  colors[i].G = start.G + (i * (end.G - start.G)) / 15;
  colors[i].B = start.B + (i * (end.B - start.B)) / 15;
}

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

float correctionFactor = 0.5f;
float red = (255 - color.R) * correctionFactor + color.R;
float green = (255 - color.G) * correctionFactor + color.G;
float blue = (255 - color.B) * correctionFactor + color.B;
Color lighterColor = Color.FromArgb(color.A, (int)red, (int)green, (int)blue);

Если вам нужна более подробная информация, читайте полную историю в моем блоге.

Очень похожий вопрос с полезными ответами был задан ранее:Как мне определить более темный или светлый цветовой вариант данного цвета?

Краткий ответ:умножьте значения RGB на константу, если вам просто нужно "достаточно хорошо", переведите в HSV, если вам требуется точность.

Я использовал ответ Эндрю и ответ Марка, чтобы сделать это (по состоянию на 1/2013 диапазон ввода для ff отсутствует).

function calcLightness(l, r, g, b) {
    var tmp_r = r;
    var tmp_g = g;
    var tmp_b = b;

    tmp_r = (255 - r) * l + r;
    tmp_g = (255 - g) * l + g;
    tmp_b = (255 - b) * l + b;

    if (tmp_r > 255 || tmp_g > 255 || tmp_b > 255) 
        return { r: r, g: g, b: b };
    else 
        return { r:parseInt(tmp_r), g:parseInt(tmp_g), b:parseInt(tmp_b) }
}

enter image description here

Преобразование в HS (LVB), увеличение яркости и последующее преобразование обратно в RGB - это единственный способ надежно осветлить цвет без изменения значений оттенка и насыщенности (т. е. только осветлить цвет, не изменяя его каким-либо другим способом).

Я делал это обоими способами - с возможностью 2 вы получаете гораздо лучшие результаты.

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

Вы бы хотели изучить возможность 1, если (1) вы можете ограничить используемые цвета и яркости, и (2) вы часто выполняете вычисления при рендеринге.

Для создания фона для пользовательского интерфейса не потребуется большого количества вычислений затенения, поэтому я предлагаю вариант 2.

-Эл.

ЕСЛИ вы хотите создать градиентное затухание, я бы предложил следующую оптимизацию:Вместо того, чтобы выполнять RGB-> HSB-> RGB для каждого отдельного цвета, вы должны вычислять только целевой цвет.Как только вы узнаете целевой RGB, вы можете просто вычислить промежуточные значения в пространстве RGB без необходимости преобразования туда-сюда.Будете ли вы рассчитывать линейный переход или использовать какую-то кривую, зависит от вас.

Способ 1:Преобразуйте RGB в HSL, отрегулируйте HSL, преобразуйте обратно в RGB.

Способ 2: Лерп значения цвета RGB - http://en.wikipedia.org/wiki/Lerp_ (вычисления)

Видишь мой ответ на этот аналогичный вопрос для реализации метода 2 на C #.

Притворись, что ты альфа, слившийся с белым:

oneMinus = 1.0 - amount
r = amount + oneMinus * r
g = amount + oneMinus * g
b = amount + oneMinus * b

где amount равно от 0 до 1, причем 0 возвращает исходный цвет, а 1 возвращает белый.

Возможно, вам захочется смешаться с любым цветом фона, если вы делаете подсветку, чтобы отобразить что-то отключенное:

oneMinus = 1.0 - amount
r = amount * dest_r + oneMinus * r
g = amount * dest_g + oneMinus * g
b = amount * dest_b + oneMinus * b

где (dest_r, dest_g, dest_b) смешивается ли цвет с amount равно от 0 до 1, с возвращением нуля (r, g, b) и 1 возвращающийся (dest.r, dest.g, dest.b)

Я не находил этот вопрос до тех пор, пока он не стал вопросом, связанным с моим первоначальным вопросом.

Однако, используя понимание из этих замечательных ответов.Для этого я собрал воедино хорошую двухстрочную функцию:

Программно осветлить или затемнить шестнадцатеричный цвет (или rgb, а также смешать цвета)

Это версия метода 1.Но с учетом перенасыщения.Как сказал Кит в своем ответе выше;используйте Lerp, чтобы, по-видимому, решить ту же проблему, о которой упоминал Марк, но без перераспределения.Результаты shadeColor2 должно быть намного ближе к тому, чтобы сделать это правильно с помощью HSL, но без накладных расходов.

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

tinycolor("red").lighten().desaturate().toHexString() // "#f53d3d" 

Я бы сначала попробовал номер # 1, но # 2 звучит довольно неплохо.Попробуйте сделать это сами и посмотрите, удовлетворены ли вы результатами, похоже, вам потребуется около 10 минут, чтобы приготовить тест на скорую руку.

Технически, я не думаю, что ни то, ни другое является правильным, но я полагаю, что вам нужен вариант варианта № 2.Проблема в том, что, взяв RGB 990000 и "осветлив" его, на самом деле он просто добавлял бы к Красному каналу (Значение, Яркость, Освещенность), пока вы не доберетесь до FF.После этого (сплошной красный) это означало бы снятие насыщенность чтобы пройти весь путь до сплошного белого цвета.

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

Вы найдете код для преобразования между цветовыми пространствами в библиотека ruby для цветовых инструментов

У меня есть запись в блоге это показывает, как это сделать в Delphi.Это довольно просто, потому что функции ColorRGBToHSL и ColorHLSToRGB являются частью стандартной библиотеки.

Вот пример осветления цвета RGB в Python:

def lighten(hex, amount):
    """ Lighten an RGB color by an amount (between 0 and 1),

    e.g. lighten('#4290e5', .5) = #C1FFFF
    """
    hex = hex.replace('#','')
    red = min(255, int(hex[0:2], 16) + 255 * amount)
    green = min(255, int(hex[2:4], 16) + 255 * amount)
    blue = min(255, int(hex[4:6], 16) + 255 * amount)
    return "#%X%X%X" % (int(red), int(green), int(blue))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top