it-swarm.com.ru

Программно облегчить цвет

мотивация

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

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

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

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

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

70
Dave Lockhart

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

38
Grey Panther

Как сказал Клин , вы хотите умножить, чтобы сделать вещи ярче, но это работает только до тех пор, пока один из цветов не станет насыщенным (то есть достигнет 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)

54
Mark Ransom

В 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) );
}

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

23
Nick

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

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

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

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

11
Ilya Ryzhenkov

Преобразуйте его в 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;
}
8
Adam Rosenfield

Чтобы получить более светлую или более темную версию данного цвета, вы должны изменить его яркость. Вы можете сделать это легко даже без преобразования вашего цвета в 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);

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

6
Pavel Vladov

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

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

3
Wedge

Я использовал ответ Эндрю и ответ Марка, чтобы сделать это (по состоянию на 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

3
user1873073

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

2
David Arno

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

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

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

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

-Аль.

2
A I Breveleri

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

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

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

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

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

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

1
Pimp Trizkit

Способ 1: конвертировать RGB в HSL, настроить HSL, преобразовать обратно в RGB.

Метод 2: Lerp значения цвета RGB - http://en.wikipedia.org/wiki/Lerp_ (вычисления)

Смотрите мой ответ на этот похожий вопрос для реализации метода 2 на C #.

1
Keith

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

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)

1
prewett

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

1
VoidPointer

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

0
davr

Технически, я не думаю, что либо является правильным, но я считаю, что вы хотите вариант варианта № 2. Проблема в том, что RGB 990000 и "осветление" действительно добавлялись на красный канал (Value, Brightness, Lightness), пока вы не добрались до FF. После этого (сплошной красный) было бы убрать насыщенность, чтобы перейти к сплошному белому.

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

0
Pseudo Masochist

Вот пример освещения цвета 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))
0
Aidan

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

0
Lawrence Barsanti

Вы найдете код для преобразования между цветовыми пространствами в библиотеке color-tools Ruby

0
Thibaut Barrère