图像处理入门(C# 和 GDI+)第 6 部分——HSL 颜色空间






4.84/5 (56投票s)
讨论 HSL 色彩空间,包括颜色选择器和图像滤镜的代码
引言
嗯,写这样的文章已经有一段时间了。鉴于目前工作有点慢,我想写一篇关于重要主题——色彩的文章。一个重要的提醒——我有色盲。所以当我谈论颜色如何工作时,我主要听取别人的说法。
背景(可选)
我敢肯定我们大多数人都知道,当您需要为 PC 指定颜色时,您会使用 RGB 三元组,如果要指定透明度,则使用 ARGB。这本质上意味着您的 CRT 有三个颜色枪,您的 LCD 有三组彩色灯来构成一个像素。这三种颜色不同级别的混合等于您的计算机可以显示的颜色范围,如下所示:
然而,这并非描述颜色的唯一可能方式,也不是一种对人类有意义的方法。如果我问您如何使用 RGB 来描述橙色、黄色或紫色,您很可能需要经过一些试错才能弄清楚。HSL 是最常见的颜色系统,它存在是为了方便人类而不是机器。
就像 RGB 代表红色、绿色、蓝色一样,HSL 也是一个首字母缩略词,在这里代表色相 (Hue)、饱和度 (Saturation) 和亮度 (Luminance)。最好按此顺序指定这三个颜色分量,因为它们代表了对颜值的不断细化(也就是说,如果没有色相,饱和度和亮度值几乎没有意义)。
色相
色相是实际的基础颜色,不受亮度或强度的任何修改。它通常表示为一个圆,其中色相值(范围从 0 到 360)表示颜色在色轮上的角度(以度为单位)。下图显示了色相圆,其亮度 (Luminance) 和饱和度 (Saturation) 恒定为 50%。
饱和度
饱和度描述了颜色的“色彩丰富程度”,例如,荧光色将具有高饱和度。为了说明这一点,我提供了三个截图,都是色相圆,亮度为 0,饱和度分别为 .25、.5 和 .75。饱和度为 0 使图像变为灰度(因为它没有任何颜色),因此我不提供 100% 饱和度的图像,因为我想让显示的范围均匀。
亮度
亮度描述了颜色的明暗程度,因此全亮度始终为白色,无亮度始终为黑色。为了说明这一点,我提供了三个截图,都是色相圆,亮度为 0,饱和度分别为 .25、.5 和 .75。
颜色图表
示例应用程序继续基于之前的章节构建,因此为其他项目构建了代码库。现在有一个名为“色彩空间”的新菜单,旨在将来扩展以涵盖其他色彩空间。“HSL 图表”菜单项会弹出一个对话框,其中包含一个色相圆和一个侧面的滑块,如下所示:

色相轮从中心到外部改变饱和度或亮度,然后滑块相应地改变亮度或饱和度。这应该会让您非常清楚这些参数是如何工作的,以及它们如何修改颜色。
使用代码
那么,我们有了这个色彩空间,我们该如何使用它呢?嗯,立刻会想到两件事。首先,我们可以提供一种方法,让用户可以使用这个色彩空间选择颜色,而不是提供 RGB 三元组。其次,我们可以提供图像滤镜,允许根据这三个值修改图像。但要做到这些(甚至要做到您已经看到的一切),我们需要能够在色彩空间中移动,我们需要能够在 HLS 和 RGB 之间进行转换。为了做到这一点,我们将要检查的第一个组件是 HLS 类。
HLS 类
所有新代码都在 ColorSpace.cs 文件中。其中的第一个类称为 YUV,这是另一个我编写了类的颜色系统,但在此不进行检查。接下来是 HLS 类,它封装了一个 HLS 颜色。它将这些值保存在私有成员中,并通过属性公开它们,以便我们可以纠正超出范围的值。我选择不抛出异常,因为当我们将此类与滤镜一起使用时,我们几乎肯定会传递一个超出范围的值。
private float h;
private float s;
private float l;
public float Hue
{
get
{
return h;
}
set
{
// Note that we don't just clamp, as 365 degrees, for
// example, is 5 degrees plus a full turn.
h = (float)(Math.Abs(value)%360);
}
}
public float Saturation
{
get
{
return s;
}
set
{
s = (float)Math.Max(Math.Min(1.0, value), 0.0);
}
}
public float Luminance
{
get
{
return l;
}
set
{
l = (float)Math.Max(Math.Min(1.0, value), 0.0);
}
}
无参数的构造函数是私有的,这样我们就不能在不指定其值的情况下创建 HLS 对象。我们还提供了一个名为 RGB 的属性,它返回一个映射到当前 HLS 值的 Color。
此外,还提供了两个静态方法,它们可以从 Color 或指定的红色、绿色和蓝色值返回一个 HSL 对象。我们的滤镜将使用这些静态方法来构建 HLS 对象,然后修改其中一个值,然后请求修改后的颜色。
颜色选择器
大多数 HSL 颜色选择器会显示一个从中心到边缘饱和度变化的色相轮,然后通过一个滑块来设置所选色相/饱和度组合的亮度。我不喜欢这种格式,因为自然地,圆圈中心附近的值代表性不足,并且更难选择。相反,我提出了一种由三个滑块组成的系统,分别用于色相、饱和度和亮度。色相滑块上的饱和度和亮度设置为 .5,饱和度滑块上的亮度也设置为 .5。因此,从左到右直观地选择颜色。

正如您所见,提供了文本框以及滑块,右侧显示选定的颜色,还显示其 RGB 值。测试应用程序将记住选定的颜色,并在按下 OK 时使用该颜色初始化对话框。HSLColorPicker 具有一个 SelectedColor 属性,可以在显示对话框之前设置,并且在对话框关闭后返回选定的颜色。它返回一个 Color 而不是 HSL 对象,但如果需要,可以轻松更改。
图像滤镜
提供了三个图像滤镜,分别用于色相、饱和度和亮度。滤镜接受一个浮点数,并将要过滤的值乘以该数字,因此 1 是恒等变换。这会导致所有值均匀变化,但副作用是使值为 0 的值完全不变。有许多方法可以解决此问题,包括在乘法之前向值添加一个小数,或者接受一个要添加的值以及一个要相乘的值。由于色相轮的性质,色相滤镜有些奇怪,它只是将颜色更改为不相关的颜色。然而,饱和度和亮度滤镜非常有用,值得纳入任何图像处理库。
一如既往,我将我的儿子作为我的滤镜模型。从上到下依次是正常图像、色相滤镜、饱和度滤镜和亮度滤镜。我试图使用极端值来夸大效果,使其显而易见。饱和度效果尤其不明显,因为他的汽车本身就是荧光色的。
结论
有许多方法可以表示颜色,在本文中,我重点介绍了一种在绘图程序等中常用的方法,并且这种方法很容易被人理解。这意味着它既是让人们选择颜色的一种好方法,也是通过增强或抑制这些值进行过滤会产生对人眼有统一意义的效果。任何需要用户选择颜色的人都应该考虑使用 HSL 作为实现这一目的的方法。
历史
1.0 首次发布。还修复了一个错误,我认为图像的 .Save 方法应该以正确的格式保存为文件扩展名,但它似乎总是保存为 PNG。代码现在可以自行确定编码器。