HSL to RGB color conversion
Asked Answered
D

24

251

I am looking for an algorithm to convert between HSL color to RGB.

It seems to me that HSL is not very widely used so I am not having much luck searching for a converter.

Detritus answered 28/2, 2010 at 23:58 Comment(10)
The article for HSL and HSV on wikipedia contains some formulas. The calculations are a bit tricky, so it might be useful to take a look at existing implementations.Wonted
Just noticed that the less.js project has a number of colour manipulation functions, including hsl_to_rgb: github.com/cloudhead/less.js/blob/master/lib/less/functions.jsCentrosome
In the browser side - d3.js has good API for this: github.com/mbostock/d3/wiki/ColorsTrilbi
checkout please js Please.HEX_to_HSV('#ffeb3b')Maniacal
if you are looking for a tool, rgb.to is good.Tessellation
Marked as off topic despite asking for an algorithm, which is explicitly an 'on-topic' at stackoverflow.com/help/on-topic -- what gives?Impartial
I asked this question ten years ago and I was surprised to find the php and python tags added. Changing back to the original wording :)Detritus
I tried to convert the given solutions into arduino-code. But I really have difficulties reading the algorithms. i would really appriciate when somebody post the arduino functions for hsl to rgb and rgb to hsl.Epstein
@Epstein here is algorithm described using mathErigeron
I voted to reopen this question because it asks for an algorithm, which is on-topic. It does not ask for a recommendation for an external resource as the close box says it does.Spaceship
D
48

Found the easiest way, python to the rescue :D

colorsys.hls_to_rgb(h, l, s)

Convert the color from HLS coordinates to RGB coordinates.

Detritus answered 1/3, 2010 at 0:20 Comment(4)
I cannot believe there is such a standard module in Python! This really rescues me. I've been struggled with the convertion algorithm in Wikipedia's HSL entry for 2 hours. Seems that algorithm can not get right output.Wideman
Ruby equivalent: rubydoc.info/gems/color/1.8/Color/RGB e.g. Color::HSL.new(40,50,60).to_rgbFlurry
I use brython to get a color picker in the browser, this is just what I needed!Lesh
hls_to_rgb vectorizationLamberto
X
385

Garry Tan posted a JavaScript solution on his blog (which he attributes to a now defunct mjijackson.com, but is archived here and the original author has a gist - thanks to user2441511).

The code is re-posted below (updated for post-ES5 conventions):

HSL to RGB:

const { abs, min, max, round } = Math;

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from https://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
function hslToRgb(h, s, l) {
  let r, g, b;

  if (s === 0) {
    r = g = b = l; // achromatic
  } else {
    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    const p = 2 * l - q;
    r = hueToRgb(p, q, h + 1/3);
    g = hueToRgb(p, q, h);
    b = hueToRgb(p, q, h - 1/3);
  }

  return [round(r * 255), round(g * 255), round(b * 255)];
}

function hueToRgb(p, q, t) {
  if (t < 0) t += 1;
  if (t > 1) t -= 1;
  if (t < 1/6) return p + (q - p) * 6 * t;
  if (t < 1/2) return q;
  if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
  return p;
}

RGB to HSL:

/**
 * Converts an RGB color value to HSL. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes r, g, and b are contained in the set [0, 255] and
 * returns h, s, and l in the set [0, 1].
 *
 * @param   {number}  r       The red color value
 * @param   {number}  g       The green color value
 * @param   {number}  b       The blue color value
 * @return  {Array}           The HSL representation
 */
