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 HSBColor
Hue As Double
Saturation As Double
Brightness As Double
End Type
Function RGBToHSB(rgb As RGBColor) As HSBColor
Dim minRGB, maxRGB, Delta As Double
Dim h, s, b As Double
h = 0
minRGB = Min(Min(rgb.Red, rgb.Green), rgb.Blue)
maxRGB = Max(Max(rgb.Red, rgb.Green), rgb.Blue)
Delta = (maxRGB - minRGB)
b = maxRGB
If (maxRGB <> 0) Then
s = 255 * Delta / maxRGB
Else
s = 0
End If
If (s <> 0) Then
If rgb.Red = maxRGB Then
h = (CDbl(rgb.Green) - CDbl(rgb.Blue)) / Delta
Else
If rgb.Green = maxRGB Then
h = 2 + (CDbl(rgb.Blue) - CDbl(rgb.Red)) / Delta
Else
If rgb.Blue = maxRGB Then
h = 4 + (CDbl(rgb.Red) - CDbl(rgb.Green)) / Delta
End If
End If
End If
Else
h = -1
End If
h = h * 60
If h < 0 Then h = h + 360
RGBToHSB.Hue = h
RGBToHSB.Saturation = s * 100 / 255
RGBToHSB.Brightness = b * 100 / 255
End Function
Function HSBToRGB(hsb As HSBColor) As RGBColor
Dim maxRGB, Delta As Double
Dim h, s, b As Double
h = hsb.Hue / 60
s = hsb.Saturation * 255 / 100
b = hsb.Brightness * 255 / 100
maxRGB = b
If s = 0 Then
HSBToRGB.Red = 0
HSBToRGB.Green = 0
HSBToRGB.Blue = 0
Else
Delta = s * maxRGB / 255
If h > 3 Then
HSBToRGB.Blue = CByte(Round(maxRGB))
If h > 4 Then
HSBToRGB.Green = CByte(Round(maxRGB - Delta))
HSBToRGB.Red = CByte(Round((h - 4) * Delta)) + HSBToRGB.Green
Else
HSBToRGB.Red = CByte(Round(maxRGB - Delta))
HSBToRGB.Green = CByte(HSBToRGB.Red - Round((h - 4) * Delta))
End If
Else
If h > 1 Then
HSBToRGB.Green = CByte(Round(maxRGB))
If h > 2 Then
HSBToRGB.Red = CByte(Round(maxRGB - Delta))
HSBToRGB.Blue = CByte(Round((h - 2) * Delta)) + HSBToRGB.Red
Else
HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
HSBToRGB.Red = CByte(HSBToRGB.Blue - Round((h - 2) * Delta))
End If
Else
If h > -1 Then
HSBToRGB.Red = CByte(Round(maxRGB))
If h > 0 Then
HSBToRGB.Blue = CByte(Round(maxRGB - Delta))
HSBToRGB.Green = CByte(Round(h * Delta)) + HSBToRGB.Blue
Else
HSBToRGB.Green = CByte(Round(maxRGB - Delta))
HSBToRGB.Blue = CByte(HSBToRGB.Green - Round(h * Delta))
End If
End If
End If
End If
End If
End Function
Then there was someone who posted that there was a mistake but didn't elaborate much
But I think it need to manage when h is more than 5, for example for the color R:130 G:65 B:111
If h > 5 Then
HSBToRGB.Red = CByte(Round(maxRGB))
If h > 6 Then
HSBToRGB.Blue= CByte(Round(maxRGB - Delta))
HSBToRGB.Green= CByte(Round((h - 6) * Delta)) HSBToRGB.Blue
Else
HSBToRGB.Green= CByte(Round(maxRGB - Delta))
HSBToRGB.Blue = CByte(HSBToRGB.Green- Round((h - 6) * Delta))
End If
Do I need to add in that piece of code? And I presume it should go into HSB to RGB (in my C# conversion)
...
if (s != 0) {
delta = s * maxRGB / 255;
if (h > 5)
rgb.Red = Convert.ToByte(Math.Round(maxRGB));
if (h > 6)
{
rgb.Green = Convert.ToByte(Math.Round(maxRGB - delta));
rgb.Blue = Convert.ToByte(rgb.Green - Math.Round((h - 6) * delta));
}
if (h > 3)
{
...
also, should it be like above, or
if (h > 6) { }
else if (h > 3) { }
If you're using .net, why reinvent the wheel?
Here's my version on how to do that (in C, sorry, but shouldn't be hard to convert, just replace the
int *
's anddouble *
's without
orref
ints, and don't use pointer syntax)RGB to hsb:
If I find my code in C#, I will edit this answer....
The conversion from RGB to HSB should be rather easy using the
Color
structure:It doesn't support the reverse, though.
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:
So, in the end you can use the built-in .Net functions and just calculate your brightness. Full code would be (excluding your types):
A bit about HSB (same as HSV)
From Darel Rex Finley:
According the the Microsoft Documentation for
Color.GetBrightness
: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#):
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
).What about using Color GetBrightness, GetHue and GetSaturation methods?
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.
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:
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. :-)