如何去饱和度一个颜色?

11

我可能没有使用正确的颜色术语,但我希望能够基本上像附图一样缩放颜色。 我一直在寻找饱和度来做到这一点,因为看起来正确的版本只是左侧的饱和度较低的版本。

输入图像描述

我尝试了这个(我发现的),但看起来一点也不正确:

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

有没有人知道一些可以做到这一点的算法?


1
在这里搜索 RGB 到 HSL,你会发现很多关于这个主题的帖子。 - Brad
4个回答

17

对于那些想要避免将所有颜色转换为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);

使用通常的假设,将RGB转换为灰度图像,即绿色、红色和蓝色分别对应于图像的 亮度 以递减的比例。因此L是一幅灰度图像,然后f就是在输入的RGB图像和那张灰度图像之间进行线性插值。


这个算法的解释是什么?它有一个名称吗?我正在尝试学习图像处理。 - kosinix
1
这种方法适用于线性色彩空间,这就是为什么例如https://www.w3.org/WAI/GL/wiki/Relative_luminance中有“乘以2.4次幂”的原因。您需要先将颜色值转换为线性,然后可以像这样计算亮度(尽管相对亮度的因素更好)。之后,您可以反转功率以进行转换(乘以1/2.4次幂)。 - fforw

4

正如@Brad在您的帖子评论中提到的那样,您的第一步是将颜色从RGB转换为HSL或HSV。从那里开始,减少饱和度就很简单-只需减去或除以一个值来减少它。

之后,将您的HSL / HSV颜色转换回RGB即可使用。

如何将RGB颜色更改为HSV?有一个很好的例子,.net中操作颜色也是如此。


2
通过实验得出结论,仅减少饱和度并不足以获得图片中所示的效果。我在下面的代码中使用了OP问题中的颜色。如果只减少饱和度,你会得到以下结果: enter image description here 如果还要减少新颜色的alpha/不透明度,可以获得更好的结果: enter image description here 我假设如果你调整参数,应该能够得到完美的匹配。尝试更改reducedSaturation2alpha(目前为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

1
我也注意到了这个问题。我尝试解决的方法是降低饱和度并提高亮度(数值)。我成功地得到了与我的图片相似的结果。不过我会尝试你的方法,谢谢。 - test
@reedparkes:我尝试调整亮度-问题在于有些时候它会变得足够高(=255),所以你不能再提高它了。这就是alpha可以帮助的地方。实际上,您可能可以使用所有三个来实现所需的结果。 - Victor Zakharov

1
这是我在C#中改变给定Color饱和度的公式。结果等同于应用CSS效果 filter: saturate(...),因为这是W3C规范使用的公式。
private static Color Saturate(Color c, double saturation) {
   // Values from: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement , type="saturate"
   var s = saturation;
   Func<double, int> clamp = i => Math.Min(255, Math.Max(0, Convert.ToInt32(i)));
   return Color.FromArgb(255,
      clamp((0.213 + 0.787 * s) * c.R + (0.715 - 0.715 * s) * c.G + (0.072 - 0.072 * s) * c.B),
      clamp((0.213 - 0.213 * s) * c.R + (0.715 + 0.285 * s) * c.G + (0.072 - 0.072 * s) * c.B),
      clamp((0.213 - 0.213 * s) * c.R + (0.715 - 0.715 * s) * c.G + (0.072 + 0.928 * s) * c.B));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接