function rgbToHsl(r, g, b) {
  (r /= 255), (g /= 255), (b /= 255);
  const vmax = max(r, g, b), vmin = min(r, g, b);
  let h, s, l = (vmax + vmin) / 2;

  if (vmax === vmin) {
    return [0, 0, l]; // achromatic
  }

  const d = vmax - vmin;
  s = l > 0.5 ? d / (2 - vmax - vmin) : d / (vmax + vmin);
  if (vmax === r) h = (g - b) / d + (g < b ? 6 : 0);
  if (vmax === g) h = (b - r) / d + 2;
  if (vmax === b) h = (r - g) / d + 4;
  h /= 6;

  return [h, s, l];
}
Xerarch answered 29/2, 2012 at 3:9 Comment(25)
if(t < 0) t += 1; if(t > 1) t -= 1; Is there a good reason that is not an else if?Mercier
I fixed up a few things and made this into a node module: gist.github.com/mnbayazit/6513318Circosta
I'm trying to use this for a project, but my results are only coming out as grayscale. Is this a limitation of HSL<->RGB? The wikipedia article seems to suggest that only a single value is set to all 3 channels.Scopoline
H should be a value 0 - 360. This function is not performing as I was hoping. It looks like its getting the correct SL variables, but H is off.Amphithecium
I love how the author included his assumptions in his comments. Even better would be to format those comments as "assert"s, a la https://mcmap.net/q/21357/-what-is-assert-in-javascript. For example: assert(0<=h && h<1 ,'Hue range problem.'); would have been helpful.Monophthong
Same issue here, I pass 3 values like hslToRgb(2.7, 0.5, 0.4) and red, green as well as blue get returned as 51. Not quite what I expected :/Gauche
Just did some tests with color charts; RGB -> HSL seems to be incorrect for saturation. F.ex. color FFEEFF will give you an incorrect value. The correct formula is: s = 1 - min / max - see nl.wikipedia.org/wiki/… - you'll find the formula somewhere thereDrusi
@Monophthong I disagree. Huge performance overhead in such a time critical function. Asserts are a bad idea in general. That should be covered by the type system. Not having one, comments is the way to go.Deterioration
The hue at the end should be h *= 60 and not h /= 6. If you use the second (with the devision) way of finalizing your hue value you get some crazy angles that don't represent the real deal. I've checked multiple palettes implemented in multiple languages (I'm doing this in C++) and all delivered the same results supporting my statement above.Wyndham
Ah, I see that the hue value is supposed to be in the interval [0, 1]. Why this is done I don't know (it most certainly is NOT radians). If whoever is reading this wants to get the angles in degrees use the "patch" I have mentioned in my previous comment.Wyndham
I'd like to point out that using Math.round introduces small inaccuracies at the low and high ends (values of 0 and 255) of the scale. Values that arent on the ends of the range can round either up or down to reach their value, but values can only be rounded down to 0, or up to 255. This means that the range of values that map to 0 and 255 are exactly half of those for other values. To fix this, use this formula instead: min(floor(val*256),255). This makes the mapping nearly perfect.Caseworm
Also, if you are getting grayscale values, It's probably because of the lines that use h + 1/3 and h - 1/3. In many languages, this uses integer division, where 1/3 is zero. to get correct results, use float literals instead, ie.: h + 1.0/3.0.Caseworm
The RGB to HSL function does not work. Here is an example of it not working: jsfiddle.net/fs5L02k0/2 and here is the function, fixed jsfiddle.net/t5nq6jjc/1 -- Formula adopted from: nl.wikipedia.org/wiki/…Ratite
The original blog post by MJackson has been archived here and it appears that the developer has a Gist with the code.Salliesallow
@Johannes - Read the description in the code: The returned h, s and l values are all between 0 and 1, so you need to multiply h by 360 in returnUsableColor. Here is your first example, corrected: jsfiddle.net/fs5L02k0/4Crosspatch
The hslToRgb function sometimes fails and returns an incorrect value. For example: hslToRgb(161,0.55,0.63) outputs [109,109,109] when it should be [109,213,180] The code at github.com/kayellpeee/hsl_rgb_converter is working well for me instead.Mb
@Mb - Read the description in the code: The input h, s and l values should between 0 and 1. You need to call hslToRgb(0.447 /*161/360*/, 0.55, 0.63).Crosspatch
I just ported this to C++ and unit-tested it, works very well! However, beware: r = g = b = l; // achromatic. In this expression, l is lowercase L, not one. Lost half an hour with this.Epicureanism
**Bad algorithme ! ** - No Modulo on Hue (360°) - No ranged variable (s,l) [0..1] And the Luminisity is totaly not fonctional ! → Great code HERE : hsl2rgb.nichabi.com/javascript-function.phpNewsmagazine
here is simpler algorithm https://mcmap.net/q/21309/-convert-hsl-to-rgb-and-hexErigeron
This implementation expects a hue value between 0 and 1 (a float). If you are using a value in the range 0 to 360 (as is used in CSS), it can be converted as hue / 360.Lecithinase
re: comment by Marcus Erronius: I found that a better conversion to integer is (int)Math.floor(255*val+.5)Difficile
This hsl2rgb fails for me, at every third, the values are all wrong. Thanks for the suggestion of jsfiddle.net/Lamik/reuk63ay which does work. Just needs h*=360.Dittography
not enough jsdoc clearlyBegay
This does not work for hue greater than one, which is not unusual to happen.Rimarimas
D
48

Found the easiest way, python to the rescue :D

colorsys.hls_to_rgb(h, l, s)

Convert the color from HLS coordinates to RGB coordinates.

Detritus answered 1/3, 2010 at 0:20 Comment(4)
I cannot believe there is such a standard module in Python! This really rescues me. I've been struggled with the convertion algorithm in Wikipedia's HSL entry for 2 hours. Seems that algorithm can not get right output.Wideman
Ruby equivalent: rubydoc.info/gems/color/1.8/Color/RGB e.g. Color::HSL.new(40,50,60).to_rgbFlurry
I use brython to get a color picker in the browser, this is just what I needed!Lesh
hls_to_rgb vectorizationLamberto
A
26

Java implementation of Mohsen's code

Note that all integer are declared as float (i.e 1f) and must be float, else you will optain grey colors.

HSL to RGB

 /**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param h       The hue
 * @param s       The saturation
 * @param l       The lightness
 * @return int array, the RGB representation
 */
public static int[] hslToRgb(float h, float s, float l){
    float r, g, b;

    if (s == 0f) {
        r = g = b = l; // achromatic
    } else {
        float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
        float p = 2 * l - q;
        r = hueToRgb(p, q, h + 1f/3f);
        g = hueToRgb(p, q, h);
        b = hueToRgb(p, q, h - 1f/3f);
    }
    int[] rgb = {to255(r), to255(g), to255(b)};
    return rgb;
}
public static int to255(float v) { return (int)Math.min(255,256*v); }

/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
    if (t < 0f)
        t += 1f;
    if (t > 1f)
        t -= 1f;
    if (t < 1f/6f)
        return p + (q - p) * 6f * t;
    if (t < 1f/2f)
        return q;
    if (t < 2f/3f)
        return p + (q - p) * (2f/3f - t) * 6f;
    return p;
}

RGB to HSL

/**
 * Converts an RGB color value to HSL. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes pR, pG, and bpBare contained in the set [0, 255] and
 * returns h, s, and l in the set [0, 1].
 *
 * @param pR       The red color value
 * @param pG       The green color value
 * @param pB       The blue color value
 * @return float array, the HSL representation
 */
public static float[] rgbToHsl(int pR, int pG, int pB) {
    float r = pR / 255f;
    float g = pG / 255f;
    float b = pB / 255f;

    float max = (r > g && r > b) ? r : (g > b) ? g : b;
    float min = (r < g && r < b) ? r : (g < b) ? g : b;

    float h, s, l;
    l = (max + min) / 2.0f;

    if (max == min) {
        h = s = 0.0f;
    } else {
        float d = max - min;
        s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);

        if (r > g && r > b)
            h = (g - b) / d + (g < b ? 6.0f : 0.0f);

        else if (g > b)
            h = (b - r) / d + 2.0f;

        else
            h = (r - g) / d + 4.0f;

        h /= 6.0f;
    }

    float[] hsl = {h, s, l};
    return hsl;
}
Adolescence answered 28/3, 2015 at 12:9 Comment(0)
H
26

Short but precise - JS

Use this JS code (more: rgb2hsl, hsv2rgb rgb2hsv and hsl2hsv) - php version here

// input: h as an angle in [0,360] and s,l in [0,1] - output: r,g,b in [0,1]
function hsl2rgb(h,s,l) 
{
   let a=s*Math.min(l,1-l);
   let f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1);
   return [f(0),f(8),f(4)];
}   

// oneliner version
let hsl2rgb = (h,s,l, a=s*Math.min(l,1-l), f= (n,k=(n+h/30)%12) => l - a*Math.max(Math.min(k-3,9-k,1),-1)) => [f(0),f(8),f(4)];

// r,g,b are in [0-1], result e.g. #0812fa.
let rgb2hex = (r,g,b) => "#" + [r,g,b].map(x=>Math.round(x*255).toString(16).padStart(2,0) ).join('');


console.log(`hsl: (30,0.2,0.3) --> rgb: (${hsl2rgb(30,0.2,0.3)}) --> hex: ${rgb2hex(...hsl2rgb(30,0.2,0.3))}`);



// ---------------
// UX
// ---------------

rgb= [0,0,0];
hs= [0,0,0];

let $ = x => document.querySelector(x);

function changeRGB(i,e) {
  rgb[i]=e.target.value/255;
  hs = rgb2hsl(...rgb);
  refresh();
}

function changeHS(i,e) {
  hs[i]=e.target.value/(i?255:1);
  rgb= hsl2rgb(...hs);
  refresh();
}

