Почему этот код Javascript RGB to HSL не работает?


person Kyle Hotchkiss    schedule 27.02.2010    source источник
comment
Несоответствие названия вопроса и имени функции, что именно вы ищете, rgb для hsl или hsl для rgb? Также, пожалуйста, покажите код, в котором вы используете эту функцию.   -  person Sarfraz    schedule 27.02.2010
comment
В чем проблема, какие значения неверны?   -  person Pekka    schedule 27.02.2010
comment
Похоже, что это работает для меня, и когда я пытаюсь это сделать, он определенно возвращает хороший массив HSL. Что заставляет вас думать, что это не работает? Вы должны вызывать его со значениями RGB, выраженными в виде десятичных целых чисел от 0 до 255, если это не ясно.   -  person Pointy    schedule 27.02.2010
comment
Может быть, он думает, что это это функция преобразования HSL в RGB?   -  person Matchu    schedule 27.02.2010
comment
Признание, что я немного не разбираюсь в цветовой математике, может быть красноречивым :-)   -  person Pointy    schedule 27.02.2010
comment
(Кстати, CSS3 изначально поддерживает цвета HSL/HSLA. Поддерживается Gecko, WebKit и Presto (не уверен насчет Trident).)   -  person kennytm    schedule 27.02.2010
comment
Дает мне десятичные дроби для оранжевого цвета, когда я ввожу значения зеленого цвета. пример: rgb: 126,210,22 дает мне hsl: .24, .81, .45   -  person Kyle Hotchkiss    schedule 27.02.2010
comment
@Kyle: Почему вы думаете, что 0,24 (= 87 градусов) оранжевый?   -  person kennytm    schedule 27.02.2010
comment
Вы хотите сказать, что 0,24 нужно умножить на 360? Я этого точно не осознавал - я ожидал, что это выйдет именно так.   -  person Kyle Hotchkiss    schedule 27.02.2010
comment
.24 - это соотношение 24/100. Из этого естественно следует, что если вы хотите, чтобы это было не из 360, вам нужно будет сделать это преобразование самостоятельно.   -  person Matchu    schedule 27.02.2010
comment
@ Кайл: Может быть. Все 3 элемента возвращаемого массива находятся в диапазоне от 0 до 1.   -  person kennytm    schedule 27.02.2010


Ответы (4)


Результирующий массив HSV должен интерпретироваться как три дроби. Для некоторых программ, если вы хотите выразить HSV в виде целых чисел, вы умножаете значение «H» на 360, а значения «S» и «V» на 100. Значение HSV, которое вы указываете для своего зеленого оттенка RGB[126, 210, 22] представляет собой HSV [87, 81, 45] в целых числах. Вы можете изменить функцию, чтобы она возвращала такие целые числа, если хотите:

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)];
}

[править] тем не менее, это все еще дает мне что-то с яркостью («L» или «V»), которая значительно слишком темная; Гимп говорит, что значение HSV должно быть [90, 80, 82] или дробным числом [0,20, 0,80, 0,82].

[другое редактирование] ну, одна проблема может заключаться в том, что HSL и HSV - это разные схемы ... все еще ищу.

Хорошо, если кто-то хочет преобразовать RGB в HSV (например, как вы видите в Gimp), вот версия этого:

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];
}
person Pointy    schedule 27.02.2010
comment
rgbToHsv(210, 210, 210) деление на 0 в h = ( g - b ) / delta; - person BrunoLM; 30.04.2011
comment
@BrunoLM это все еще альфа-версия 0.0.1 :-) Я подозреваю, что это произойдет только тогда, когда вы дадите ему нейтральный (серый) цвет - я не уверен, что с этим делать. Я предполагаю, что оттенок будет черным, насыщенность будет 100%, а значение будет max/255, может быть? Я должен был бы играть с этим еще немного. - person Pointy; 30.04.2011
comment
@Pointy Это был именно мой тест: P. Я решил использовать другой var для деления. var deltadiv = delta == 0 ? 1 : delta;. Результат 0, 0, 82, а Photoshop говорит мне, что это rgb(209, 209, 209). Справедливо :) - person BrunoLM; 30.04.2011
comment
Вот ваш код с исправлением для этого случая, а также HsvToRgb, который я нашел на другом веб-сайте: stackoverflow.com/questions/5833624/ - person BrunoLM; 30.04.2011
comment
@BrunoLM, спасибо!! Лично я в настоящее время не использую такой код (на моем текущем веб-сайте), но ваше внимание к деталям и полноте, безусловно, улучшит опыт будущих клиентов Stackoverflow :-) - person Pointy; 30.04.2011
comment
Я смущен. HSL и HSV - это разные вещи, верно? Спрашивающий попросил HSL? - person slashdottir; 24.02.2014
comment
@slashdottir да, они разные - этот ответ (старый и) запутанный, но в нем есть оба преобразования. Конечно, в современных браузерах вы можете напрямую задавать цвета как hsl, так и hsv! - person Pointy; 24.02.2014
comment
@Pointy Преобразование HSV работает хорошо, но я думаю, что значения должны быть округлены, а не установлены на пол, чтобы точно соответствовать значениям Photoshop. (Требуется обширное тестирование) - person Mr Mystery Guest; 28.02.2017
comment
почти уверен, что это round, а не floor - person Tom Roggero; 18.10.2017

