在 .NET 中操作颜色 - 第一部分






4.96/5 (269投票s)
理解并使用 .NET 中的颜色模型
- 下载演示项目 (.NET 1.1) - 58.8 KB
- 下载 C# 源文件 (.NET 1.1) - 110.0 KB
- 下载 C# 源文件 (.NET 2.0) - 111.2 KB
- 下载 VB 源文件 (.NET 2.0) - 115.7 KB
目录 |
一个将一种颜色模型转换为另一种颜色模型的应用程序示例。 |
引言
为什么要写一篇关于“颜色”的文章? 在我开始写这个系列之前,我也问了自己同样的问题。
事实上,在 .NET 中,只有两种颜色格式可以使用:RGB 颜色模型和 HSB 颜色模型。这两种模型都封装在 `System.Drawing` 命名空间中的 `Color` 结构体里。
这对于简单的用途来说已经足够了,例如更改组件的背景颜色,但如果我们要开发图形工具(或涉及颜色格式转换的工具)则远远不够。
当我学习如何为我的自定义控件添加炫酷的设计时支持时,我开始研究其他格式,例如 CMYK。
但是,我很快就意识到我编写的实用工具在其他项目中也会非常有用,比如我的 SVG 编辑器。
因此,我发表了这个包含 3 篇文章的系列。本文的预期内容如下:
- 第一部分:本文将介绍颜色空间以及最常用的颜色空间。
- 第二部分:将介绍专用控件,或者如何使用自定义组件选择/定义颜色。我将分享很多我的自定义控件用于此目的。
- 第三部分:将介绍如何在设计时使用这些颜色控件。我将创建一个关于如何创建你自己的编辑器的教程。
让我们从一些定义开始。
“颜色(或 colour,见拼写差异)是对应于人类的视觉感知属性,即我们称之为红色、黄色、白色等的范畴。颜色源于光的频谱(光能量随波长的分布)与眼睛中光感受器的光谱敏感性相互作用。颜色类别和颜色的物理规格也与物体、材料、光源等相关,基于它们的光物理特性,如光吸收、反射或发射光谱。”
“色度学是描述颜色的科学,它通过各种测量仪器以数字方式表示颜色或提供物理颜色匹配。色度学应用于化学以及颜色印刷、纺织品制造、油漆制造和食品行业等行业。”
那么,我们如何以数字形式显示颜色呢?答案是:颜色模型。
一、颜色模型
A - RGB(红绿蓝)
RGB(Red、Green、Blue)颜色模型是最知名、最常用的模型。它根据三个分量定义了一个颜色空间:
- Red,范围从 0-255。
- Green,范围从 0-255。
- Blue,范围从 0-255。
RGB 颜色模型是加色模型。换句话说,Red、Green 和 Blue 值(称为三原色)组合起来以重现其他颜色。
例如,“红色”可以表示为 [R=255, G=0, B=0],而“紫色”则可以表示为 [R=238, G=130, B=238] 等。
它的常见图形表示如下:
在 .NET 中,`Color` 结构体使用此模型通过 R、G 和 B 属性提供颜色支持。
Console.WriteLine(String.Format("R={0}, G={1}, B={2}",
Color.Red.R, Color.Red.G, Color.Red.B);
Console.WriteLine(String.Format("R={0}, G={1}, B={2}",
Color.Cyan.R, Color.Cyan.G, Color.Cyan.B);
Console.WriteLine(String.Format("R={0}, G={1}, B={2}",
Color.White.R, Color.White.G, Color.White.B);
Console.WriteLine(String.Format("R={0}, G={1}, B={2}",
Color.SteelBlue.R, Color.SteelBlue.G, Color.SteelBlue.B);
// etc...
但这并不是它唯一的用途。因此,我们可以定义一个专用的 RGB 结构体用于进一步的编码,如下所示:
/// <summary>
/// RGB structure.
/// </summary>
public struct RGB
{
/// <summary>
/// Gets an empty RGB structure;
/// </summary>
public static readonly RGB Empty = new RGB();
private int red;
private int green;
private int blue;
public static bool operator ==(RGB item1, RGB item2)
{
return (
item1.Red == item2.Red
&& item1.Green == item2.Green
&& item1.Blue == item2.Blue
);
}
public static bool operator !=(RGB item1, RGB item2)
{
return (
item1.Red != item2.Red
|| item1.Green != item2.Green
|| item1.Blue != item2.Blue
);
}
/// <summary>
/// Gets or sets red value.
/// </summary>
public int Red
{
get
{
return red;
}
set
{
red = (value>255)? 255 : ((value<0)?0 : value);
}
}
/// <summary>
/// Gets or sets red value.
/// </summary>
public int Green
{
get
{
return green;
}
set
{
green = (value>255)? 255 : ((value<0)?0 : value);
}
}
/// <summary>
/// Gets or sets red value.
/// </summary>
public int Blue
{
get
{
return blue;
}
set
{
blue = (value>255)? 255 : ((value<0)?0 : value);
}
}
public RGB(int R, int G, int B)
{
this.red = (R>255)? 255 : ((R<0)?0 : R);
this.green = (G>255)? 255 : ((G<0)?0 : G);
this.blue = (B>255)? 255 : ((B<0)?0 : B);
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (RGB)obj);
}
public override int GetHashCode()
{
return Red.GetHashCode() ^ Green.GetHashCode() ^ Blue.GetHashCode();
}
}
B - HSB 颜色空间
HSB(Hue、Saturation、Brightness)颜色模型根据三个组成部分定义了一个颜色空间:
- Hue(色相):颜色的类型(如红色、蓝色或黄色)。
- 在大多数应用程序中,范围从 0 到 360°。(每个值对应一种颜色:0 是红色,45 是某种橙色,55 是某种黄色)。
- Saturation(饱和度):颜色的强度。
- 范围从 0 到 100%(0 表示没有颜色,即黑色和白色之间的灰色;100 表示浓烈的颜色)。
- 有时也称为“纯度”,与色度学量的激发纯度类似。
- Brightness(亮度)(或 Value(值)):颜色的亮度。
- 范围从 0 到 100%(0 始终是黑色;根据饱和度,100 可能是白色或深浅不一的颜色)。
它的常见图形表示如下:
HSB 模型也称为 HSV(Hue、Saturation、Value)模型。HSV 模型由 Alvy Ray Smith 于 1978 年创建。它是 RGB 颜色空间的一种非线性变换。换句话说,颜色不是定义为原色的简单组合(加法/减法),而是通过数学变换定义的。
注意: HSV 和 HSB 是相同的,但 HSL 不同。
说了这么多,一个 HSB 结构体可以是:
/// <summary>
/// Structure to define HSB.
/// </summary>
public struct HSB
{
/// <summary>
/// Gets an empty HSB structure;
/// </summary>
public static readonly HSB Empty = new HSB();
private double hue;
private double saturation;
private double brightness;
public static bool operator ==(HSB item1, HSB item2)
{
return (
item1.Hue == item2.Hue
&& item1.Saturation == item2.Saturation
&& item1.Brightness == item2.Brightness
);
}
public static bool operator !=(HSB item1, HSB item2)
{
return (
item1.Hue != item2.Hue
|| item1.Saturation != item2.Saturation
|| item1.Brightness != item2.Brightness
);
}
/// <summary>
/// Gets or sets the hue component.
/// </summary>
public double Hue
{
get
{
return hue;
}
set
{
hue = (value>360)? 360 : ((value<0)?0:value);
}
}
/// <summary>
/// Gets or sets saturation component.
/// </summary>
public double Saturation
{
get
{
return saturation;
}
set
{
saturation = (value>1)? 1 : ((value<0)?0:value);
}
}
/// <summary>
/// Gets or sets the brightness component.
/// </summary>
public double Brightness
{
get
{
return brightness;
}
set
{
brightness = (value>1)? 1 : ((value<0)? 0 : value);
}
}
/// <summary>
/// Creates an instance of a HSB structure.
/// </summary>
/// <param name="h">Hue value.</param>
/// <param name="s">Saturation value.</param>
/// <param name="b">Brightness value.</param>
public HSB(double h, double s, double b)
{
hue = (h>360)? 360 : ((h<0)?0:h);
saturation = (s>1)? 1 : ((s<0)?0:s);
brightness = (b>1)? 1 : ((b<0)?0:b);
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (HSB)obj);
}
public override int GetHashCode()
{
return Hue.GetHashCode() ^ Saturation.GetHashCode() ^
Brightness.GetHashCode();
}
}
C - HSL 颜色空间
HSL 颜色空间,也称为 HLS 或 HSI,代表:
- Hue(色相):颜色的类型(如红色、蓝色或黄色)。
- 在大多数应用程序中,范围从 0 到 360°(每个值对应一种颜色:0 是红色,45 是某种橙色,55 是某种黄色)。
- Saturation(饱和度):颜色随明度的变化。
- 范围从 0 到 100%(从黑白轴的中心开始)。
- Lightness(亮度)(也称为 Luminance(发光度)或 Luminosity(亮度)或 Intensity(强度))。
- 范围从 0 到 100%(从黑色到白色)。
它的常见图形表示如下:
HSL 与 HSB 类似。主要区别在于 HSL 相对于亮度和暗度是对称的。这意味着:
- 在 HSL 中,饱和度分量始终从完全饱和的颜色变为等效的灰色(在 HSB 中,当 B 达到最大值时,它从饱和颜色变为白色)。
- 在 HSL 中,亮度始终跨越从黑色到所选色相再到白色的整个范围(在 HSB 中,B 分量只跨越一半,从黑色到所选色相)。
对我而言,HSL 提供了比 HSB 更准确(即使不是绝对)的颜色近似。
说了这么多,一个 HSL 结构体可以是:
/// <summary>
/// Structure to define HSL.
/// </summary>
public struct HSL
{
/// <summary>
/// Gets an empty HSL structure;
/// </summary>
public static readonly HSL Empty = new HSL();
private double hue;
private double saturation;
private double luminance;
public static bool operator ==(HSL item1, HSL item2)
{
return (
item1.Hue == item2.Hue
&& item1.Saturation == item2.Saturation
&& item1.Luminance == item2.Luminance
);
}
public static bool operator !=(HSL item1, HSL item2)
{
return (
item1.Hue != item2.Hue
|| item1.Saturation != item2.Saturation
|| item1.Luminance != item2.Luminance
);
}
/// <summary>
/// Gets or sets the hue component.
/// </summary>
public double Hue
{
get
{
return hue;
}
set
{
hue = (value>360)? 360 : ((value<0)?0:value);
}
}
/// <summary>
/// Gets or sets saturation component.
/// </summary>
public double Saturation
{
get
{
return saturation;
}
set
{
saturation = (value>1)? 1 : ((value<0)?0:value);
}
}
/// <summary>
/// Gets or sets the luminance component.
/// </summary>
public double Luminance
{
get
{
return luminance;
}
set
{
luminance = (value>1)? 1 : ((value<0)? 0 : value);
}
}
/// <summary>
/// Creates an instance of a HSL structure.
/// </summary>
/// <param name="h">Hue value.</param>
/// <param name="s">Saturation value.</param>
/// <param name="l">Lightness value.</param>
public HSL(double h, double s, double l)
{
this.hue = (h>360)? 360 : ((h<0)?0:h);
this.saturation = (s>1)? 1 : ((s<0)?0:s);
this.luminance = (l>1)? 1 : ((l<0)?0:l);
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (HSL)obj);
}
public override int GetHashCode()
{
return Hue.GetHashCode() ^ Saturation.GetHashCode() ^
Luminance.GetHashCode();
}
}
D - CMYK 颜色空间
CMYK 颜色空间,也称为 CMJN,代表:
- Cyan(青色)。
- 在大多数应用程序中,范围从 0 到 100%。
- Magenta(品红色)。
- 在大多数应用程序中,范围从 0 到 100%。
- Yellow(黄色)。
- 在大多数应用程序中,范围从 0 到 100%。
- blacK(黑色)。
- 在大多数应用程序中,范围从 0 到 100%。
它是一种用于彩色印刷的减色模型。CMYK 基于一种基于光吸收的光学错觉。
其原理是叠加三个图像;一个用于青色,一个用于品红色,一个用于黄色;它们将重现颜色。
它的常见图形表示如下:
与 RGB 颜色模型一样,CMYK 是原色(青色、品红色、黄色和黑色)的组合。这可能是它们唯一的共同点。
CMYK 在颜色色调方面存在不足,导致其可重现的颜色光谱出现“漏洞”。这就是为什么在有人将颜色从 CMYK 转换为 RGB 时经常存在差异。
为什么要使用这个模型?为什么使用黑色?你可能会问……好吧,这只是出于实际目的。维基百科说:*
- 为了提高打印质量并减少摩尔纹,
- 文本通常以黑色印刷,并包含精细细节(如衬线);因此,用三种油墨复制文本需要为每个三种组件图像进行极其精确的对齐。
- 青色、品红色和黄色颜料的混合通常(或很少)不产生纯黑色。
- 混合所有三种颜色油墨来制作黑色,如果不是使用干式碳粉,可能会使纸张变得相当湿润,这在高速度印刷中是一个问题,因为纸张必须极快地干燥以避免弄脏下一张纸,而像报纸这样的劣质纸张如果变得太湿可能会破裂。
- 使用单位量的黑色油墨而不是三单位量的过程油墨可以带来显著的成本节约(黑色油墨通常更便宜)。*
回到我们的现实。一个 CMYK 结构体可以是:
/// <summary>
/// Structure to define CMYK.
/// </summary>
public struct CMYK
{
/// <summary>
/// Gets an empty CMYK structure;
/// </summary>
public readonly static CMYK Empty = new CMYK();
private double c;
private double m;
private double y;
private double k;
public static bool operator ==(CMYK item1, CMYK item2)
{
return (
item1.Cyan == item2.Cyan
&& item1.Magenta == item2.Magenta
&& item1.Yellow == item2.Yellow
&& item1.Black == item2.Black
);
}
public static bool operator !=(CMYK item1, CMYK item2)
{
return (
item1.Cyan != item2.Cyan
|| item1.Magenta != item2.Magenta
|| item1.Yellow != item2.Yellow
|| item1.Black != item2.Black
);
}
public double Cyan
{
get
{
return c;
}
set
{
c = value;
c = (c>1)? 1 : ((c<0)? 0 : c);
}
}
public double Magenta
{
get
{
return m;
}
set
{
m = value;
m = (m>1)? 1 : ((m<0)? 0 : m);
}
}
public double Yellow
{
get
{
return y;
}
set
{
y = value;
y = (y>1)? 1 : ((y<0)? 0 : y);
}
}
public double Black
{
get
{
return k;
}
set
{
k = value;
k = (k>1)? 1 : ((k<0)? 0 : k);
}
}
/// <summary>
/// Creates an instance of a CMYK structure.
/// </summary>
public CMYK(double c, double m, double y, double k)
{
this.c = c;
this.m = m;
this.y = y;
this.k = k;
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (CMYK)obj);
}
public override int GetHashCode()
{
return Cyan.GetHashCode() ^
Magenta.GetHashCode() ^ Yellow.GetHashCode() ^ Black.GetHashCode();
}
}
E - YUV 颜色空间
YUV 模型根据一个亮度(luma)和两个色度(chrominance)分量定义一个颜色空间。YUV 颜色模型用于 PAL、NTSC 和 SECAM 复合彩色视频标准。
YUV 颜色空间比计算机图形硬件中使用的标准 RGB 模型更接近人类对颜色的感知。
YUV 颜色空间代表:
- Y,亮度分量,或亮度。
- 在大多数应用程序中,范围从 0 到 100%。
- U 和 V 是色度分量(蓝度-亮度和红度-亮度差异分量)。
- 表示为取决于你想要使用的 YUV 版本的因子。
图形表示如下:
一个 YUV 结构体可以是:
/// <summary>
/// Structure to define YUV.
/// </summary>
public struct YUV
{
/// <summary>
/// Gets an empty YUV structure.
/// </summary>
public static readonly YUV Empty = new YUV();
private double y;
private double u;
private double v;
public static bool operator ==(YUV item1, YUV item2)
{
return (
item1.Y == item2.Y
&& item1.U == item2.U
&& item1.V == item2.V
);
}
public static bool operator !=(YUV item1, YUV item2)
{
return (
item1.Y != item2.Y
|| item1.U != item2.U
|| item1.V != item2.V
);
}
public double Y
{
get
{
return y;
}
set
{
y = value;
y = (y>1)? 1 : ((y<0)? 0 : y);
}
}
public double U
{
get
{
return u;
}
set
{
u = value;
u = (u>0.436)? 0.436 : ((u<-0.436)? -0.436 : u);
}
}
public double V
{
get
{
return v;
}
set
{
v = value;
v = (v>0.615)? 0.615 : ((v<-0.615)? -0.615 : v);
}
}
/// <summary>
/// Creates an instance of a YUV structure.
/// </summary>
public YUV(double y, double u, double v)
{
this.y = (y>1)? 1 : ((y<0)? 0 : y);
this.u = (u>0.436)? 0.436 : ((u<-0.436)? -0.436 : u);
this.v = (v>0.615)? 0.615 : ((v<-0.615)? -0.615 : v);
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (YUV)obj);
}
public override int GetHashCode()
{
return Y.GetHashCode() ^ U.GetHashCode() ^ V.GetHashCode();
}
}
F - CIE XYZ 颜色空间
与之前的模型相反,CIE XYZ 模型定义了一个绝对颜色空间。它也称为 CIE 1931 XYZ 颜色空间,代表:
- X,可以与红色进行比较
- 范围从 0 到 0.9505。
- Y,可以与绿色进行比较
- 范围从 0 到 1.0。
- Z,可以与蓝色进行比较
- 范围从 0 到 1.089。
在尝试解释为什么我在本文中包含这个颜色空间之前,你应该知道,它是国际照明委员会 (CIE) 于 1931 年创建的第一个标准之一。它基于人眼的直接测量,并作为许多其他颜色空间定义的基准。
图形表示如下:
一个 CIE XYZ 结构体可以是:
/// <summary>
/// Structure to define CIE XYZ.
/// </summary>
public struct CIEXYZ
{
/// <summary>
/// Gets an empty CIEXYZ structure.
/// </summary>
public static readonly CIEXYZ Empty = new CIEXYZ();
/// <summary>
/// Gets the CIE D65 (white) structure.
/// </summary>
public static readonly CIEXYZ D65 = new CIEXYZ(0.9505, 1.0, 1.0890);
private double x;
private double y;
private double z;
public static bool operator ==(CIEXYZ item1, CIEXYZ item2)
{
return (
item1.X == item2.X
&& item1.Y == item2.Y
&& item1.Z == item2.Z
);
}
public static bool operator !=(CIEXYZ item1, CIEXYZ item2)
{
return (
item1.X != item2.X
|| item1.Y != item2.Y
|| item1.Z != item2.Z
);
}
/// <summary>
/// Gets or sets X component.
/// </summary>
public double X
{
get
{
return this.x;
}
set
{
this.x = (value>0.9505)? 0.9505 : ((value<0)? 0 : value);
}
}
/// <summary>
/// Gets or sets Y component.
/// </summary>
public double Y
{
get
{
return this.y;
}
set
{
this.y = (value>1.0)? 1.0 : ((value<0)?0 : value);
}
}
/// <summary>
/// Gets or sets Z component.
/// </summary>
public double Z
{
get
{
return this.z;
}
set
{
this.z = (value>1.089)? 1.089 : ((value<0)? 0 : value);
}
}
public CIEXYZ(double x, double y, double z)
{
this.x = (x>0.9505)? 0.9505 : ((x<0)? 0 : x);
this.y = (y>1.0)? 1.0 : ((y<0)? 0 : y);
this.z = (z>1.089)? 1.089 : ((z<0)? 0 : z);
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (CIEXYZ)obj);
}
public override int GetHashCode()
{
return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
}
}
好吧!我为什么要包含这个模型?
我快速研究了一下,想在本文中包含 Cie L*a*b* 颜色模型,我发现需要先转换到绝对颜色空间,然后再转换为 L*a*b*。转换原理中使用的模型是 Cie XYZ。所以,我包含了它,现在大家都能理解文章中更进一步使用的“那些 XYZ 值”是什么了。
G - CIE L*a*b* 颜色空间
“Lab 颜色空间是一个颜色对抗空间,其维度 L 代表亮度,a 和 b 代表颜色对抗维度,基于非线性压缩的 CIE XYZ 颜色空间坐标。”
正如在前一个定义中所述,CIE L*a*b* 颜色空间,也称为 CIE 1976 颜色空间,代表:
- L*,亮度。
- a*,红/绿颜色对抗维度。
- b*,黄/蓝颜色对抗维度。
L*a*b* 颜色模型被创建为一种与设备无关的模型,用作参考。它直接基于 CIE 1931 XYZ 颜色空间,旨在线性化颜色差异的可感知性。
L*、a* 和 b* 的非线性关系旨在模仿人眼的对数响应,颜色信息相对于系统白点的颜色。
一个 CIE L*a*b* 结构体可以是:
/// <summary>
/// Structure to define CIE L*a*b*.
/// </summary>
public struct CIELab
{
/// <summary>
/// Gets an empty CIELab structure.
/// </summary>
public static readonly CIELab Empty = new CIELab();
private double l;
private double a;
private double b;
public static bool operator ==(CIELab item1, CIELab item2)
{
return (
item1.L == item2.L
&& item1.A == item2.A
&& item1.B == item2.B
);
}
public static bool operator !=(CIELab item1, CIELab item2)
{
return (
item1.L != item2.L
|| item1.A != item2.A
|| item1.B != item2.B
);
}
/// <summary>
/// Gets or sets L component.
/// </summary>
public double L
{
get
{
return this.l;
}
set
{
this.l = value;
}
}
/// <summary>
/// Gets or sets a component.
/// </summary>
public double A
{
get
{
return this.a;
}
set
{
this.a = value;
}
}
/// <summary>
/// Gets or sets a component.
/// </summary>
public double B
{
get
{
return this.b;
}
set
{
this.b = value;
}
}
public CIELab(double l, double a, double b)
{
this.l = l;
this.a = a;
this.b = b;
}
public override bool Equals(Object obj)
{
if(obj==null || GetType()!=obj.GetType()) return false;
return (this == (CIELab)obj);
}
public override int GetHashCode()
{
return L.GetHashCode() ^ a.GetHashCode() ^ b.GetHashCode();
}
}
还有很多其他格式,如 RYB 和 CcMmYK。我仍然不打算创建一个“颜色框架”,但如果你有其他想法……
二、模型之间的转换
A - RGB 转换
将 RGB 颜色转换为任何其他模型是转换算法的基础。它涉及到对红、绿、蓝进行归一化:值范围从 `[0..255]` 变为 `[0..1]`。
a - RGB 到 HSB
转换原理如下:
H ? [0, 360]
S, V, R, G, B ? [0, 1]
V = MAX
好吧!很有趣!但是 C# 的等效代码是什么?在这里。
/// <summary>
/// Converts RGB to HSB.
/// </summary>
public static HSB RGBtoHSB(int red, int green, int blue)
{
// normalize red, green and blue values
double r = ((double)red/255.0);
double g = ((double)green/255.0);
double b = ((double)blue/255.0);
// conversion start
double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));
double h = 0.0;
if(max==r && g>=b)
{
h = 60 * (g-b)/(max-min);
}
else if(max==r && g < b)
{
h = 60 * (g-b)/(max-min) + 360;
}
else if(max == g)
{
h = 60 * (b-r)/(max-min) + 120;
}
else if(max == b)
{
h = 60 * (r-g)/(max-min) + 240;
}
double s = (max == 0)? 0.0 : (1.0 - (min/max));
return new HSB(h, s, (double)max);
}
b - RGB 到 HSL
转换原理如下:
H ? [0, 360]
S, L, R, G, B ? [0, 1]
L = ½(MAX + MIN)
C# 的等效代码是:
/// <summary>
/// Converts RGB to HSL.
/// </summary>
/// <param name="red">Red value, must be in [0,255].</param>
/// <param name="green">Green value, must be in [0,255].</param>
/// <param name="blue">Blue value, must be in [0,255].</param>
public static HSL RGBtoHSL(int red, int green, int blue)
{
double h=0, s=0, l=0;
// normalize red, green, blue values
double r = (double)red/255.0;
double g = (double)green/255.0;
double b = (double)blue/255.0;
double max = Math.Max(r, Math.Max(g, b));
double min = Math.Min(r, Math.Min(g, b));
// hue
if(max == min)
{
h = 0; // undefined
}
else if(max==r && g>=b)
{
h = 60.0*(g-b)/(max-min);
}
else if(max==r && g<b)
{
h = 60.0*(g-b)/(max-min) + 360.0;
}
else if(max==g)
{
h = 60.0*(b-r)/(max-min) + 120.0;
}
else if(max==b)
{
h = 60.0*(r-g)/(max-min) + 240.0;
}
// luminance
l = (max+min)/2.0;
// saturation
if(l == 0 || max == min)
{
s = 0;
}
else if(0<l && l<=0.5)
{
s = (max-min)/(max+min);
}
else if(l>0.5)
{
s = (max-min)/(2 - (max+min)); //(max-min > 0)?
}
return new HSL(
Double.Parse(String.Format("{0:0.##}", h)),
Double.Parse(String.Format("{0:0.##}", s)),
Double.Parse(String.Format("{0:0.##}", l))
);
}
注意:你可能已经注意到 String.Format("{0:0.##}", h)
……这是 .NET 保持相同舍入行为的解决方案。如果你不明白我的意思,可以尝试下面的示例代码:
Console.WriteLine(Math.Round(4.45, 1)); // returns 4.4.
Console.WriteLine(Math.Round(4.55, 1)); // returns 4.6.
你没注意到问题吗?好吧,4.45 应该返回 4.5 而不是 4.4。解决方案是使用 String.Format()
,它始终应用“就近偶数舍入”方法。
c - RGB 到 CMYK
转换原理如下:
R, G, B ? [0, 1]
tC'M'Y' = {1 - R, 1 - G, 1 - B}
K = min{C', M', Y'}
tCMYK = {0, 0, 0, 1} if K = 1
tCMYK = { (C' - K)/(1 - K), (M' - K)/(1 - K), (Y' - K)/(1 - K), K } otherwise
C# 的等效代码是:
/// <summary>
/// Converts RGB to CMYK.
/// </summary>
/// <param name="red">Red vaue must be in [0, 255]. </param>
/// <param name="green">Green vaue must be in [0, 255].</param>
/// <param name="blue">Blue vaue must be in [0, 255].</param>
public static CMYK RGBtoCMYK(int red, int green, int blue)
{
// normalizes red, green, blue values
double c = (double)(255 - red)/255;
double m = (double)(255 - green)/255;
double y = (double)(255 - blue)/255;
double k = (double)Math.Min(c, Math.Min(m, y));
if(k == 1.0)
{
return new CMYK(0,0,0,1);
}
else
{
return new CMYK((c-k)/(1-k), (m-k)/(1-k), (y-k)/(1-k), k);
}
}
d - RGB 到 YUV (YUV444)
转换原理如下:
R, G, B, Y ? [0, 1]
U ? [-0.436, 0.436]
V ? [-0.615, 0.615]
tYUV = { (0.299 R + 0.587 G + 0.114 B), (- 0.14713 R + 0.28886 G + 0.436 B), (0.615 R + 0.51499 G + 0.10001 B) }
C# 的等效代码是:
/// <summary>
/// Converts RGB to YUV.
/// </summary>
/// <param name="red">Red must be in [0, 255].</param>
/// <param name="green">Green must be in [0, 255].</param>
/// <param name="blue">Blue must be in [0, 255].</param>
public static YUV RGBtoYUV(int red, int green, int blue)
{
YUV yuv = new YUV();
// normalizes red, green, blue values
double r = (double)red/255.0;
double g = (double)green/255.0;
double b = (double)blue/255.0;
yuv.Y = 0.299*r + 0.587*g + 0.114*b;
yuv.U = -0.14713*r -0.28886*g + 0.436*b;
yuv.V = 0.615*r -0.51499*g -0.10001*b;
return yuv;
}
e - RGB 到网页颜色
哈哈!这是我可以解释的。
正如你可能已经知道的,网页颜色可以用两种方式定义:例如,“red”可以定义为 rgb(255,0,0)
或 #FF0000
。
第二种形式的解释很简单:
- “
#
”字符表示格式是十六进制的。 - 最后 6 个字符定义了 3 对:一对用于“Red”,一对用于“Green”,一对用于“Blue”。
- 每对都是一个十六进制值(基数为 16),其值范围从 0 到 255。
因此,你可以将每个颜色分量除以 16,并将大于 9 的数字替换为它们对应的十六进制值(例如,10 = A,11 = B,依此类推)……但最好的方法是利用 String.Format()
的功能。
/// <summary>
/// Converts a RGB color format to an hexadecimal color.
/// </summary>
/// <param name="r">Red value.</param>
/// <param name="g">Green value.</param>
/// <param name="b">Blue value.</param>
public static string RGBToHex(int r, int g, int b)
{
return String.Format("#{0:x2}{1:x2}{2:x2}", r, g, b).ToUpper();
}
f - RGB 到 XYZ
转换原理如下:
R, G, B ? [0, 1]
a = 0.055 且 ? ˜ 2.2

其中

C# 的等效代码是:
/// <summary>
/// Converts RGB to CIE XYZ (CIE 1931 color space)
/// </summary>
public static CIEXYZ RGBtoXYZ(int red, int green, int blue)
{
// normalize red, green, blue values
double rLinear = (double)red/255.0;
double gLinear = (double)green/255.0;
double bLinear = (double)blue/255.0;
// convert to a sRGB form
double r = (rLinear > 0.04045)? Math.Pow((rLinear + 0.055)/(
1 + 0.055), 2.2) : (rLinear/12.92) ;
double g = (gLinear > 0.04045)? Math.Pow((gLinear + 0.055)/(
1 + 0.055), 2.2) : (gLinear/12.92) ;
double b = (bLinear > 0.04045)? Math.Pow((bLinear + 0.055)/(
1 + 0.055), 2.2) : (bLinear/12.92) ;
// converts
return new CIEXYZ(
(r*0.4124 + g*0.3576 + b*0.1805),
(r*0.2126 + g*0.7152 + b*0.0722),
(r*0.0193 + g*0.1192 + b*0.9505)
);
}
g - RGB 到 L*a*b*
正如我之前所说,转换为 CIE L*a*b 颜色模型有点棘手:我们需要先转换为 CIE XYZ,然后再尝试获取 L*a*b* 值。
/// <summary>
/// Converts RGB to CIELab.
/// </summary>
public static CIELab RGBtoLab(int red, int green, int blue)
{
return XYZtoLab( RGBtoXYZ(red, green, blue) );
}
XYZ 和 L*a*b* 之间的转换如下所示。
B - HSB 转换
a - HSB 到 RGB
转换原理如下:
H ? [0, 360]
S, V, R, G, B ? [0, 1]
Hi = [H / 60] mod 6
f = (H / 60) - Hi
p = V (1 - S)
q = V (1 - f S)
t = V (1 - (1 - f ) S)
if Hi = 0 ? R = V, G = t, B = p
if Hi = 1 ? R = q, G = V, B = p
if Hi = 2 ? R = p, G = V, B = t
if Hi = 3 ? R = p, G = q, B = V
if Hi = 4 ? R = t, G = p, B = V
if Hi = 5 ? R = V, G = p, B = q
C# 的等效代码?在这里。
/// <summary>
/// Converts HSB to RGB.
/// </summary>
public static RGB HSBtoRGB(double h, double s, double b)
{
double r = 0;
double g = 0;
double b = 0;
if(s == 0)
{
r = g = b = b;
}
else
{
// the color wheel consists of 6 sectors. Figure out which sector
// you're in.
double sectorPos = h / 60.0;
int sectorNumber = (int)(Math.Floor(sectorPos));
// get the fractional part of the sector
double fractionalSector = sectorPos - sectorNumber;
// calculate values for the three axes of the color.
double p = b * (1.0 - s);
double q = b * (1.0 - (s * fractionalSector));
double t = b * (1.0 - (s * (1 - fractionalSector)));
// assign the fractional colors to r, g, and b based on the sector
// the angle is in.
switch(sectorNumber)
{
case 0:
r = b;
g = t;
b = p;
break;
case 1:
r = q;
g = b;
b = p;
break;
case 2:
r = p;
g = b;
b = t;
break;
case 3:
r = p;
g = q;
b = b;
break;
case 4:
r = t;
g = p;
b = b;
break;
case 5:
r = b;
g = p;
b = q;
break;
}
}
return new RGB(
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", r*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", g*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", b*255.0)) )
);
}
b - HSB 到 HSL
转换原理非常简单(但不准确):先转换为 RGB,然后再转换为 HSL。
/// <summary>
/// Converts HSB to HSL.
/// </summary>
public static HSL HSBtoHSL(double h, double s, double b)
{
RGB rgb = HSBtoRGB(h, s, b);
return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
}
c - HSB 到 CMYK
没什么新内容:转换原理是先转换为 RGB,然后再转换为 CMYK。
/// <summary>
/// Converts HSB to CMYK.
/// </summary>
public static CMYK HSBtoCMYK(double h, double s, double b)
{
RGB rgb = HSBtoRGB(h, s, b);
return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
}
d - HSB 到 YUV
没什么新内容:转换原理是先转换为 RGB,然后再转换为 YUV。
/// <summary>
/// Converts HSB to CMYK.
/// </summary>
public static YUV HSBtoYUV(double h, double s, double b)
{
RGB rgb = HSBtoRGB(h, s, b);
return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
}
C - HSL 转换
a - HSL 到 RGB
转换原理如下:
H ? [0, 360]
S, L, R, G, B ? [0, 1]
if L < 0.5 ? Q = L × (1 + S)
if L = 0.5 ? Q = L + S – (L × S)
P = 2 × L – Q
Hk = H / 360
Tr = Hk + 1/3
Tg = Hk
Tb = Hk – 1/3
对于每个 c = R,G,B
if Tc < 0 ? Tc = Tc + 1.0
if Tc > 1 ? Tc = Tc – 1.0
if Tc < 1/6 ? Tc = P + ((Q – P) × 6.0 × Tc)
if 1/6 = Tc > 1/2 ? Tc = Q
if 1/2 = Tc > 2/3 ? Tc = P + ((Q – P) × (2/3 – Tc) × 6.0)
else Tc = P
C# 的等效代码?在这里。
/// <summary>
/// Converts HSL to RGB.
/// </summary>
/// <param name="h">Hue, must be in [0, 360].</param>
/// <param name="s">Saturation, must be in [0, 1].</param>
/// <param name="l">Luminance, must be in [0, 1].</param>
public static RGB HSLtoRGB(double h, double s, double l)
{
if(s == 0)
{
// achromatic color (gray scale)
return new RGB(
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
l*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
l*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
l*255.0)) )
);
}
else
{
double q = (l<0.5)?(l * (1.0+s)):(l+s - (l*s));
double p = (2.0 * l) - q;
double Hk = h/360.0;
double[] T = new double[3];
T[0] = Hk + (1.0/3.0); // Tr
T[1] = Hk; // Tb
T[2] = Hk - (1.0/3.0); // Tg
for(int i=0; i<3; i++)
{
if(T[i] < 0) T[i] += 1.0;
if(T[i] > 1) T[i] -= 1.0;
if((T[i]*6) < 1)
{
T[i] = p + ((q-p)*6.0*T[i]);
}
else if((T[i]*2.0) < 1) //(1.0/6.0)<=T[i] && T[i]<0.5
{
T[i] = q;
}
else if((T[i]*3.0) < 2) // 0.5<=T[i] && T[i]<(2.0/3.0)
{
T[i] = p + (q-p) * ((2.0/3.0) - T[i]) * 6.0;
}
else T[i] = p;
}
return new RGB(
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
T[0]*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
T[1]*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
T[2]*255.0)) )
);
}
}
b - HSL 到 HSB
没什么新内容:转换原理是先转换为 RGB,然后再转换为 HSB。
/// <summary>
/// Converts HSL to HSB.
/// </summary>
public static HSB HSLtoHSB(double h, double s, double l)
{
RGB rgb = HSLtoRGB(h, s, l);
return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
}
c - HSL 到 CMYK
没什么新内容:转换原理是先转换为 RGB,然后再转换为 CMYK。
/// <summary>
/// Converts HSL to CMYK.
/// </summary>
public static CMYK HSLtoCMYK(double h, double s, double l)
{
RGB rgb = HSLtoRGB(h, s, l);
return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
}
d - HSL 到 YUV
没什么新内容:转换原理是先转换为 RGB,然后再转换为 CMYK。
/// <summary>
/// Converts HSL to YUV.
/// </summary>
public static YUV HSLtoYUV(double h, double s, double l)
{
RGB rgb = HSLtoRGB(h, s, l);
return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
}
D - CMYK 转换
a - CMYK 到 RGB
转换原理如下:
tRGB = { (1 - C) × (1 - K) , (1 - M) × (1 - K), (1 - Y) × (1 - K)}
C# 的等效代码?在这里。
/// <summary>
/// Converts CMYK to RGB.
/// </summary>
public static Color CMYKtoRGB(double c, double m, double y, double k)
{
int red = Convert.ToInt32((1-c) * (1-k) * 255.0);
int green = Convert.ToInt32((1-m) * (1-k) * 255.0);
int blue = Convert.ToInt32((1-y) * (1-k) * 255.0);
return Color.FromArgb(red, green, blue);
}
b - CMYK 到 HSL
没什么新内容:转换原理是先转换为 RGB,然后再转换为 HSL。
/// <summary>
/// Converts CMYK to HSL.
/// </summary>
public static HSL CMYKtoHSL(double c, double m, double y, double k)
{
RGB rgb = CMYKtoRGB(c, m, y, k);
return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
}
c - CMYK 到 HSB
没什么新内容:转换原理是先转换为 RGB,然后再转换为 HSB。
/// <summary>
/// Converts CMYK to HSB.
/// </summary>
public static HSB CMYKtoHSB(double c, double m, double y, double k)
{
RGB rgb = CMYKtoRGB(c, m, y, k);
return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
}
d - CMYK 到 YUV
没什么新内容:转换原理是先转换为 RGB,然后再转换为 YUV。
/// <summary>
/// Converts CMYK to YUV.
/// </summary>
public static YUV CMYKtoYUV(double c, double m, double y, double k)
{
RGB rgb = CMYKtoRGB(c, m, y, k);
return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
}
E - YUV 转换
a - YUV 到 RGB
转换原理如下:
R, G, B, Y ? [0, 1]
U ? [-0.436, 0.436]
V ? [-0.615, 0.615]
tRGB = { (Y + 1.13983 V), (Y - 0.39466 U - 0.58060 V), (Y + 2.03211 U) }
C# 的等效代码是:
/// <summary>
/// Converts YUV to RGB.
/// </summary>
/// <param name="y">Y must be in [0, 1].</param>
/// <param name="u">U must be in [-0.436, +0.436].</param>
/// <param name="v">V must be in [-0.615, +0.615].</param>
public static RGB YUVtoRGB(double y, double u, double v)
{
RGB rgb = new RGB();
rgb.Red = Convert.ToInt32((y + 1.139837398373983740*v)*255);
rgb.Green = Convert.ToInt32((
y - 0.3946517043589703515*u - 0.5805986066674976801*v)*255);
rgb.Blue = Convert.ToInt32((y + 2.032110091743119266*u)*255);
return rgb;
}
b - YUV 到 HSL
没什么新内容:转换原理是先转换为 RGB,然后再转换为 HSL。
/// <summary>
/// Converts YUV to HSL.
/// </summary>
/// <param name="y">Y must be in [0, 1].</param>
/// <param name="u">U must be in [-0.436, +0.436].</param>
/// <param name="v">V must be in [-0.615, +0.615].</param>
public static HSL YUVtoHSL(double y, double u, double v)
{
RGB rgb = YUVtoRGB(y, u, v);
return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
}
c - YUV 到 HSB
没什么新内容:转换原理是先转换为 RGB,然后再转换为 HSB。
/// <summary>
/// Converts YUV to HSB.
/// </summary>
/// <param name="y">Y must be in [0, 1].</param>
/// <param name="u">U must be in [-0.436, +0.436].</param>
/// <param name="v">V must be in [-0.615, +0.615].</param>
public static HSB YUVtoHSB(double y, double u, double v)
{
RGB rgb = YUVtoRGB(y, u, v);
return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
}
d - YUV 到 CMYK
没什么新内容:转换原理是先转换为 RGB,然后再转换为 CMYK。
/// <summary>
/// Converts YUV to CMYK.
/// </summary>
/// <param name="y">Y must be in [0, 1].</param>
/// <param name="u">U must be in [-0.436, +0.436].</param>
/// <param name="v">V must be in [-0.615, +0.615].</param>
public static CMYK YUVtoCMYK(double y, double u, double v)
{
RGB rgb = YUVtoRGB(y, u, v);
return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
}
F - XYZ 转换
a - XYZ 到 RGB
转换原理如下:
a = 0.055

