如何将RGB颜色转换为HSV?

问题描述 投票:0回答:8

如何使用 C# 将 RGB 颜色转换为 HSV?
我正在寻找一种无需使用任何外部库的快速方法。

c# colors rgb hsv
8个回答
120
投票

请注意,

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);
}

25
投票

您是否考虑过简单地使用 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返回的转换,但后者相当易于添加


7
投票

EasyRGB 有许多色彩空间转换。 这是 RGB->HSV 转换的代码


6
投票

这里有一个 C 实现:

http://www.cs.rit.edu/~ncs/color/t_convert.html

转换为 C# 应该非常简单,因为几乎没有调用任何函数 - 只是计算。

通过Google

找到

3
投票

这是从 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

1
投票

我因为有同样的需求而来到这里。

我正在分享迄今为止我能找到的最好、最简单的解决方案。
这是 Greg 的修改答案(在这里找到);但具有更简单且易于理解的代码。

对于那些正在学习的人,我添加了一些参考资料,为了理解,值得检查。


参考文献

  1. “Lukas Stratmann”HSV 模型工具(包括其他模型系统:CMY / CMYK / HSL)
  2. 《平面设计中的HSV色彩模型》
  3. “确定 RGB 颜色感知亮度的公式”
  4. 从 RGB 获取色调的最快公式
  5. “颜色转换算法”
  6. “将 RGB 颜色模型更改为 HSV 颜色模型的程序”
  7. “RGB 到 HSV 颜色转换算法”
  8. “RGB 到 HSV 色彩空间转换 (C)”
  9. “如何将 RGB 颜色转换为 HSV”

代码

    /// <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;
    }

0
投票

我有这个复杂的类来转换颜色,它扩展了.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));
    }
}

-1
投票

首先:确保你有一个颜色作为位图,如下所示:

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 值了:)

希望这有帮助

© www.soinside.com 2019 - 2024. All rights reserved.