如何使用 C# 将 RGB 颜色转换为 HSV?
我正在寻找一种无需使用任何外部库的快速方法。
请注意,
Color.GetSaturation()
和Color.GetBrightness()
返回HSL值,而不是HSV。Color original = Color.FromArgb(50, 120, 200);
// original = {Name=ff3278c8, ARGB=(255, 50, 120, 200)}
double hue;
double saturation;
double value;
ColorToHSV(original, out hue, out saturation, out value);
// hue = 212.0
// saturation = 0.75
// value = 0.78431372549019607
Color copy = ColorFromHSV(hue, saturation, value);
// copy = {Name=ff3278c8, ARGB=(255, 50, 120, 200)}
// Compare that to the HSL values that the .NET framework provides:
original.GetHue(); // 212.0
original.GetSaturation(); // 0.6
original.GetBrightness(); // 0.490196079
以下 C# 代码就是您想要的。它使用 Wikipedia 上描述的算法在 RGB 和 HSV 之间进行转换。
hue
的范围为 0 - 360,saturation
或 value
的范围为 0 - 1。
public static void ColorToHSV(Color color, out double hue, out double saturation, out double value)
{
int max = Math.Max(color.R, Math.Max(color.G, color.B));
int min = Math.Min(color.R, Math.Min(color.G, color.B));
hue = color.GetHue();
saturation = (max == 0) ? 0 : 1d - (1d * min / max);
value = max / 255d;
}
public static Color ColorFromHSV(double hue, double saturation, double value)
{
int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6;
double f = hue / 60 - Math.Floor(hue / 60);
value = value * 255;
int v = Convert.ToInt32(value);
int p = Convert.ToInt32(value * (1 - saturation));
int q = Convert.ToInt32(value * (1 - f * saturation));
int t = Convert.ToInt32(value * (1 - (1 - f) * saturation));
if (hi == 0)
return Color.FromArgb(255, v, t, p);
else if (hi == 1)
return Color.FromArgb(255, q, v, p);
else if (hi == 2)
return Color.FromArgb(255, p, v, t);
else if (hi == 3)
return Color.FromArgb(255, p, q, v);
else if (hi == 4)
return Color.FromArgb(255, t, p, v);
else
return Color.FromArgb(255, v, p, q);
}
您是否考虑过简单地使用 System.Drawing 命名空间?例如:
System.Drawing.Color color = System.Drawing.Color.FromArgb(red, green, blue);
float hue = color.GetHue();
float saturation = color.GetSaturation();
float lightness = color.GetBrightness();
请注意,这并不完全是您所要求的(请参阅HSL和HSV之间的差异,并且颜色类没有从HSL/HSV返回的转换,但后者相当易于添加。
EasyRGB 有许多色彩空间转换。 这是 RGB->HSV 转换的代码。
这里有一个 C 实现:
http://www.cs.rit.edu/~ncs/color/t_convert.html
转换为 C# 应该非常简单,因为几乎没有调用任何函数 - 只是计算。
通过Google
找到这是从 BlaM 帖子中的 C 代码移植而来的 VB.net 版本,对我来说工作得很好。
这里有一个 C 实现:
http://www.cs.rit.edu/~ncs/color/t_convert.html
转换为 C# 应该非常简单,因为几乎没有调用任何函数 - 只是 > 计算。
Public Sub HSVtoRGB(ByRef r As Double, ByRef g As Double, ByRef b As Double, ByVal h As Double, ByVal s As Double, ByVal v As Double)
Dim i As Integer
Dim f, p, q, t As Double
If (s = 0) Then
' achromatic (grey)
r = v
g = v
b = v
Exit Sub
End If
h /= 60 'sector 0 to 5
i = Math.Floor(h)
f = h - i 'factorial part of h
p = v * (1 - s)
q = v * (1 - s * f)
t = v * (1 - s * (1 - f))
Select Case (i)
Case 0
r = v
g = t
b = p
Exit Select
Case 1
r = q
g = v
b = p
Exit Select
Case 2
r = p
g = v
b = t
Exit Select
Case 3
r = p
g = q
b = v
Exit Select
Case 4
r = t
g = p
b = v
Exit Select
Case Else 'case 5:
r = v
g = p
b = q
Exit Select
End Select
End Sub
我因为有同样的需求而来到这里。
我正在分享迄今为止我能找到的最好、最简单的解决方案。
这是 Greg 的修改答案(在这里找到);但具有更简单且易于理解的代码。
对于那些正在学习的人,我添加了一些参考资料,为了理解,值得检查。
参考文献
代码
/// <summary> Convert RGB Color to HSV. </summary>
/// <param name="color"></param>
/// <returns> A double[] Containing HSV Color Values. </returns>
public double[] rgbToHSV(Color color)
{
double[] output = new double[3];
double hue, saturation, value;
int max = Math.Max(color.R, Math.Max(color.G, color.B));
int min = Math.Min(color.R, Math.Min(color.G, color.B));
hue = color.GetHue();
saturation = (max == 0) ? 0 : 1d - (1d * min / max);
value = max / 255d;
output[0] = hue;
output[1] = saturation;
output[2] = value;
return output;
}
我有这个复杂的类来转换颜色,它扩展了.NET System.Drawing.Color。
这是 ToHsv 方法。
internal static class ColorHelper
{
public static System.Drawing.Color ParseCssColor(string cssColor)
{
cssColor = cssColor.Trim().ToLowerInvariant();
if (cssColor.StartsWith("rgb", StringComparison.Ordinal)) //rgb or rgba
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("rgba format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
int parseRgbValue(string value)
{
return value.IndexOf("%") == -1 ?
int.Parse(value, System.Globalization.CultureInfo.InvariantCulture) :
(int)Math.Round(255f * float.Parse(value.Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture) / 100, MidpointRounding.AwayFromZero);
}
int red = parseRgbValue(parts[0]);
int green = parseRgbValue(parts[1]);
int blue = parseRgbValue(parts[2]);
if (parts.Length == 3)
{
return System.Drawing.Color.FromArgb(red, green, blue);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromRgb(red, green, blue, (int)Math.Round(255f * alpha));
}
}
else if (cssColor.StartsWith("hsl", StringComparison.Ordinal)) //hsl or hsla
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("hsvl format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
float hue = float.Parse(parts[0], System.Globalization.CultureInfo.InvariantCulture);
float saturationPercent = float.Parse(parts[1].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
float lightnessPercent = float.Parse(parts[2].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
if (parts.Length == 3)
{
return FromHsl(hue, saturationPercent, lightnessPercent);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromHsl(hue, saturationPercent, lightnessPercent, (int)Math.Round(255f * alpha));
}
}
else if (cssColor.StartsWith("hsv", StringComparison.Ordinal)) //hsv or hsva
{
int left = cssColor.IndexOf('(');
int right = cssColor.IndexOf(')');
if (left < 0 || right < 0)
{
throw new FormatException("hsva format error");
}
string noBrackets = cssColor.Substring(left + 1, right - left - 1);
string[] parts = noBrackets.Split(',');
float hue = float.Parse(parts[0], System.Globalization.CultureInfo.InvariantCulture);
float saturationPercent = float.Parse(parts[1].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
float valuePercent = float.Parse(parts[2].Replace("%", "", StringComparison.Ordinal), System.Globalization.CultureInfo.InvariantCulture);
if (parts.Length == 3)
{
return FromHsv(hue, saturationPercent, valuePercent);
}
if (parts.Length == 4)
{
float alpha = float.Parse(parts[3], System.Globalization.CultureInfo.InvariantCulture);
return FromHsv(hue, saturationPercent, valuePercent, (int)Math.Round(255f * alpha));
}
}
//Fallback to ColorTranslator for hex format (#) or named colors, e.g. "Black", "White" etc.
return System.Drawing.ColorTranslator.FromHtml(cssColor);
}
public static System.Drawing.Color FromRgb(int red, int green, int blue, int alpha = 255)
{
return System.Drawing.Color.FromArgb(alpha, red, green, blue);
}
public static System.Drawing.Color FromRgbPercent(float redPercent, float greenPercent, float bluePercent, int alpha = 255)
{
if (redPercent < 0 || redPercent > 100)
{
throw new ArgumentException($"Value of '{redPercent}' is not valid for '{nameof(redPercent)}'. '{nameof(redPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(redPercent));
}
if (greenPercent < 0 || greenPercent > 100)
{
throw new ArgumentException($"Value of '{greenPercent}' is not valid for '{nameof(greenPercent)}'. '{nameof(greenPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(greenPercent));
}
if (bluePercent < 0 || bluePercent > 100)
{
throw new ArgumentException($"Value of '{bluePercent}' is not valid for '{nameof(bluePercent)}'. '{nameof(bluePercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(bluePercent));
}
int red = (int)Math.Round(255f * redPercent / 100, MidpointRounding.AwayFromZero);
int green = (int)Math.Round(255f * greenPercent / 100, MidpointRounding.AwayFromZero);
int blue = (int)Math.Round(255f * bluePercent / 100, MidpointRounding.AwayFromZero);
return System.Drawing.Color.FromArgb(alpha, red, green, blue);
}
public static System.Drawing.Color FromHsl(float hue, float saturationPercent, float lightnessPercent, int alpha = 255)
{
if (hue < 0 || hue > 360)
{
throw new ArgumentException($"Value of '{hue}' is not valid for '{nameof(hue)}'. '{nameof(hue)}' should be greater than or equal to 0 and less than or equal to 360.", nameof(hue));
}
if (saturationPercent < 0 || saturationPercent > 100)
{
throw new ArgumentException($"Value of '{saturationPercent}' is not valid for '{nameof(saturationPercent)}'. '{nameof(saturationPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(saturationPercent));
}
if (lightnessPercent < 0 || lightnessPercent > 100)
{
throw new ArgumentException($"Value of '{lightnessPercent}' is not valid for '{nameof(lightnessPercent)}'. '{nameof(lightnessPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(lightnessPercent));
}
float saturation = saturationPercent / 100f;
float lightness = lightnessPercent / 100f;
hue /= 60f;
float q = (!((double)lightness <= 0.5)) ? (lightness + saturation - (lightness * saturation)) : (lightness * (saturation + 1f));
float p = (lightness * 2f) - q;
float hueToRgb(float p, float q, float t)
{
if (t < 0f)
{
t += 6f;
}
if (t >= 6f)
{
t -= 6f;
}
if (t < 1f)
{
return ((q - p) * t) + p;
}
if (t < 3f)
{
return q;
}
if (t < 4f)
{
return ((q - p) * (4f - t)) + p;
}
return p;
}
return System.Drawing.Color.FromArgb(alpha,
(int)Math.Round(255f * hueToRgb(p, q, hue + 2f), MidpointRounding.AwayFromZero),
(int)Math.Round(255f * hueToRgb(p, q, hue), MidpointRounding.AwayFromZero),
(int)Math.Round(255f * hueToRgb(p, q, hue - 2f), MidpointRounding.AwayFromZero));
}
public static System.Drawing.Color FromHsv(float hue, float saturationPercent, float valuePercent, int alpha = 255)
{
if (hue < 0 || hue > 360)
{
throw new ArgumentException($"Value of '{hue}' is not valid for '{nameof(hue)}'. '{nameof(hue)}' should be greater than or equal to 0 and less than or equal to 360.", nameof(hue));
}
if (saturationPercent < 0 || saturationPercent > 100)
{
throw new ArgumentException($"Value of '{saturationPercent}' is not valid for '{nameof(saturationPercent)}'. '{nameof(saturationPercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(saturationPercent));
}
if (valuePercent < 0 || valuePercent > 100)
{
throw new ArgumentException($"Value of '{valuePercent}' is not valid for '{nameof(valuePercent)}'. '{nameof(valuePercent)}' should be greater than or equal to 0 and less than or equal to 100.", nameof(valuePercent));
}
float saturation = saturationPercent / 100f;
float value = valuePercent / 100f;
float lightness = value - (value * saturation / 2f);
float hslSaturation = lightness == 0f || lightness == 1f ? 0f : (value - lightness) / Math.Min(lightness, 1f - lightness);
return FromHsl(hue, hslSaturation * 100, lightness * 100, alpha);
}
public static (float hue, float saturationPercent, float lightnessPercent) ToHsl(this System.Drawing.Color color)
{
float red = color.R / 255f;
float green = color.G / 255f;
float blue = color.B / 255f;
float vmax = new[] { red, green, blue }.Max();
float vmin = new[] { red, green, blue }.Min();
float d = vmax - vmin;
float hue = 0f;
if (d != 0)
{
if (vmax == red)
{
hue = (green - blue) / d;
}
if (vmax == green)
{
hue = ((blue - red) / d) + 2f;
}
if (vmax == blue)
{
hue = ((red - green) / d) + 4f;
}
}
hue *= 60f;
if (hue < 0f)
{
hue += 360f;
}
float lightness = (vmin + vmax) / 2f;
float saturation = vmin == vmax ? 0f : lightness > 0.5f ? d / (2f - vmax - vmin) : d / (vmax + vmin);
return (hue: (int)Math.Round(hue, MidpointRounding.AwayFromZero), (int)Math.Round(saturation * 100d, MidpointRounding.AwayFromZero), lightnessPercent: (int)Math.Round(lightness * 100d, MidpointRounding.AwayFromZero));
}
public static (float hue, float saturationPercent, float valuePercent) ToHsv(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float lightnessPercent) = ToHsl(color);
float saturation = saturationPercent / 100f;
float lightness = lightnessPercent / 100f;
float value = lightness + (saturation * Math.Min(lightness, 1f - lightness));
float hsvSaturation = value == 0f ? 0f : 2f - (2f * lightness / value);
return (hue, saturationPercent: (int)Math.Round(hsvSaturation * 100d, MidpointRounding.AwayFromZero), valuePercent: (int)Math.Round(value * 100d, MidpointRounding.AwayFromZero));
}
public static string ToHexCssString(this System.Drawing.Color color)
{
if (color.A == byte.MaxValue)
{
return string.Format("#{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B).ToLowerInvariant();
}
return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B).ToLowerInvariant();
}
public static string ToRgbCssString(this System.Drawing.Color color)
{
if (color.A == byte.MaxValue)
{
return string.Format("rgb({0}, {1}, {2})", color.R, color.G, color.B);
}
return string.Format("rgba({0}, {1}, {2}, {3})", color.R, color.G, color.B, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static string ToHslCssString(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float lightnessPercent) = ToHsl(color);
if (color.A == byte.MaxValue)
{
return string.Format("hsl({0}, {1}%, {2}%)", hue, saturationPercent, lightnessPercent);
}
return string.Format("hsla({0}, {1}%, {2}%, {3})", hue, saturationPercent, lightnessPercent, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static string ToHsvCssString(this System.Drawing.Color color)
{
(float hue, float saturationPercent, float valuePercent) = ToHsv(color);
if (color.A == byte.MaxValue)
{
return string.Format("hsv({0}, {1}%, {2}%)", hue, saturationPercent, valuePercent);
}
return string.Format("hsva({0}, {1}%, {2}%, {3})", hue, saturationPercent, valuePercent, Math.Round((decimal)color.A / 255, 2, MidpointRounding.AwayFromZero).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
}
首先:确保你有一个颜色作为位图,如下所示:
Bitmap bmp = (Bitmap)pictureBox1.Image.Clone();
paintcolor = bmp.GetPixel(e.X, e.Y);
(e 来自选择我的颜色的事件处理程序!)
不久前遇到这个问题时我做了什么,我首先得到了 rgba(红色、绿色、蓝色和 alpha)值。 接下来我创建了 3 个浮动:浮动色调、浮动饱和度、浮动亮度。然后你只需做:
hue = yourcolor.Gethue;
saturation = yourcolor.GetSaturation;
brightness = yourcolor.GetBrightness;
整体看起来像这样:
Bitmap bmp = (Bitmap)pictureBox1.Image.Clone();
paintcolor = bmp.GetPixel(e.X, e.Y);
float hue;
float saturation;
float brightness;
hue = paintcolor.GetHue();
saturation = paintcolor.GetSaturation();
brightness = paintcolor.GetBrightness();
如果您现在想在标签中显示它们,只需执行以下操作:
yourlabelname.Text = hue.ToString;
yourlabelname.Text = saturation.ToString;
yourlabelname.Text = brightness.ToString;
现在,您已经将 RGB 值转换为 HSV 值了:)
希望这有帮助