function refresh() {
  rr = rgb.map(x=>x*255|0).join(', ')
  hh = rgb2hex(...rgb);
  tr = `RGB: ${rr}`
  th = `HSL: ${hs.map((x,i)=>i? (x*100).toFixed(2)+'%':x|0).join(', ')}`
  thh= `HEX: ${hh}`
  $('.box').style.backgroundColor=`rgb(${rr})`;  
  $('.infoRGB').innerHTML=`${tr}`;  
  $('.infoHS').innerHTML =`${th}\n${thh}`;  
  
  $('#r').value=rgb[0]*255;
  $('#g').value=rgb[1]*255;
  $('#b').value=rgb[2]*255;
  
  $('#h').value=hs[0];
  $('#s').value=hs[1]*255;
  $('#l').value=hs[2]*255;  
}

function rgb2hsl(r,g,b) {
  let a=Math.max(r,g,b), n=a-Math.min(r,g,b), f=(1-Math.abs(a+a-n-1)); 
  let h= n && ((a==r) ? (g-b)/n : ((a==g) ? 2+(b-r)/n : 4+(r-g)/n)); 
  return [60*(h<0?h+6:h), f ? n/f : 0, (a+a-n)/2];
}

refresh();
.box {
  width: 50px;
  height: 50px;
  margin: 20px;
}

body {
    display: flex;
}
<div>
<input id="r" type="range" min="0" max="255" oninput="changeRGB(0,event)">R<br>
<input id="g" type="range" min="0" max="255" oninput="changeRGB(1,event)">G<br>
<input id="b" type="range" min="0" max="255" oninput="changeRGB(2,event)">B<br>
<pre class="infoRGB"></pre>
</div> 

<div>
<div class="box hsl"></div>

</div>

<div>
<input id="h" type="range" min="0" max="360" oninput="changeHS(0,event)">H<br>
<input id="s" type="range" min="0" max="255" oninput="changeHS(1,event)">S<br>
<input id="l" type="range" min="0" max="255" oninput="changeHS(2,event)">L<br>
<pre class="infoHS"></pre><br>
</div>

Here is formula which I discover and precisely describe in wiki + error analysis,

enter image description here

Hailstorm answered 27/9, 2020 at 16:50 Comment(1)
Excellent answer. I translated it to Python for a small project and it worked beautifully.Cad
M
15

If you're looking for something that definitely conforms with the CSS semantics for HSL and RGB, you could use the algorithm specified in the CSS 3 specification, which reads:

HOW TO RETURN hsl.to.rgb(h, s, l): 
   SELECT: 
      l<=0.5: PUT l*(s+1) IN m2
      ELSE: PUT l+s-l*s IN m2
   PUT l*2-m2 IN m1
   PUT hue.to.rgb(m1, m2, h+1/3) IN r
   PUT hue.to.rgb(m1, m2, h    ) IN g
   PUT hue.to.rgb(m1, m2, h-1/3) IN b
   RETURN (r, g, b)

HOW TO RETURN hue.to.rgb(m1, m2, h): 
   IF h<0: PUT h+1 IN h
   IF h>1: PUT h-1 IN h
   IF h*6<1: RETURN m1+(m2-m1)*h*6
   IF h*2<1: RETURN m2
   IF h*3<2: RETURN m1+(m2-m1)*(2/3-h)*6
   RETURN m1

I believe this is the source for some of the other answers here.

Mercier answered 8/6, 2013 at 17:27 Comment(0)
B
11

C# Code from Mohsen's answer.

Here is the code from Mohsen's answer in C# if anyone else wants it. Note: Color is a custom class and Vector4 is from OpenTK. Both are easy to replace with something else of your choosing.

Hsl To Rgba

/// <summary>
/// Converts an HSL color value to RGB.
/// Input: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
/// Output: Color ( R: [0, 255], G: [0, 255], B: [0, 255], A: [0, 255] )
/// </summary>
/// <param name="hsl">Vector4 defining X = h, Y = s, Z = l, W = a. Ranges [0, 1.0]</param>
/// <returns>RGBA Color. Ranges [0, 255]</returns>
public static Color HslToRgba(Vector4 hsl)
{
    float r, g, b;

    if (hsl.Y == 0.0f)
        r = g = b = hsl.Z;

    else
    {
        var q = hsl.Z < 0.5f ? hsl.Z * (1.0f + hsl.Y) : hsl.Z + hsl.Y - hsl.Z * hsl.Y;
        var p = 2.0f * hsl.Z - q;
        r = HueToRgb(p, q, hsl.X + 1.0f / 3.0f);
        g = HueToRgb(p, q, hsl.X);
        b = HueToRgb(p, q, hsl.X - 1.0f / 3.0f);
    }

    return new Color((int)(r * 255), (int)(g * 255), (int)(b * 255), (int)(hsl.W * 255));
}

// Helper for HslToRgba
private static float HueToRgb(float p, float q, float t)
{
    if (t < 0.0f) t += 1.0f;
    if (t > 1.0f) t -= 1.0f;
    if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
    if (t < 1.0f / 2.0f) return q;
    if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
    return p;
}

Rgba To Hsl

/// <summary>
/// Converts an RGB color value to HSL.
/// Input: Color ( R: [0, 255], G: [0, 255], B: [0, 255], A: [0, 255] )
/// Output: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
/// </summary>
/// <param name="rgba"></param>
/// <returns></returns>
public static Vector4 RgbaToHsl(Color rgba)
{
    float r = rgba.R / 255.0f;
    float g = rgba.G / 255.0f;
    float b = rgba.B / 255.0f;

    float max = (r > g && r > b) ? r : (g > b) ? g : b;
    float min = (r < g && r < b) ? r : (g < b) ? g : b;

    float h, s, l;
    h = s = l = (max + min) / 2.0f;

    if (max == min)
        h = s = 0.0f;

    else
    {
        float d = max - min;
        s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);

        if (r > g && r > b)
            h = (g - b) / d + (g < b ? 6.0f : 0.0f);

        else if (g > b)
            h = (b - r) / d + 2.0f;

        else
            h = (r - g) / d + 4.0f;

        h /= 6.0f;
    }

    return new Vector4(h, s, l, rgba.A / 255.0f);
}
Boggs answered 12/10, 2013 at 20:4 Comment(1)
here is simpler algorithm than on wiki: https://mcmap.net/q/21309/-convert-hsl-to-rgb-and-hexErigeron
S
8

This is how I do it which is easy to remember is to think of RGB as three spokes on a wheel, 120 degrees apart.

H = hue (0-360)
S = saturation (0-1)
L = luminance (0-1)

R1 = SIN( H ) * L 
G1 = SIN( H + 120 ) * L 
B1 = SIN( H + 240 ) * L 

The tricky part is saturation, which is to a scale down to the average of those three.

AVERAGE = (R1 + G1 + B1) / 3 

R2 = ((R1 - AVERAGE) * S) + AVERAGE 
G2 = ((G1 - AVERAGE) * S) + AVERAGE 
B2 = ((B1 - AVERAGE) * S) + AVERAGE 

