Как десатурировать цвет?

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

enter image description here

Я пробовал это (что я нашел), но это не выглядит правильным вообще:

Public Shared Function GetDesaturatedColor(color As Color) As Color
    Const kValue As Double = 0.01R

    Dim greyLevel = (color.R * 0.299R) + _
                    (color.G * 0.587R) + _
                    (color.B * 0.144R)

    Dim r = greyLevel * kValue + (color.R) * (1 - kValue)
    Dim g = greyLevel * kValue + (color.G) * (1 - kValue)
    Dim b = greyLevel * kValue + (color.B) * (1 - kValue)

    ' ColorUtils.ToByte converts the double value 
    ' to a byte safely
    Return color.FromArgb(255, _
                          ColorUtils.ToByte(r), _
                          ColorUtils.ToByte(g), _
                          ColorUtils.ToByte(b))
End Function

кто-нибудь знает какой-нибудь алгоритм, который может это сделать?

3 ответов


Как @Brad упоминалось в комментариях к вашему сообщению, ваш первый шаг-преобразовать цвета из RGB в любой HSL или HSV. Оттуда уменьшение насыщенности тривиально - просто вычесть или разделить насыщение на значение, чтобы уменьшить его.

после этого преобразуйте цвет HSL/HSV обратно в RGB, и он готов к использованию.

как изменить цвет RGB на HSV? имеет хороший пример того, как это сделать, как и управление цветами в .чистая.


для тех, кто хочет избежать преобразования всего в HSL / HSV и обратно, это работает достаточно хорошо (если не правильно в зависимости от того, что человек думает, что "правильное" ненасыщенное изображение):

f = 0.2; // desaturate by 20%
L = 0.3*r + 0.6*g + 0.1*b;
new_r = r + f * (L - r);
new_g = g + f * (L - g);
new_b = b + f * (L - b);

эксперимент показывает, что просто уменьшения насыщенности недостаточно, чтобы получить результат, показанный на рисунке. Я использовал цвета из вопроса OP в коде, показанном ниже. Если вы просто уменьшите насыщенность, вот что вы получите:

enter image description here

если вы также уменьшите Альфа / непрозрачность нового цвета, вы можете достичь лучшего результата:

enter image description here

Я предполагаю, что если вы играете с параметрами, вы должны иметь возможность получить идеальный матч. Пытаться меняется alpha на reducedSaturation2 (в настоящее время =40) и GetSaturation делителя (в настоящее время =1.3)

вот мой пример кода:

Public Function HSVToColor(ByVal H As Double, ByVal S As Double, ByVal V As Double) As Color
  Dim Hi As Integer = (H / 60) Mod 6
  Dim f As Double = H / 60 Mod 1
  Dim p As Integer = V * (1 - S) * 255
  Dim q As Integer = V * (1 - f * S) * 255
  Dim t As Integer = V * (1 - (1 - f) * S) * 255
  Select Case Hi
    Case 0 : Return Color.FromArgb(V * 255, t, p)
    Case 1 : Return Color.FromArgb(q, V * 255, p)
    Case 2 : Return Color.FromArgb(p, V * 255, t)
    Case 3 : Return Color.FromArgb(p, V * 255, q)
    Case 4 : Return Color.FromArgb(t, p, V * 255)
    Case 5 : Return Color.FromArgb(V * 255, q, p)
  End Select
End Function

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
  Dim normalSaturation As Color = Color.FromArgb(255, 216, 53, 45)
  Me.CreateGraphics.FillRectangle(New SolidBrush(normalSaturation), 100, 0, 100, 100)
  Dim reducedSaturation As Color = HSVToColor(normalSaturation.GetHue, normalSaturation.GetSaturation / 1.3, normalSaturation.GetBrightness)
  Dim reducedSaturation2 As Color = Color.FromArgb(40, reducedSaturation)
  Me.CreateGraphics.FillRectangle(New SolidBrush(reducedSaturation2), 0, 0, 100, 100)
End Sub