Algorithm to Switch Between RGB and HSB Color Values

后端 未结 6 651
没有蜡笔的小新
没有蜡笔的小新 2020-12-06 08:18

I read the article Algorithm to Switch Between RGB and HSB Color Values

Type RGBColor
     Red As Byte
     Green As Byte
     Blue As Byte
End Type

Type HS         


        
相关标签:
6条回答
  • 2020-12-06 08:24

    Here's my version on how to do that (in C, sorry, but shouldn't be hard to convert, just replace the int *'s and double *'s with out or ref ints, and don't use pointer syntax)

    void colorlib_hsbtorgb(double hue, double saturation, double brightness, int *red, int *green, int *blue)
    {   
        if (saturation == 0)
        {
            *red = *green = *blue = brightness;
        }
        else
        {
            // the color wheel consists of 6 sectors. Figure out which sector you're in.
            double sectorPos = hue / 60.0;
            int sectorNumber = (int)(floor(sectorPos));
            // get the fractional part of the sector
            double fractionalSector = sectorPos - sectorNumber;
    
            // calculate values for the three axes of the color. 
            double p = brightness * (1.0 - saturation);
            double q = brightness * (1.0 - (saturation * fractionalSector));
            double t = brightness * (1.0 - (saturation * (1 - fractionalSector)));
    
            // assign the fractional colors to r, g, and b based on the sector the angle is in.
            switch (sectorNumber)
            {
                case 0:
                    *red = brightness;
                    *green = t;
                    *blue = p;
                    break;
                case 1:
                    *red = q;
                    *green = brightness;
                    *blue = p;
                    break;
                case 2:
                    *red = p;
                    *green = brightness;
                    *blue = t;
                    break;
                case 3:
                    *red = p;
                    *green = q;
                    *blue = brightness;
                    break;
                case 4:
                    *red = t;
                    *green = p;
                    *blue = brightness;
                    break;
                case 5:
                    *red = brightness;
                    *green = p;
                    *blue = q;
                    break;
            }
        }
    }
    

    RGB to hsb:

    void colorlib_rgbtohsb(int red, int green, int blue, double *hue, double *saturation, double *brightness)
    {
        double dRed = red / 255;
        double dGreen = green / 255;
        double dBlue = blue / 255;
    
        double max = fmax(dRed, fmax(dGreen, dBlue));
        double min = fmin(dRed, fmin(dGreen, dBlue));
    
        double h = 0;
        if (max == dRed && dGreen >= dBlue)
        {
            h = 60 * (dGreen - dBlue) / (max - min);
        }
        else if (max == dRed && dGreen < dBlue)
        {
            h = 60 * (dGreen - dBlue) / (max - min) + 360;
        }
        else if (max == dGreen)
        {
            h = 60 * (dBlue - dRed) / (max - min) + 120;
        }
        else if (max == dBlue)
        {
            h = 60 * (dRed - dGreen) / (max - min) + 240;
        }
    
        double s = (max == 0) ? 0.0 : (1.0 - (min / max));
    
        *hue = h;
        *saturation = s;
        *brightness = max;
    }
    

    If I find my code in C#, I will edit this answer....

    0 讨论(0)
  • 2020-12-06 08:24

    The conversion from RGB to HSB should be rather easy using the Color structure:

    Function RGBToHSB(rgb As RGBColor) As HSBColor
      Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
      RGBToHSB.Hue = c.GetHue()
      RGBToHSB.Saturation = c.GetSaturation()
      RGBToHSB.Brightness = c.GetBrightness()
    End Function
    

    It doesn't support the reverse, though.

    0 讨论(0)
  • 2020-12-06 08:26

    What about using Color GetBrightness, GetHue and GetSaturation methods?

    0 讨论(0)
  • 2020-12-06 08:30

    Using the methods built into .NET's Color object is a non-starter because, as several of the answers point out, they don't support the reverse (converting an HSB color to RGB). Additionally, Color.GetBrightness actually returns lightness, rather than brightness/value. There is a lot of confusion over the differences between the HSB/HSV and HSL color spaces because of their similarities (Wikipedia). I see lots of color pickers that end up using the wrong algorithm and/or model.

    The original code looks to me like it misses a few possible scenarios when it calculates the value for hue, given an RGB color. It's a little difficult for me to follow the additions that you're contemplating to the code, but the first thing that jumps out at me (and that you don't appear to suggest correcting) is that when the saturation = 0, you set hue to -1. When you later multiply the hue by 60, you end up with -60, then you add that to 360 (If h < 0 Then h = h + 360), producing a result of 300, which is not correct.

    I use the following code (in VB.NET) to convert between RGB and HSB (which I call HSV). The results have been tested very extensively, and the results are virtually identical to those given by Photoshop's color picker (aside from the compensation it does for color profiles). The major difference between the posted code and mine (aside from the important portion that calculates the hue) is that I prefer normalizing the RGB values to be between 0 and 1 to do the calculations, rather than working with the original values between 0 and 255. This eliminates some of the inefficiencies and multiple conversions in the original code that you posted, as well.

    Public Function RGBtoHSV(ByVal R As Integer, ByVal G As Integer, ByVal B As Integer) As HSV
         ''# Normalize the RGB values by scaling them to be between 0 and 1
         Dim red As Decimal = R / 255D
         Dim green As Decimal = G / 255D
         Dim blue As Decimal = B / 255D
    
         Dim minValue As Decimal = Math.Min(red, Math.Min(green, blue))
         Dim maxValue As Decimal = Math.Max(red, Math.Max(green, blue))
         Dim delta As Decimal = maxValue - minValue
    
         Dim h As Decimal
         Dim s As Decimal
         Dim v As Decimal = maxValue
    
         ''# Calculate the hue (in degrees of a circle, between 0 and 360)
         Select Case maxValue
            Case red
               If green >= blue Then
                   If delta = 0 Then
                      h = 0
                   Else
                      h = 60 * (green - blue) / delta
                   End If
               ElseIf green < blue Then
                   h = 60 * (green - blue) / delta + 360
               End If
            Case green
               h = 60 * (blue - red) / delta + 120
            Case blue
               h = 60 * (red - green) / delta + 240
         End Select
    
         ''# Calculate the saturation (between 0 and 1)
         If maxValue = 0 Then
            s = 0
         Else
            s = 1D - (minValue / maxValue)
         End If
    
         ''# Scale the saturation and value to a percentage between 0 and 100
         s *= 100
         v *= 100
    
      ''# Return a color in the new color space
      Return New HSV(CInt(Math.Round(h, MidpointRounding.AwayFromZero)), _
                     CInt(Math.Round(s, MidpointRounding.AwayFromZero)), _
                     CInt(Math.Round(v, MidpointRounding.AwayFromZero)))
    End Function
    

    You didn't post the code you use to convert from an HSB (which I call HSV) color to RGB, but here's what I use, again working with interim values that are between 0 and 1:

    Public Function HSVtoRGB(ByVal H As Integer, ByVal S As Integer, ByVal V As Integer) As RGB
         ''# Scale the Saturation and Value components to be between 0 and 1
         Dim hue As Decimal = H
         Dim sat As Decimal = S / 100D
         Dim val As Decimal = V / 100D
    
         Dim r As Decimal
         Dim g As Decimal
         Dim b As Decimal
    
         If sat = 0 Then
           ''# If the saturation is 0, then all colors are the same.
           ''# (This is some flavor of gray.)
            r = val
            g = val
            b = val
         Else
            ''# Calculate the appropriate sector of a 6-part color wheel
            Dim sectorPos As Decimal = hue / 60D
            Dim sectorNumber As Integer = CInt(Math.Floor(sectorPos))
    
            ''# Get the fractional part of the sector
            ''# (that is, how many degrees into the sector you are)
            Dim fractionalSector As Decimal = sectorPos - sectorNumber
    
            ''# Calculate values for the three axes of the color
            Dim p As Decimal = val * (1 - sat)
            Dim q As Decimal = val * (1 - (sat * fractionalSector))
            Dim t As Decimal = val * (1 - (sat * (1 - fractionalSector)))
    
            ''# Assign the fractional colors to red, green, and blue
            ''# components based on the sector the angle is in
            Select Case sectorNumber
               Case 0, 6
                  r = val
                  g = t
                  b = p
               Case 1
                  r = q
                  g = val
                  b = p
               Case 2
                  r = p
                  g = val
                  b = t
               Case 3
                  r = p
                  g = q
                  b = val
               Case 4
                  r = t
                  g = p
                  b = val
               Case 5
                  r = val
                  g = p
                  b = q
            End Select
         End If
    
         ''# Scale the red, green, and blue values to be between 0 and 255
         r *= 255
         g *= 255
         b *= 255
    
         ''# Return a color in the new color space
         Return New RGB(CInt(Math.Round(r, MidpointRounding.AwayFromZero)), _
                        CInt(Math.Round(g, MidpointRounding.AwayFromZero)), _
                        CInt(Math.Round(b, MidpointRounding.AwayFromZero)))
    End Function
    

    EDIT: This code looks very similar to that provided in C by Richard J. Ross III. I hunted down as many different algorithms as I could find online, rewrote a lot of code borrowing the best from each of them, and did extensive testing to verify the accuracy of the results. I neglected to note who I borrowed code from, as this was just for a private library. Maybe the VB version will help someone who doesn't want to do a conversion from C. :-)

    0 讨论(0)
  • 2020-12-06 08:34

    Solution

    You can calculate the Brightness component quite simply as it's the max of R, G, and B (reference: formula for RGB to HSV from the Rochester Institute of Technology). You can scale it however you like by dividing by 255 and multiplying by the scale. This is the same as done in your existing code:

    maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
    b = maxRGB    
    ...    
    RGBToHSB.Brightness = b * 100 / 255
    

    So, in the end you can use the built-in .Net functions and just calculate your brightness. Full code would be (excluding your types):

    Function RGBToHSB(rgb As RGBColor) As HSBColor
      Dim maxRGB As Double
      maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
    
      Dim c As Color = Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue)
      RGBToHSB.Hue = c.GetHue()
      RGBToHSB.Saturation = c.GetSaturation() * 100
      RGBToHSB.Brightness = maxRGB * 100 / 255
    End Function
    

    A bit about HSB (same as HSV)

    From Darel Rex Finley:

    In the HSV (also called HSB) system, the brightness of a color is its V component. That component is defined simply as the maximum value of any of the three RGB components of the color — the other two RGB components are ignored when determining V.

    According the the Microsoft Documentation for Color.GetBrightness:

    Gets the hue-saturation-brightness (HSB) brightness value for this Color structure.

    I have found some references saying the MSDN uses HSB when it means HSL like this one from MSDN blogs (see the comments). A quick test proves this to be true (in C#):

    // Define a color which gives different HSL and HSB value
    Color c = Color.FromArgb(255, 0, 0);
    // Get the brightness, scale it from 0.0 - 1.0 up to 0 - 255
    int bright = (int)(c.GetBrightness() * 255.00);
    // Output it
    Console.WriteLine(bright.ToString());
    

    This results in a value of 127, which is clearly HSL. If it was HSB the value should be the max of R G and B (i.e. 255).

    0 讨论(0)
  • 2020-12-06 08:36

    If you're using .net, why reinvent the wheel?

    Dim c = Color.FromArgb(myRed, myGreen, myBlue)
    Dim h = c.GetHue()
    Dim s = c.GetSaturation()
    Dim b = c.GetBrightness()
    
    0 讨论(0)
提交回复
热议问题