To demonstrate the correct conversion from HSB to RGB in case of hue lamps I will show you some code out of a little sample programm of mine. But to understand why i did this and how it works i want to give you a little bit of background information to this topic.
The background:
The hue lamp can display colors out of two colorsystems: HSB/HSV and CIE 1931 xyY, while xyY consists of two values from 0-1 with floating point that display a point on the color systems color palette. This color system is very hard to display in a canvas cause of the proportions of the xy values.
In generall HSB/HSV consists of these components:
- Hue in degrees from 0-359
- Saturation from 0 – 1 with floating point
- Brightness/Value from 0 – 1 with floating point
The hue lamps HSB is a little bit different:
- Hue from 0 – 65535 (Hue in degrees * 182.04)
- Saturation from 0 – 255
- Brightness from 0 – 255
The scenario:
To use for example a WPF colorcanvas to set the color on your hue lamp and receive the color from the hue lamp again to display it in your colorcanvas a little color conversion is needed.
Common colorpickers/canvas work on the RGB/HEX color system. So it is necessary to convert form RGB/HEX to HSB vice versa.
To make it easy to follow i want to make my own classes RGB and HSB like the following (the values can easily be parsed to a inbuilt .NET color classes :
public class RGB { public int R { get; set; } public int G { get; set; } public int B { get; set; } } public class HSB { public int Hue { get; set; } public int Saturation { get; set; } public int Brightness { get; set; } }
To make the code as clean as possible I want to make class methods for each RGB and HSB.
I will add some additional extensions for float and numbers to keep the code as simple as possible.
public static class FloatExtension { /// /// Tests equality with a certain amount of precision. Default to smallest possible double /// ///first value ///second value ///optional, smallest possible double value /// public static bool AlmostEquals(this float a, float b, double precision = float.Epsilon) { return Math.Abs(a - b) <= precision; } } public static class Numbers { public static float Max(params float[] numbers) { return numbers.Max(); } public static float Min(params float[] numbers) { return numbers.Min(); } }
RGB to HSB conversion:
There shall be a class method GETHSB() that will deliver the HSB value for the hue lamp.
public HSB GetHSB() { var hsb = new HSB { Hue = (int)GetHue(), Saturation = (int)GetSaturation(), Brightness = (int)GetBrightness() }; return hsb; }
And this are the methods for conversion itself:
private float GetHue() { if (R == G && G == B) return 0; var r = R / 255f; var g = G / 255f; var b = B / 255f; float hue; var min = Numbers.Min(r, g, b); var max = Numbers.Max(r, g, b); var delta = max - min; if (r.AlmostEquals(max)) hue = (g - b) / delta; // between yellow & magenta else if (g.AlmostEquals(max)) hue = 2 + (b - r) / delta; // between cyan & yellow else hue = 4 + (r - g) / delta; // between magenta & cyan hue *= 60; // degrees if (hue < 0) hue += 360; return hue * 182.04f; } private float GetSaturation() { var r = R / 255f; var g = G / 255f; var b = B / 255f; var min = Numbers.Min(r, g, b); var max = Numbers.Max(r, g, b); if (max.AlmostEquals(min)) return 0; return ((max.AlmostEquals(0f)) ? 0f : 1f - (1f * min / max)) * 255; } private float GetBrightness() { var r = R / 255f; var g = G / 255f; var b = B / 255f; return Numbers.Max(r, g, b) * 255; }
Thats it. The HSB object delivered can be directly used for the hue lamp.
Now comes the more tricky part.
HSB to RGB conversion
There also shall be a class method GetRGB that delivers common RGB values.
public RGB GetRGB() { var hue = (double)Hue; var saturation = (double)Saturation; var brightness = (double)Brightness; //Convert Hue into degrees for HSB hue = hue / 182.04; //Bri and Sat must be values from 0-1 (~percentage) brightness = brightness / 255.0; saturation = saturation / 255.0; double r = 0; double g = 0; double b = 0; if (saturation == 0) { r = g = b = brightness; } else { // the color wheel consists of 6 sectors. double sectorPos = hue / 60.0; int sectorNumber = (int)(Math.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: r = brightness; g = t; b = p; break; case 1: r = q; g = brightness; b = p; break; case 2: r = p; g = brightness; b = t; break; case 3: r = p; g = q; b = brightness; break; case 4: r = t; g = p; b = brightness; break; case 5: r = brightness; g = p; b = q; break; } } //Check if any value is out of byte range if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } return new RGB() { R = (int)(r * 255.0), G = (int)(g * 255.0), B = (int)(b * 255.0) }; }
So long so good. Now you can use this classes like this.
var rgb = new RGB() { R= 255, G = 255, B = 255} var hsb = rgb.GetHSB(); var received rgb = hsb.GetRGB();
To test your code you can use this sample code in a console application to show if the conversion is alright. You can also download the zipped project from my DropBox
List RGBColors = new List(); RGBColors.Add(new RGB() { R= 255, G = 255, B = 255}); RGBColors.Add(new RGB() { R = 0, G = 255, B = 255 }); RGBColors.Add(new RGB() { R = 255, G = 0, B = 255 }); RGBColors.Add(new RGB() { R = 255, G = 255, B = 0 }); RGBColors.Add(new RGB() { R = 0, G = 0, B = 255 }); RGBColors.Add(new RGB() { R = 255, G = 0, B = 0 }); RGBColors.Add(new RGB() { R = 0, G = 255, B = 0 }); RGBColors.Add(new RGB() { R = 88, G = 211, B = 211 }); RGBColors.Add(new RGB() { R = 27, G = 148, B = 9 }); List HSBColors = new List(); foreach (var rgbColor in RGBColors) { var hsbColor = rgbColor.GetHSB(); HSBColors.Add(hsbColor); Console.WriteLine($"HSB: {hsbColor.Hue} {hsbColor.Saturation} {hsbColor.Brightness}"); } foreach (var hsbColor in HSBColors) { var rgbColor = hsbColor.GetRGB(); RGBColors.Add(rgbColor); Console.WriteLine($"RGB: {rgbColor.R} {rgbColor.G} {rgbColor.B}" ); }
Comments are closed.