然后

C# 的等效代码是:
/// <summary>
/// Converts CIEXYZ to RGB structure.
/// </summary>
public static RGB XYZtoRGB(double x, double y, double z)
{
double[] Clinear = new double[3];
Clinear[0] = x*3.2410 - y*1.5374 - z*0.4986; // red
Clinear[1] = -x*0.9692 + y*1.8760 - z*0.0416; // green
Clinear[2] = x*0.0556 - y*0.2040 + z*1.0570; // blue
for(int i=0; i<3; i++)
{
Clinear[i] = (Clinear[i]<=0.0031308)? 12.92*Clinear[i] : (
1+0.055)* Math.Pow(Clinear[i], (1.0/2.4)) - 0.055;
}
return new RGB(
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
Clinear[0]*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
Clinear[1]*255.0)) ),
Convert.ToInt32( Double.Parse(String.Format("{0:0.00}",
Clinear[2]*255.0)) )
);
}
b - XYZ 到 L*a*b*
转换原理如下:


Xn, Yn 和 Zn 是参考白点的 CIE XYZ 三刺激值。
C# 的等效代码是:
/// <summary>
/// XYZ to L*a*b* transformation function.
/// </summary>
private static double Fxyz(double t)
{
return ((t > 0.008856)? Math.Pow(t, (1.0/3.0)) : (7.787*t + 16.0/116.0));
}
/// <summary>
/// Converts CIEXYZ to CIELab.
/// </summary>
public static CIELab XYZtoLab(double x, double y, double z)
{
CIELab lab = CIELab.Empty;
lab.L = 116.0 * Fxyz( y/CIEXYZ.D65.Y ) -16;
lab.A = 500.0 * (Fxyz( x/CIEXYZ.D65.X ) - Fxyz( y/CIEXYZ.D65.Y) );
lab.B = 200.0 * (Fxyz( y/CIEXYZ.D65.Y ) - Fxyz( z/CIEXYZ.D65.Z) );
return lab;
}
G - L*a*b* 转换
a - L*a*b* 到 XYZ
转换原理如下:
d = 6/29