Коротко, но точно

Похоже, ваш код в порядке (но он возвращает оттенок = 0,24 - умножьте это на 360 градусов, чтобы получить целочисленное значение угла) - однако попробуйте этот более короткий (подробнее: hsl2rgb, rgb2hsv, hsv2rgb и sl22sv):

// in: r,g,b in [0,1], out: h in [0,360) and s,l in [0,1]
function rgb2hsl(r,g,b) {
  let v=Math.max(r,g,b), c=v-Math.min(r,g,b), f=(1-Math.abs(v+v-c-1)); 
  let h= c && ((v==r) ? (g-b)/c : ((v==g) ? 2+(b-r)/c : 4+(r-g)/c)); 
  return [60*(h<0?h+6:h), f ? c/f : 0, (v+v-c)/2];
}

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

console.log(`rgb: (0.36,0.3,0.24) --> hsl: (${rgb2hsl(0.36,0.3,0.24)})`);


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

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

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

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)];

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(', ')
  tr = `RGB: ${rr}`
  th = `HSL: ${hs.map((x,i)=>i? (x*100).toFixed(2)+'%':x|0).join(', ')}`
  $('.box').style.backgroundColor=`rgb(${rr})`;  
  $('.infoRGB').innerHTML=`${tr}`;  
  $('.infoHS').innerHTML =`${th}`;  
  
  $('#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;  
}

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>

Я разрабатываю формулы S_HSL wiki (обозначены зеленой рамкой) - где MAX=max(r ,g,b) и MIN=min(r,g,b) - и в приведенном выше коде я делаю некоторые улучшения и делаю анализ, который показывает, что результаты верны. Это позволяет мне получить довольно короткий код в конце

введите здесь описание изображения

person Kamil Kiełczewski    schedule 07.01.2019

Функция ниже преобразует цвет RGB в цвет Hue Saturation Brightness, как палитра цветов Photoshop, результаты находятся в следующих диапазонах:

  • Оттенок 0-360 (градусов)
  • Насыщенность: 0-100 (%)
  • Яркость: 0-100 (%)

Я до сих пор не понимаю, почему люди используют термин HSV (Hue Saturation Value) вместо HSB (Hue Saturation Brightness), в любом случае это вопрос терминологии, результаты одинаковы

   //Converts to color HSB object (code from here http://www.csgnetwork.com/csgcolorsel4.html with some improvements)
   function rgb2hsb(r, g, b)
   {    
    r /= 255; g /= 255; b /= 255; // Scale to unity.   
    var minVal = Math.min(r, g, b),
    maxVal = Math.max(r, g, b),
    delta = maxVal - minVal,
    HSB = {hue:0, sat:0, bri:maxVal},
    del_R, del_G, del_B;

    if( delta !== 0 )
    {
        HSB.sat = delta / maxVal;
        del_R = (((maxVal - r) / 6) + (delta / 2)) / delta;
        del_G = (((maxVal - g) / 6) + (delta / 2)) / delta;
        del_B = (((maxVal - b) / 6) + (delta / 2)) / delta;

        if (r === maxVal) {HSB.hue = del_B - del_G;}
        else if (g === maxVal) {HSB.hue = (1 / 3) + del_R - del_B;}
        else if (b === maxVal) {HSB.hue = (2 / 3) + del_G - del_R;}

        if (HSB.hue < 0) {HSB.hue += 1;}
        if (HSB.hue > 1) {HSB.hue -= 1;}
    }

    HSB.hue *= 360;
    HSB.sat *= 100;
    HSB.bri *= 100;

    return HSB;
   }

Пример использования:

var hsb = rgb2hsb(126,210,22);
alert("hue = " + hsb.hue + "saturation = " + hsb.sat + "brightness = " + hsb.bri);
person Marco Demaio    schedule 30.05.2011

Вы должны перейти на формат hsl точно так же, как hsl(155,100%,30%), который можно использовать в HTML.

//this change hsl to :  "hsl(155,100%,30%)"
function hsl2str({h,s,l,a=1}) {
    return "hsl("+h+","+s*100+"%,"+l*100+"%,"+a+")";
}
//for html, the h is 0-360, so you need this function
function rgb2hsl({r,g,b,a=1}) {
    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;
        }
    }

    return {h: h*60,s,l,a};
}
person defend orca    schedule 06.05.2020