My recommendation would be the same as ChrisA. answer, with one difference:
Use HS P color space , as it is an approximation of the Photoshop algorithm and has better results.
For the sake of not only links to the HSP site (which, frankly, should be more than enough, I just donβt like to answer without examples), here is my C# implementation that follows the site:
#region Definitions //Perceived brightness to Red ratio. private const double Pr = .299; //Perceived brightness to Green ratio. private const double Pg = .587; //Perceived brightness to Blue ratio. private const double Pb = .114; #endregion //Expected ranges: Hue = 0-359... Other values = 0-1 public static ColorRGB ToRGB(double hue, double saturation, double perceivedBrightness, double alpha) { //Check values within expected range hue = hue < 0 ? 0 : hue > 359 ? 359 : hue; saturation = saturation < 0 ? 0 : saturation > 1 ? 1 : saturation; perceivedBrightness = perceivedBrightness < 0 ? 0 : perceivedBrightness > 1 ? 1 : perceivedBrightness; alpha = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha; //Conversion var minOverMax = 1 - saturation; double r, g, b; if (minOverMax > 0) { double part; if (hue < 0.166666666666667D) { //R>G>B hue = 6 * (hue - 0); part = 1 + hue * (1 / minOverMax - 1); b = perceivedBrightness / Math.Sqrt(Pr / minOverMax / minOverMax + Pg * part * part + Pb); r = b / minOverMax; g = b + hue * (r - b); } else if (hue < 0.333333333333333D) { //G>R>B hue = 6 * (-hue + 0.333333333333333D); part = 1 + hue * (1 / minOverMax - 1); b = perceivedBrightness / Math.Sqrt(Pg / minOverMax / minOverMax + Pr * part * part + Pb); g = b / minOverMax; r = b + hue * (g - b); } else if (hue < 0.5D) { // G>B>R hue = 6 * (hue - 0.333333333333333D); part = 1 + hue * (1 / minOverMax - 1); r = perceivedBrightness / Math.Sqrt(Pg / minOverMax / minOverMax + Pb * part * part + Pr); g = r / minOverMax; b = r + hue * (g - r); } else if (hue < 0.666666666666667D) { //B>G>R hue = 6 * (-hue + 0.666666666666667D); part = 1 + hue * (1 / minOverMax - 1); r = perceivedBrightness / Math.Sqrt(Pb / minOverMax / minOverMax + Pg * part * part + Pr); b = r / minOverMax; g = r + hue * (b - r); } else if (hue < 0.833333333333333D) { //B>R>G hue = 6 * (hue - 0.666666666666667D); part = 1 + hue * (1 / minOverMax - 1); g = perceivedBrightness / Math.Sqrt(Pb / minOverMax / minOverMax + Pr * part * part + Pg); b = g / minOverMax; r = g + hue * (b - g); } else { //R>B>G hue = 6 * (-hue + 1D); part = 1 + hue * (1 / minOverMax - 1); g = perceivedBrightness / Math.Sqrt(Pr / minOverMax / minOverMax + Pb * part * part + Pg); r = g / minOverMax; b = g + hue * (r - g); } } else { if (hue < 0.166666666666667D) { //R>G>B hue = 6 * (hue - 0D); r = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pr + Pg * hue * hue)); g = r * hue; b = 0; } else if (hue < 0.333333333333333D) { //G>R>B hue = 6 * (-hue + 0.333333333333333D); g = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pg + Pr * hue * hue)); r = g * hue; b = 0; } else if (hue < 0.5D) { //G>B>R hue = 6 * (hue - 0.333333333333333D); g = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pg + Pb * hue * hue)); b = g * hue; r = 0; } else if (hue < 0.666666666666667D) { //B>G>R hue = 6 * (-hue + 0.666666666666667D); b = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pb + Pg * hue * hue)); g = b * hue; r = 0; } else if (hue < 0.833333333333333D) { //B>R>G hue = 6 * (hue - 0.666666666666667D); b = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pb + Pr * hue * hue)); r = b * hue; g = 0; } else { //R>B>G hue = 6 * (-hue + 1D); r = Math.Sqrt(perceivedBrightness * perceivedBrightness / (Pr + Pb * hue * hue)); b = r * hue; g = 0; } } return new ColorRGB(r, g, b, alpha); } //Expected ranges: 0-1 on all components public static ColorHSP FromRGB(double red, double green, double blue, double alpha) { //Guarantee RGB values are in the correct ranges red = red < 0 ? 0 : red > 1 ? 1 : red; green = green < 0 ? 0 : green > 1 ? 1 : green; blue = blue < 0 ? 0 : blue > 1 ? 1 : blue; alpha = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha; //Prepare & cache values for conversion var max = MathExtensions.Max(red, green, blue); var min = MathExtensions.Min(red, green, blue); var delta = max - min; double h, s, p = Math.Sqrt(0.299 * red + 0.587 * green + 0.114 * blue); //Conversion if (delta.Equals(0)) h = 0; else if (max.Equals(red)) { h = (green - blue) / delta % 6; } else if (max.Equals(green)) h = (blue - red) / delta + 2; else h = (red - green) / delta + 4; h *= 60; if (h < 0) h += 360; if (p.Equals(0)) s = 0; else s = delta / p; //Result return new ColorHSP(h, s, p, alpha); }
source share