如何将RGB颜色转换为HSV?

72

我该如何在C#中将RGB颜色转换为HSV颜色空间?
我搜索了一种不使用任何外部库的快速方法。


36
不是完全的重复。HSL不等于HSV。 - Adam Rosenfield
如何将HSL文章中的代码适应于HSV解决方案? - Tom Smykowski
8
维基百科:HSV 和 HSL 在数学上都是圆柱形,但可以从概念上将 HSV 视为颜色的倒置锥体,而将 HSL 视为双锥体或球体。虽然 HSL 和 HSV 中的“色相”指的是同一属性,但它们对“饱和度”的定义却有很大不同。 - BlaM
2
@Adam:我希望Paint.NET的开发人员能够解决这个问题! :) - leppie
4
重申一下:HSV 和 HSL 很不相同。HSV 有时被称为 HSB(尤其是在 Photoshop 和 .NET 中)。 - Ian Boyd
7个回答

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#代码,它使用在维基百科上描述的算法在RGB和HSV之间进行转换。范围为0-360的色调(hue),以及0-1的饱和度(saturation)或值(value)。
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);
}

有没有想法如何将饱和度从0-1转换为MS Paint使用的饱和度值(即0-240刻度)? - Jake Drew
好的回答!也许考虑使用float而不是double,以保持与.NET color.GetHue()返回值的一致性? - WSBT
不知道我是否做错了什么,但这对函数对我来说并没有往返。执行 ToHSV(FromHSV(32,1, 1)) 返回 32,但 ToHSV(FromHSV(33,1, 1)) 返回 32.9411773ToHSV(FromHSV(34,1, 1)) 返回 33.8823547ToHSV(FromHSV(35,1, 1)) 返回 35.0588264ToHSV(FromHSV(36,1, 1)) 再次返回 36。看起来倍数为 4 的工作正常,而其他的则会被多个 1/17 的倍数移位。现在这个神奇的数字从哪里来,我也不知道。 - dotNET
1
明白了。HSV变量使用的是double类型,其精度比8位RGB通道所能容纳的要高得多。因此,无法实现100%的往返映射。不过,我想知道这些函数是否可以改进,以便在RGB子集上安全地执行。 - dotNET
@Greg:请原谅我的无知问题(因为我正在学习中,可能错过了一些C#课程),但是...你会如何获取这个值呢? 该问题暗示了检索转换后的值的一种方法。 在我的知识范围内,您的方法请求用户想要检索的参数,并且没有返回任何内容。你能否请教我一下呢? - user12761381
显示剩余3条评论

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之间的差异),而且Color类没有从HSL / HSV的转换方式,但后者相当容易添加


9
正如你所指出的那样,这并没有回答问题,因为这些方法提供的是RGB到HSL的转换,而不是RGB到HSV的转换。 - Greg
3
@greg: 我同意,这将导致可怕的结果,因为我自己也有过类似的经历。 - leppie
14
由于这不是问题的答案。HSV与HSL非常不同。注意: HSV有时被称为HSB(特别是在Photoshop和.NET中)。 - Ian Boyd
2
我不确定lightness、brightness和value之间的区别是什么(一直以为它们是同义词),但是看了System.Drawing.Color的实现后,我发现它并不基于NTSC对红、绿、蓝三色的权重,而是将它们视为相同的,导致结果不够优化。 - Itai Bar-Haim
1
@IanBoyd 尽管您针对 HSV、HSL 和 HSB 的陈述是正确的,但问题在于 System.Drawing 实现的 HSB 实际上是个误称:他们实现的是 HSL。.NET API 表明 Color.GetBrightness() 返回亮度,而不是值(即,它不是 HSV 中的“V”,而是 HSL 中的“L”)。 - Informagic

7

6

4
这个实现不正确。它似乎是基于这个示例代码,但缺少一个部分(归一化部分)。这让我卡了一段时间! - Dan Messing

3
这是我从BlaM的帖子中移植过来的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;
    }

注意:Greg的链接答案还包括ColorFromHSV,它是相反的方向。 - ToolmakerSteve

-1

首先,请确保您有一个位图颜色,就像这样:

Bitmap bmp = (Bitmap)pictureBox1.Image.Clone();
paintcolor = bmp.GetPixel(e.X, e.Y);

(e是从事件处理程序中选择我的颜色!)

当我遇到这个问题时,我首先获取了rgba(红,绿,蓝和alpha)值。接下来我创建了3个浮点数:float hue,float saturation,float brightness。然后你只需要执行:

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

希望这能帮到你


1
这不是HSV而是HSL。https://dev59.com/NGUo5IYBdhLWcg3w7S-T - Sha

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