RED = R2 * 255 
GREEN = G2 * 255 
BLUE = B2 * 255 
Saffian answered 22/4, 2014 at 13:14 Comment(3)
@JoelFan - It is simple but not correct. There is no way for pure rational expressions (with only +, -, *, and /) - as are used by definition for color conversions - to express them with the help of the sine functions with the same independent (input) variables. In spite of this it is nice for understanding the principle (but not for perform conversions).Chlamydate
The problem with this solution is that AVERAGE is allways equal ZERO: (R1 + G1 + B1) = L*[ SIN(H) + SIN(H+120) + SIN(H+240) ] - and now if we use formula sin(a)+sin(b) = 2*sin((a+b)/2)*cos((a-b)/2) for first two sin we get: AVERAGE=L*( sin(h+60) + sin(h+240) ) and again AVERAGE= L*2*sin(h+150)*cos(-180/2) = 0 (since cos(-180/2)=cos(90)=0). So calculation for saturation is wrong and in fact saturation works here as luminance.Erigeron
@JoelFan The second problem with this solution is that we need to add 180 degree to H to have "compatible" version with en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB (test case: red color for H=0) but also it is still problem with number of colors - in above solution the yellow, magenta and cyan colors are ommit and/or mapped in wrong way. There is js fiddle with comparision: jsfiddle.net/Lamik/9s24uc1o/10Erigeron
R
7

#Php Implementation of Chris's C# Code

Also from here, which explains the math of it very well.

This is basically a bunch of functions to convert to and from HSL (Hue Saturation Lightness)

Tested and working on PHP 5.6.15

TL;DR: The full code can be found here on Pastebin.


##Hex to HSL Input: Hex color in format: [#]0f4 or [#]00ff44 (pound sign optional)
Output: HSL in Degrees, Percent, Percent

/**
* Input: hex color
* Output: hsl(in ranges from 0-1)
* 
* Takes the hex, converts it to RGB, and sends
* it to RGBToHsl.  Returns the output.
* 
*/
function hexToHsl($hex) {
    $r = "";
    $g = "";
    $b = "";

    $hex = str_replace('#', '', $hex);
    
    if (strlen($hex) == 3) {
        $r = substr($hex, 0, 1);
        $r = $r . $r;
        $g = substr($hex, 1, 1);
        $g = $g . $g;
        $b = substr($hex, 2, 1);
        $b = $b . $b;
    } elseif (strlen($hex) == 6) {
        $r = substr($hex, 0, 2);
        $g = substr($hex, 2, 2);
        $b = substr($hex, 4, 2);
    } else {
        return false;
    }

    $r = hexdec($r);
    $g = hexdec($g);
    $b = hexdec($b);

    $hsl =  rgbToHsl($r,$g,$b);
    return $hsl;
}

RGB to HSL

Input: RGB in range 0-255 Output: HSL in Degrees, Percent, Percent.

/**
* 
*Credits:
* https://mcmap.net/q/21359/-rgb-to-hsl-and-back-calculation-problems
* http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
*
* Called by hexToHsl by default.
*
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
* Assumes r, g, and b are contained in the range [0 - 255] and
* returns h, s, and l in the format Degrees, Percent, Percent.
*
* @param   Number  r       The red color value
* @param   Number  g       The green color value
* @param   Number  b       The blue color value
* @return  Array           The HSL representation
*/
function rgbToHsl($r, $g, $b){  
    //For the calculation, rgb needs to be in the range from 0 to 1. To convert, divide by 255 (ff). 
    $r /= 255;
    $g /= 255;
    $b /= 255;
    
    $myMax = max($r, $g, $b);
    $myMin = min($r, $g, $b);
    
    $maxAdd = ($myMax + $myMin);
    $maxSub = ($myMax - $myMin);
    
    //luminence is (max + min)/2
    $h = 0;
    $s = 0;
    $l = ($maxAdd / 2.0);
    
    //if all the numbers are equal, there is no saturation (greyscale).
    if($myMin != $myMax){
        if ($l < 0.5) {
            $s = ($maxSub / $maxAdd);
        } else {
            $s = (2.0 - $myMax - $myMin); //note order of opperations - can't use $maxSub here
            $s = ($maxSub / $s);
        }
        
        //find hue
        switch($myMax){
            case $r: 
                $h = ($g - $b);
                $h = ($h / $maxSub);
                break;
            case $g: 
                $h = ($b - $r); 
                $h = ($h / $maxSub);
                $h = ($h + 2.0);
                break;
            case $b: 
                $h = ($r - $g);
                $h = ($h / $maxSub); 
                $h = ($h + 4.0);
                break;
        } 
    }
    
    $hsl = hslToDegPercPerc($h, $s, $l);
    return $hsl;
}

##HSL (0-1 range) to Degrees, Percent, Percent format

For the math calculations, HSL is easier to deal with in the 0-1 range, but for human readability, it's easier in Degrees, Percent, Percent. This function takes HSL in the ranges 0-1, and returns HSL in Degrees, Percent, Percent.

/**
* Input: HSL in ranges 0-1.
* Output: HSL in format Deg, Perc, Perc.
* 
* Note: rgbToHsl calls this function by default.
* 
* Multiplies $h by 60, and $s and $l by 100.
*/
function hslToDegPercPerc($h, $s, $l) {
    //convert h to degrees
    $h *= 60;
    
    if ($h < 0) {
        $h += 360;
    }
    
    //convert s and l to percentage
    $s *= 100;
    $l *= 100;
    
    $hsl['h'] = $h;
    $hsl['s'] = $s;
    $hsl['l'] = $l;
    return $hsl;
}

##HSL (Degrees, Percent, Percent format) to HSL in range 0-1

This function converts HSL in the format Degrees, Percent, Percent, to the ranges 0-1 for easier computing.

/**
* Input: HSL in format Deg, Perc, Perc
* Output: An array containing HSL in ranges 0-1
* 
* Divides $h by 60, and $s and $l by 100.
* 
* hslToRgb calls this by default.
*/
function degPercPercToHsl($h, $s, $l) { 
    //convert h, s, and l back to the 0-1 range
    
    //convert the hue's 360 degrees in a circle to 1
    $h /= 360;
    
    //convert the saturation and lightness to the 0-1 
    //range by multiplying by 100
    $s /= 100;
    $l /= 100;
    
    $hsl['h'] =  $h;
    $hsl['s'] = $s;
    $hsl['l'] = $l;
    
    return $hsl;
}

##HSL to RGB

Input: HSL in the format Degrees, Percent, Percent Output: RGB in the format 255, 255, 255.