if



if



if



C# 的等效代码是:
/// <summary>
/// Converts CIELab to CIEXYZ.
/// </summary>
public static CIEXYZ LabtoXYZ(double l, double a, double b)
{
double delta = 6.0/29.0;
double fy = (l+16)/116.0;
double fx = fy + (a/500.0);
double fz = fy - (b/200.0);
return new CIEXYZ(
(fx > delta)? CIEXYZ.D65.X * (fx*fx*fx) : (fx - 16.0/116.0)*3*(
delta*delta)*CIEXYZ.D65.X,
(fy > delta)? CIEXYZ.D65.Y * (fy*fy*fy) : (fy - 16.0/116.0)*3*(
delta*delta)*CIEXYZ.D65.Y,
(fz > delta)? CIEXYZ.D65.Z * (fz*fz*fz) : (fz - 16.0/116.0)*3*(
delta*delta)*CIEXYZ.D65.Z
);
}
b - L*a*b* 到 RGB
没什么特别新的,原理是先转换为 XYZ,然后再转换为 RGB。
/// <summary>
/// Converts CIELab to RGB.
/// </summary>
public static RGB LabtoRGB(double l, double a, double b)
{
return XYZtoRGB( LabtoXYZ(l, a, b) );
}
使用代码
好了,展示了转换算法后,也许我没有什么可以告诉你的了。
事实上,ColorSpaceHelper
中还有许多其他有用的方法。你会发现:
- 平均颜色实现(
ColorSpaceHelper.GetColorDistance()
)。 - 色轮颜色生成(
ColorSpaceHelper.GetWheelColors()
),支持 32 位(alpha)。 - 光谱颜色生成(
ColorSpaceHelper.GetSpectrumColors()
),支持 32 位(alpha)。 - 与网页颜色的相互转换(
ColorSpaceHelper.HexToColor()
)。 - 与
System.Drawing.Color
和其他结构体之间的相互转换。
关注点
正如我之前所说,格式有很多种。本文提供了一个关于如何使用常见颜色模型(RGB、HSL、HSB 等)的示例,也许能帮助你完成自己的项目。
以下是我们能够做到的(以及我们将在下一篇文章中看到的)ColorSpaceHelper
的预览:
历史
- 2007 年 6 月 8 日
- 添加了 GraGra_33 VB .NET 版本。
- 2007 年 6 月 7 日
- 添加了 CIE XYZ 颜色空间定义,以及
ColorSpaceHelper
中的转换方法。 - 添加了 CIE L*a*b* 颜色空间定义,以及
ColorSpaceHelper
中的转换方法。
- 添加了 CIE XYZ 颜色空间定义,以及
- 2007 年 6 月 3 日 - 初始文章。