JavaScript Convert Hsb/Hsv Color to Rgb Accurately

Javascript convert HSB/HSV color to RGB accurately

From Parthik Gosar's link in this comment with slight modification to let you enter each value independently or all at once as an object

/* accepts parameters
* h Object = {h:x, s:y, v:z}
* OR
* h, s, v
*/
function HSVtoRGB(h, s, v) {
var r, g, b, i, f, p, q, t;
if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
}
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}

This code expects 0 <= h, s, v <= 1, if you're using degrees or radians, remember to divide them out.

The returned 0 <= r, g, b <= 255 are rounded to the nearest Integer. If you don't want this behaviour remove the Math.rounds from the returned object.


And the reverse (with less division)

/* accepts parameters
* r Object = {r:x, g:y, b:z}
* OR
* r, g, b
*/
function RGBtoHSV(r, g, b) {
if (arguments.length === 1) {
g = r.g, b = r.b, r = r.r;
}
var max = Math.max(r, g, b), min = Math.min(r, g, b),
d = max - min,
h,
s = (max === 0 ? 0 : d / max),
v = max / 255;

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

return {
h: h,
s: s,
v: v
};
}

This code will output 0 <= h, s, v <= 1, but this time takes any 0 <= r, g, b <= 255 (does not need to be an integer)


For completeness,

function HSVtoHSL(h, s, v) {
if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
}
var _h = h,
_s = s * v,
_l = (2 - s) * v;
_s /= (_l <= 1) ? _l : 2 - _l;
_l /= 2;

return {
h: _h,
s: _s,
l: _l
};
}

function HSLtoHSV(h, s, l) {
if (arguments.length === 1) {
s = h.s, l = h.l, h = h.h;
}
var _h = h,
_s,
_v;

l *= 2;
s *= (l <= 1) ? l : 2 - l;
_v = (l + s) / 2;
_s = (2 * s) / (l + s);

return {
h: _h,
s: _s,
v: _v
};
}

All of these values should be in the range 0 to 1. For HSL<->RGB go via HSV.

Javascript: Converting HSV to RGB, output correct but shuffled

First, the most important issue is that although

if ( 0 <= h <= 1 )

looks reasonable, it doesn't work the way one might think. It's interpreted by JavaScript as if it were written

if ((0 <= h) <= 1)

The JavaScript comparison operators return boolean results, so while it's syntactically correct it's doing something completely different than checking whether h is between 0 and 1.

Because the algorithm is using the H angle of the input value to pick between one of six different scenarios, the whole thing could be done a little bit more simply with a switch statement (as mentioned in a comment on the question). First, the code as written handles the case of h being negative, which is fine, but since it's an angular value it's safe to just force it into the range [0, 360):

function hsvRGB(h, s, v) {
while (h < 0) h += 360; // could be smarter but just for illustration
h = h % 360;
h /= 60, s /= 100, v /= 100; // Convert [deg, %, %] to ranges 0-6, 0-1, 0-1

So now h is between 0 and 6; it might be 0 but it won't ever be exactly 6. We can then use a switch to distinguish the cases:

    switch (Math.floor(h)) {
case 0: [r1, g1, b1] = [c, x, 0]; break;
case 1: [r1, g1, b1] = [x, c, 0]; break;
case 2: [r1, g1, b1] = [0, c, x]; break;
case 3: [r1, g1, b1] = [0, x, c]; break;
case 4: [r1, g1, b1] = [x, 0, c]; break;
case 5: [r1, g1, b1] = [c, 0, x]; break;
}

HSL to RGB color conversion

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.

RGB to HSV color in javascript?

Here is a standalone function:

function rgb2hsv (r, g, b) {
let rabs, gabs, babs, rr, gg, bb, h, s, v, diff, diffc, percentRoundFn;
rabs = r / 255;
gabs = g / 255;
babs = b / 255;
v = Math.max(rabs, gabs, babs),
diff = v - Math.min(rabs, gabs, babs);
diffc = c => (v - c) / 6 / diff + 1 / 2;
percentRoundFn = num => Math.round(num * 100) / 100;
if (diff == 0) {
h = s = 0;
} else {
s = diff / v;
rr = diffc(rabs);
gg = diffc(gabs);
bb = diffc(babs);

if (rabs === v) {
h = bb - gg;
} else if (gabs === v) {
h = (1 / 3) + rr - bb;
} else if (babs === v) {
h = (2 / 3) + gg - rr;
}
if (h < 0) {
h += 1;
}else if (h > 1) {
h -= 1;
}
}
return {
h: Math.round(h * 360),
s: percentRoundFn(s * 100),
v: percentRoundFn(v * 100)
};
}

And how to use it:

console.log( rgb2hsv(60, 120, 180) );
// {h: 210, s: 66.67, v: 70.59}

Why doesn't this Javascript RGB to HSL code work?

The resulting HSV array has to be interpreted as three fractions. For some programs, if you want to express HSV as integers, you multiply the "H" value by 360 and the "S" and "V" values by 100. The HSV value you quote for your green shade RGB[126, 210, 22] is HSV [87, 81, 45] in integers. You could change the function to return such integers if you want to:

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 + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}

return [Math.floor(h * 360), Math.floor(s * 100), Math.floor(l * 100)];
}

[edit] that said, it's still giving me something with a brightness ("L" or "V") that's considerably too dark; Gimp says that the HSV value should be [90, 80, 82], or in fractional terms [.20, .80, .82].

[another edit] well one problem could be that HSL and HSV are different schemes ... still looking around.

OK in case anybody wants RGB to HSV (like you'd see in Gimp for example) here's a version of that:

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

v = Math.floor(max / 255 * 100);
if ( max != 0 )
s = Math.floor(delta / max * 100);
else {
// black
return [0, 0, 0];
}

if( r == max )
h = ( g - b ) / delta; // between yellow & magenta
else if( g == max )
h = 2 + ( b - r ) / delta; // between cyan & yellow
else
h = 4 + ( r - g ) / delta; // between magenta & cyan

h = Math.floor(h * 60); // degrees
if( h < 0 ) h += 360;

return [h, s, v];
}

edit note that a couple comments suggest that Math.round() might give better answers than Math.floor(), if anybody wants to experiment.



Related Topics



Leave a reply



Submit