/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
* Assumes h, s, and l are in the format Degrees,
* Percent, Percent, and returns r, g, and b in 
* the range [0 - 255].
*
* Called by hslToHex by default.
*
* Calls: 
*   degPercPercToHsl
*   hueToRgb
*
* @param   Number  h       The hue value
* @param   Number  s       The saturation level
* @param   Number  l       The luminence
* @return  Array           The RGB representation
*/
function hslToRgb($h, $s, $l){
    $hsl = degPercPercToHsl($h, $s, $l);
    $h = $hsl['h'];
    $s = $hsl['s'];
    $l = $hsl['l'];

    //If there's no saturation, the color is a greyscale,
    //so all three RGB values can be set to the lightness.
    //(Hue doesn't matter, because it's grey, not color)
    if ($s == 0) {
        $r = $l * 255;
        $g = $l * 255;
        $b = $l * 255;
    }
    else {
        //calculate some temperary variables to make the 
        //calculation eaisier.
        if ($l < 0.5) {
            $temp2 = $l * (1 + $s);
        } else {
            $temp2 = ($l + $s) - ($s * $l);
        }
        $temp1 = 2 * $l - $temp2;
        
        //run the calculated vars through hueToRgb to
        //calculate the RGB value.  Note that for the Red
        //value, we add a third (120 degrees), to adjust 
        //the hue to the correct section of the circle for
        //red.  Simalarly, for blue, we subtract 1/3.
        $r = 255 * hueToRgb($temp1, $temp2, $h + (1 / 3));
        $g = 255 * hueToRgb($temp1, $temp2, $h);
        $b = 255 * hueToRgb($temp1, $temp2, $h - (1 / 3));
    }
        
    $rgb['r'] = $r;
    $rgb['g'] = $g;
    $rgb['b'] = $b;

    return $rgb;
}

###Hue to RGB

This function is called by hslToRgb to convert the hue into the separate RGB values.

/**
* Converts an HSL hue to it's RGB value.  
*
* Input: $temp1 and $temp2 - temperary vars based on 
* whether the lumanence is less than 0.5, and 
* calculated using the saturation and luminence
* values.
*  $hue - the hue (to be converted to an RGB 
* value)  For red, add 1/3 to the hue, green 
* leave it alone, and blue you subtract 1/3 
* from the hue.
*
* Output: One RGB value.
*
* Thanks to Easy RGB for this function (Hue_2_RGB).
* http://www.easyrgb.com/index.php?X=MATH&$h=19#text19
*
*/
function hueToRgb($temp1, $temp2, $hue) {
    if ($hue < 0) { 
        $hue += 1;
    }
    if ($hue > 1) {
        $hue -= 1;
    }
    
    if ((6 * $hue) < 1 ) {
        return ($temp1 + ($temp2 - $temp1) * 6 * $hue);
    } elseif ((2 * $hue) < 1 ) {
        return $temp2;
    } elseif ((3 * $hue) < 2 ) {
        return ($temp1 + ($temp2 - $temp1) * ((2 / 3) - $hue) * 6);
    }
    return $temp1;
}

##HSL to Hex

Input: HSL in format Degrees, Percent, Percent Output: Hex in format 00ff22 (no pound sign).

Converts to RGB, then converts separately to hex.

/**
* Converts HSL to Hex by converting it to 
* RGB, then converting that to hex.
* 
* string hslToHex($h, $s, $l[, $prependPound = true]
* 
* $h is the Degrees value of the Hue
* $s is the Percentage value of the Saturation
* $l is the Percentage value of the Lightness
* $prependPound is a bool, whether you want a pound 
*  sign prepended. (optional - default=true)
*
* Calls: 
*   hslToRgb
*
* Output: Hex in the format: #00ff88 (with 
* pound sign).  Rounded to the nearest whole
* number.
*/
function hslToHex($h, $s, $l, $prependPound = true) {
    //convert hsl to rgb
    $rgb = hslToRgb($h,$s,$l);

    //convert rgb to hex
    $hexR = $rgb['r'];
    $hexG = $rgb['g'];
    $hexB = $rgb['b'];
    
    //round to the nearest whole number
    $hexR = round($hexR);
    $hexG = round($hexG);
    $hexB = round($hexB);
    
    //convert to hex
    $hexR = dechex($hexR);
    $hexG = dechex($hexG);
    $hexB = dechex($hexB);
    
    //check for a non-two string length
    //if it's 1, we can just prepend a
    //0, but if it is anything else non-2,
    //it must return false, as we don't 
    //know what format it is in.
    if (strlen($hexR) != 2) {
        if (strlen($hexR) == 1) {
            //probably in format #0f4, etc.
            $hexR = "0" . $hexR;
        } else {
            //unknown format
            return false;
        }
    }
    if (strlen($hexG) != 2) {
        if (strlen($hexG) == 1) {
            $hexG = "0" . $hexG;
        } else {
            return false;
        }
    }
    if (strlen($hexB) != 2) {
        if (strlen($hexB) == 1) {
            $hexB = "0" . $hexB;
        } else {
            return false;
        }
    }
    
    //if prependPound is set, will prepend a
    //# sign to the beginning of the hex code.
    //(default = true)
    $hex = "";
    if ($prependPound) {
        $hex = "#";
    }
    
    $hex = $hex . $hexR . $hexG . $hexB;
    
    return $hex;
}
Rabbet answered 18/12, 2015 at 21:6 Comment(7)
I made an edit to rgbToHsl, you may want to update your php code. There was/is a bug in the code. In rgbToHsl() s = maxSub / (2 - maxSub) should be s = maxSub / (2 - maxAdd)Maniacal
@Maniacal According to here and here, my code actually is correct. I think you might be confusing the if l < 0.5 with the else. Could you explain your thinking? Thanks for taking the time to give feedback though!Rabbet
sorry, you're right, but there is still an order of operations issue. #8cd08c to HSL using this calc (2 - maxSub) = 1.7333333333333334 when it should be as in the second link example ( 2 - max - min ) = 0.6352941176470588. Using 2 - maxAdd got me consistently closer to photoshops output so I assumed it was correct.Maniacal
Oh, ok. Thanks for pointing that out! I fixed it now. Hope this is helpful!Rabbet
Its really helpful, I've been in the process of converting it to JS github.com/bambii7/glowManiacal
Awesome! Be sure to post an answer here when you're done! For pseudo code for some of the conversions, you can check out my second link in the comment.Rabbet
here is simpler algorithm than on wiki for hsl2rgb: https://mcmap.net/q/21309/-convert-hsl-to-rgb-and-hexErigeron
F
6

Here's a fast, super-simple, branchless version in GLSL:

vec3 hsl2rgb( vec3 c ) {
    vec3 rgb = clamp(abs(mod(c.x*6.0 + vec3(0.0, 4.0, 2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
    return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));
}

Doesn't get much shorter than that ~


Link to the original proof-of-concept: https://www.shadertoy.com/view/XljGzV

(Disclaimer: not my code!)

Farlee answered 15/2, 2017 at 22:41 Comment(0)
A
2

Here is the modified javascript function, it outputs Hue in set 0-360 degrees.

function rgbToHsl(r, g, b) {
      r /= 255, g /= 255, b /= 255;
      var max = Math.max(r, g, b), min = Math.min(r, g, b);
      var h, s, l = (max + min) / 2;

      if(max == min){
          h = s = 0; // achromatic
      } else {
          var d = max - min;
          s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
          switch(max){
              case r: h = (g - b) / d ; break;
              case g: h = 2 + ( (b - r) / d); break;
              case b: h = 4 + ( (r - g) / d); break;
          }
          h*=60;
          if (h < 0) h +=360;
      }
     return([h, s, l]);
  }  
alert(rgbToHsl(125,115,145));
Amphithecium answered 6/5, 2014 at 18:38 Comment(0)
P
2

I got this from Brandon Mathis' HSL Picker source code.

It was originally written in CoffeeScript. I converted it to JavaScript using an online converter, and took out the mechanism to verify the user input was a valid RGB value. This answer worked for my usecase, as the most up-voted answer on this post I found to not produce a valid HSL value.

Note that it returns an hsla value, with a representing opacity/transparency. 0 is completely transparent, and 1 fully opaque.

function rgbToHsl(rgb) {
  var a, add, b, diff, g, h, hue, l, lum, max, min, r, s, sat;
  r = parseFloat(rgb[0]) / 255;
  g = parseFloat(rgb[1]) / 255;
  b = parseFloat(rgb[2]) / 255;
  max = Math.max(r, g, b);
  min = Math.min(r, g, b);
  diff = max - min;
  add = max + min;
  hue = min === max ? 0 : r === max ? ((60 * (g - b) / diff) + 360) % 360 : g === max ? (60 * (b - r) / diff) + 120 : (60 * (r - g) / diff) + 240;
  lum = 0.5 * add;
  sat = lum === 0 ? 0 : lum === 1 ? 1 : lum <= 0.5 ? diff / add : diff / (2 - add);
  h = Math.round(hue);
  s = Math.round(sat * 100);
  l = Math.round(lum * 100);
  a = parseFloat(rgb[3]) || 1;
  return [h, s, l, a];
}
Petersham answered 20/9, 2016 at 17:23 Comment(2)
I used this as a basis for a Python method. Thanks.Mulligan
this is not HSL to RGB as the poster asksJasminjasmina
P
2

An hsl|a color value, set in javascript, will be instantly converted to rgb|a All you need to do then is access the computed style value

document.body.style.color = 'hsla(44, 100%, 50%, 0.8)';

console.log(window.getComputedStyle(document.body).color);

// displays: rgba(255, 187, 0, 0.8)

Technically, I guess, this isn't even any lines of code - it's just done automatically. So, depending on your environment, you might be able to get away with just this. Not that there aren't a lot of very thoughtful responses here. I don't know what your goal is.

Now, what if you want to convert from rbg|a to hsl|a?

Pretypify answered 26/4, 2017 at 2:26 Comment(0)
C
2

HSL to RGB in Typescript

All the options above didn't work on my code in TS.

I tweak one of those and now it works as a charm:

type HslType = { h: number; s: number; l: number }

const hslToRgb = (hsl: HslType): RgbType => {
  let { h, s, l } = hsl

  // IMPORTANT if s and l between 0,1 remove the next two lines:
  s /= 100
  l /= 100

  const k = (n: number) => (n + h / 30) % 12
  const a = s * Math.min(l, 1 - l)
  const f = (n: number) =>
    l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))
  return {
    r: Math.round(255 * f(0)),
    g: Math.round(255 * f(8)),
    b: Math.round(255 * f(4)),
  }
}
Clowers answered 12/3, 2022 at 8:47 Comment(0)
F
1

With H, S,and L in [0,1] range:

ConvertHslToRgb: function (iHsl)
{
    var min, sv, sextant, fract, vsf;

    var v = (iHsl.l <= 0.5) ? (iHsl.l * (1 + iHsl.s)) : (iHsl.l + iHsl.s - iHsl.l * iHsl.s);
    if (v === 0)
        return { Red: 0, Green: 0, Blue: 0 };

    min = 2 * iHsl.l - v;
    sv = (v - min) / v;
    var h = (6 * iHsl.h) % 6;
    sextant = Math.floor(h);
    fract = h - sextant;
    vsf = v * sv * fract;

    switch (sextant)
    {
        case 0: return { r: v, g: min + vsf, b: min };
        case 1: return { r: v - vsf, g: v, b: min };
        case 2: return { r: min, g: v, b: min + vsf };
        case 3: return { r: min, g: v - vsf, b: v };
        case 4: return { r: min + vsf, g: min, b: v };
        case 5: return { r: v, g: min, b: v - vsf };
    }
}
Frailty answered 1/4, 2013 at 19:54 Comment(0)
U
1

For when you need RGB to HSV and vice versa instead:

function rgbToHsv(r, g, b)
{
    r /= 255, g /= 255, b /= 255;

    var min = Math.min(r, g, b),
    max = Math.max(r, g, b),
    delta = max - min,
    h = 0, s = 0, v = max;

    if (min != max)
    {
        s = (delta / max);

        switch (max)
        {
            case r: h = (g - b) / delta + (g < b ? 6 : 0); break;
            case g: h = (b - r) / delta + 2; break;
            case b: h = (r - g) / delta + 4; break;
        }

        h /= 6;
    }

    return [h, s, v];
}

function hsvToRgb(h, s, v)
{
    var step = h / (1 / 6),
    pos = step - Math.floor(step), // the hue position within the current step
    m = (Math.floor(step) % 2) ? (1 - pos) * v : pos * v, // mix color value adjusted to the brightness(v)
    max = 1 * v,
    min = (1 - s) * v,
    med = m + ((1 - s) * (v - m)),
    r, g, b;

    switch (Math.floor(step))
    {
        case 0:
            r = max;
            g = med;
            b = min;
            break;
        case 1:
            r = med;
            g = max;
            b = min;
            break;
        case 2:
            r = min;
            g = max;
            b = med;
            break;
        case 3:
            r = min;
            g = med;
            b = max;
            break;
        case 4:
            r = med;
            g = min;
            b = max;
            break;
        case 5:
            r = max;
            g = min;
            b = med;
            break;
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
Undesigned answered 10/12, 2013 at 23:18 Comment(0)
W
1

Unity3D C# Code from Mohsen's answer.

Here is the code from Mohsen's answer in C# targeted specifically for Unity3D. It was adapted from the C# answer given by Alec Thilenius above.

using UnityEngine;
using System.Collections;

public class ColorTools {

    /// <summary>
    /// Converts an HSL color value to RGB.
    /// Input: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )**strong text**
    /// Output: Color ( R: [0.0, 1.0], G: [0.0, 1.0], B: [0.0, 1.0], A: [0.0, 1.0] )
    /// </summary>
    /// <param name="hsl">Vector4 defining X = h, Y = s, Z = l, W = a. Ranges [0, 1.0]</param>
    /// <returns>RGBA Color. Ranges [0.0, 1.0]</returns>
    public static Color HslToRgba(Vector4 hsl)
    {
        float r, g, b;

        if (hsl.y == 0.0f)
            r = g = b = hsl.z;
        else
        {
            var q = hsl.z < 0.5f ? hsl.z * (1.0f + hsl.y) : hsl.z + hsl.y - hsl.z * hsl.y;
            var p = 2.0f * hsl.z - q;
            r = HueToRgb(p, q, hsl.x + 1.0f / 3.0f);
            g = HueToRgb(p, q, hsl.x);
            b = HueToRgb(p, q, hsl.x - 1.0f / 3.0f);
        }

        return new Color(r, g, b, hsl.w);
    }

    // Helper for HslToRgba
    private static float HueToRgb(float p, float q, float t)
    {
        if (t < 0.0f) t += 1.0f;
        if (t > 1.0f) t -= 1.0f;
        if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
        if (t < 1.0f / 2.0f) return q;
        if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
        return p;
    }

    /// <summary>
    /// Converts an RGB color value to HSL.
    /// Input: Color ( R: [0.0, 1.0], G: [0.0, 1.0], B: [0.0, 1.0], A: [0.0, 1.0] )
    /// Output: Vector4 ( X: [0.0, 1.0], Y: [0.0, 1.0], Z: [0.0, 1.0], W: [0.0, 1.0] )
    /// </summary>
    /// <param name="rgba"></param>
    /// <returns></returns>
    public static Vector4 RgbaToHsl(Color rgba)
    {

        float max = (rgba.r > rgba.g && rgba.r > rgba.b) ? rgba.r : 
            (rgba.g > rgba.b) ? rgba.g : rgba.b;
        float min = (rgba.r < rgba.g && rgba.r < rgba.b) ? rgba.r : 
            (rgba.g < rgba.b) ? rgba.g : rgba.b;

        float h, s, l;
        h = s = l = (max + min) / 2.0f;

        if (max == min)
            h = s = 0.0f;
        else
        {
            float d = max - min;
            s = (l > 0.5f) ? d / (2.0f - max - min) : d / (max + min);

            if (rgba.r > rgba.g && rgba.r > rgba.b)
                h = (rgba.g - rgba.b) / d + (rgba.g < rgba.b ? 6.0f : 0.0f);

            else if (rgba.g > rgba.b)
                h = (rgba.b - rgba.r) / d + 2.0f;

            else
                h = (rgba.r - rgba.g) / d + 4.0f;

            h /= 6.0f;
        }

        return new Vector4(h, s, l, rgba.a);
    }

}
Wonderland answered 13/10, 2015 at 23:2 Comment(0)
F
1

For all who said that Garry Tan solution converting incorrect from RGB to HSL and back. It because he left out fraction part of number in his code. I corrected his code (javascript). Sorry for link on russian languadge, but on english absent - HSL-wiki

function toHsl(r, g, b)
{
    r /= 255.0;
    g /= 255.0;
    b /= 255.0;
    var max = Math.max(r, g, b);
    var min = Math.min(r, g, b);
    var h, s, l = (max + min) / 2.0;

    if(max == min)
    {
        h = s = 0; 
    }
    else
    {
        var d = max - min;
        s = (l > 0.5 ? d / (2.0 - max - min) : d / (max + min));

        if(max == r && g >= b)
        {
            h = 1.0472 * (g - b) / d ;
        }
        else if(max == r && g < b)
        {
            h = 1.0472 * (g - b) / d + 6.2832;
        }
        else if(max == g)
        {
            h = 1.0472 * (b - r) / d + 2.0944;
        }
        else if(max == b)
        {
            h = 1.0472 * (r - g) / d + 4.1888;
        }
    }
    return {
        str: 'hsl(' + parseInt(h / 6.2832 * 360.0 + 0.5) + ',' + parseInt(s * 100.0 + 0.5) + '%,' + parseInt(l * 100.0 + 0.5) + '%)',
        obj: { h: parseInt(h / 6.2832 * 360.0 + 0.5), s: parseInt(s * 100.0 + 0.5), l: parseInt(l * 100.0 + 0.5) }
    };
};
Favien answered 22/1, 2016 at 11:51 Comment(0)
T
1

PHP implementation of @Mohsen's code (including Test!)

Sorry to re-post this. But I really haven't seen any other implementation that gives the quality I needed.

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {number}  h       The hue
 * @param   {number}  s       The saturation
 * @param   {number}  l       The lightness
 * @return  {Array}           The RGB representation
 */
  
function hue2rgb($p, $q, $t){
            if($t < 0) $t += 1;
            if($t > 1) $t -= 1;
            if($t < 1/6) return $p + ($q - $p) * 6 * $t;
            if($t < 1/2) return $q;
            if($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
            return $p;
        }
function hslToRgb($h, $s, $l){
    if($s == 0){
        $r = $l;
        $g = $l;
        $b = $l; // achromatic
    }else{
        $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
        $p = 2 * $l - $q;
        $r = hue2rgb($p, $q, $h + 1/3);
        $g = hue2rgb($p, $q, $h);
        $b = hue2rgb($p, $q, $h - 1/3);
    }

    return array(round($r * 255), round($g * 255), round($b * 255));
}

/* Uncomment to test * /
for ($i=0;$i<360;$i++) {
  $rgb=hslToRgb($i/360, 1, .9);
  echo '<div style="background-color:rgb(' .$rgb[0] . ', ' . $rgb[1] . ', ' . $rgb[2] . ');padding:2px;"></div>';
}
/* End Test */
Tomas answered 8/11, 2016 at 15:21 Comment(0)
S
1

C++ implementation with probably better performance than @Mohsen code. It uses a [0-6] range for the hue, avoiding the division and multiplication by 6. S and L range is [0,1]

void fromRGBtoHSL(float rgb[], float hsl[])
{
     const float maxRGB = max(rgb[0], max(rgb[1], rgb[2]));
     const float minRGB = min(rgb[0], min(rgb[1], rgb[2]));
     const float delta2 = maxRGB + minRGB;
     hsl[2] = delta2 * 0.5f;

     const float delta = maxRGB - minRGB;
     if (delta < FLT_MIN)
         hsl[0] = hsl[1] = 0.0f;
     else
     {
         hsl[1] = delta / (hsl[2] > 0.5f ? 2.0f - delta2 : delta2);
         if (rgb[0] >= maxRGB)
         {
             hsl[0] = (rgb[1] - rgb[2]) / delta;
             if (hsl[0] < 0.0f)
                 hsl[0] += 6.0f;
         }
         else if (rgb[1] >= maxRGB)
             hsl[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
         else
             hsl[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
     }
}

void fromHSLtoRGB(const float hsl[], float rgb[])
{
    if(hsl[1] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = hsl[2];
    else if(hsl[2] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = 0.0f;
    else
    {
        const float q = hsl[2] < 0.5f ? hsl[2] * (1.0f + hsl[1]) : hsl[2] + hsl[1] - hsl[2] * hsl[1];
        const float p = 2.0f * hsl[2] - q;
        float t[] = {hsl[0] + 2.0f, hsl[0], hsl[0] - 2.0f};

        for(int i=0; i<3; ++i)
        {
            if(t[i] < 0.0f)
                t[i] += 6.0f;
            else if(t[i] > 6.0f)
                t[i] -= 6.0f;

            if(t[i] < 1.0f)
                rgb[i] = p + (q - p) * t[i];
            else if(t[i] < 3.0f)
                rgb[i] = q;
            else if(t[i] < 4.0f)
                rgb[i] = p + (q - p) * (4.0f - t[i]);
            else
                rgb[i] = p;
          }
      }
}
Smaragdite answered 22/3, 2018 at 16:25 Comment(0)
K
1

Java version:

/*
Converts color from HSL/A format to RGB/A format
0 <= h <= 360
0 <= s, l, a <= 1
Based on: https://en.wikipedia.org/wiki/HSL_and_HSV#:~:text=%5Bedit%5D-,HSL%20to%20RGB%5Bedit%5D,-Given%20a%20color
 */
public RGB toRGB(double h, double s, double l, Double a) {
    double c = (1 - Math.abs(2 * l - 1)) * s;
    double x = c * (1 - Math.abs((h / 60) % 2 - 1));
    double m = l - c / 2;
    int[] RGBTag = Arrays.stream(getRGBTag(c, x)).mapToInt(e -> (int)Math.round((e + m) * 255)).toArray();
    return RGB(RGBTag[0], RGBTag[1], RGBTag[2], a);
}

private double[] getRGBTag(double c, double x) {
    if (h < 60) {
        return new double[] {c, x, 0};
    } else if (h < 120) {
        return new double[] {x, c, 0};
    } else if (h < 180) {
        return new double[] {0, c, x};
    } else if (h < 240) {
        return new double[] {0, x, c};
    } else if (h < 300) {
        return new double[] {x, 0, c};
    }
    return new double[] {c, 0, x};
}
Kelsy answered 5/1, 2023 at 15:38 Comment(0)
D
1

I modified @moshen's answer to use numpy vectorization in python. Ends up a couple hundred times faster than two for loops over the image with colorsys.hsl_to_rgb.


import time

def hue_to_rgb(p, q, t):
    t = t % 1.0
    condlist = [t < 1/6, t < 1/2, t < 2/3]
    choicelist = [p + (q - p) * 6 * t, q, p + (q - p) * (2/3 - t) * 6]
    return np.select(condlist, choicelist, p)

def fast_hls_to_rgb(hls):
    h = hls[..., 0]
    l = hls[..., 1]
    s = hls[..., 2]
    q = np.where(l < 0.5, l * (1 + s), l + s - l * s)
    p = 2 * l - q
    return np.stack((hue_to_rgb(p, q, h + 1/3), hue_to_rgb(p, q, h), hue_to_rgb(p, q, h - 1/3)), axis=-1)

hls = np.empty((image_shape, 3))
hls[..., 0] = hue
hls[..., 1] = lightness
hls[..., 2] = saturation

# compute the rgb using colorssys and report the time required
start = time.perf_counter()
rgb = np.empty((*image_shape, 3))
for i in range(image_shape[0]):
    for j in range(image_shape[1]):
        rgb[i, j] = colorsys.hls_to_rgb(hls[i, j, 0], hls[i, j, 1], hls[i, j, 2])
end = time.perf_counter()
print(end - start)
# compute the rgb using fast_hsl_to_rgb and report the time required
start = time.perf_counter()
rgb = fast_hls_to_rgb(hls)
end = time.perf_counter()
print(end - start)

plt.figure(figsize=(18, 18))
plt.imshow(rgb)
plt.axis('off')
plt.show()

which gives the output (for an 815 x 1024 size image):

1.1594520434737206 # time taken to convert with for loops
0.06863640993833542 # time taken to convert with np vectorized
Daisey answered 5/5, 2023 at 13:7 Comment(0)
U
0

I needed a really light weight one, Its not 100%, but it gets close enough for some usecases.

float3 Hue(float h, float s, float l)
{
    float r = max(cos(h * 2 * UNITY_PI) * 0.5 + 0.5, 0);
    float g = max(cos((h + 0.666666) * 2 * UNITY_PI) * 0.5 + 0.5, 0);
    float b = max(cos((h + 0.333333) * 2 * UNITY_PI) * 0.5 + 0.5, 0);
    float gray = 0.2989 * r + 0.5870 * g + 0.1140 * b;
    return lerp(gray, float3(r, g, b), s) * smoothstep(0, 0.5, l) + 1 * smoothstep(0.5, 1, l);
}
Uniocular answered 12/11, 2015 at 22:12 Comment(0)
H
0

PHP - shortest but precise

Here I rewrite my JS answer (math details are there) to PHP - you can run it here

function hsl2rgb($h,$s,$l) 
{
  $a = $s * min($l, 1-$l);
  $k = function($n,$h) { return ($n+$h/30)%12;};
  $f = function($n) use ($h,$s,$l,$a,$k) { 
      return $l - $a * max( min($k($n,$h)-3, 9-$k($n,$h), 1),-1);
  };
  return [ $f(0), $f(8), $f(4) ];
}   
Hailstorm answered 28/10, 2020 at 17:17 Comment(0)
S
0

Since colorsys isn't supported in (or currently ported to) circuitpython, if you're trying to handle this conversion on Raspberry Pi, the following works (subject to the rounding limitation to accuracy mentioned elsewhere in this thread):

def hslToRgb (h, s, l): #in range 0-1 for h,s,l
    if s == 0:
        r = g = b = l #achromatic
    else:
        def hue2rgb(p, q, t):
            if t < 0: t += 1
            if t > 1: t -= 1
            if t < 1.0 / 6.0: return p + (q - p) * 6 * t
            if t < 1.0 / 2.0: return q
            if t < 2.0 / 3.0: return p + (q - p) * ((2.0 / 3.0) - t) * 6
            return p
        if l < 0.5:
            q = l * (1 + s)
        else:
            q = l + s - l * s
        p = 2 * l - q
        r = hue2rgb(p, q, h + 1.0/3.0)
        g = hue2rgb(p, q, h)
        b = hue2rgb(p, q, h - 1.0/3.0)
    return [round(r * 255), round(g * 255), round(b * 255)]
Seville answered 13/10, 2022 at 7:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.