RGB到HSL的转换

aki*_*uri 25 hsl rgb colors converter

我正在创建一个拾色器工具,对于HSL滑块,我需要能够将RGB转换为HSL.当我搜索SO以进行转换的方法时,我发现这个问题HSL到RGB颜色转换.

虽然它提供了从RGB转换到HSL的功能,但我没有看到计算中实际发生的事情的解释.为了更好地理解它,我在维基百科上阅读了HSL和HSV.

后来,我使用"HSL和HSV"页面中的计算重写了"HSL到RGB颜色转换"的功能.

如果R是最大值,我会坚持计算色调.请参阅"HSL和HSV"页面中的计算:

在此输入图像描述

这是来自荷兰语的另一个维基页面:

在此输入图像描述

这是从"HSL到RGB颜色转换" 的答案:

case r: h = (g - b) / d + (g < b ? 6 : 0); break; // d = max-min = c
Run Code Online (Sandbox Code Playgroud)

我已经用几个RGB值测试了所有三个,它们似乎产生了类似的(如果不是精确的)结果.我想知道的是他们表演同样的事情吗?对于某些特定的RGB值,我会得到不同的结果吗?我应该使用哪一个?

hue = (g - b) / c;                   // dutch wiki
hue = ((g - b) / c) % 6;             // eng wiki
hue = (g - b) / c + (g < b ? 6 : 0); // SO answer
Run Code Online (Sandbox Code Playgroud)

function rgb2hsl(r, g, b) {
    // see https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation
    // convert r,g,b [0,255] range to [0,1]
    r = r / 255,
    g = g / 255,
    b = b / 255;
    // get the min and max of r,g,b
    var max = Math.max(r, g, b);
    var min = Math.min(r, g, b);
    // lightness is the average of the largest and smallest color components
    var lum = (max + min) / 2;
    var hue;
    var sat;
    if (max == min) { // no saturation
        hue = 0;
        sat = 0;
    } else {
        var c = max - min; // chroma
        // saturation is simply the chroma scaled to fill
        // the interval [0, 1] for every combination of hue and lightness
        sat = c / (1 - Math.abs(2 * lum - 1));
        switch(max) {
            case r:
                // hue = (g - b) / c;
                // hue = ((g - b) / c) % 6;
                // hue = (g - b) / c + (g < b ? 6 : 0);
                break;
            case g:
                hue = (b - r) / c + 2;
                break;
            case b:
                hue = (r - g) / c + 4;
                break;
        }
    }
    hue = Math.round(hue * 60); // °
    sat = Math.round(sat * 100); // %
    lum = Math.round(lum * 100); // %
    return [hue, sat, lum];
}
Run Code Online (Sandbox Code Playgroud)

aki*_*uri 73

我一直在阅读几个维基页面并检查不同的计算,并创建RGB立方体投影到六边形的可视化.我想发表我对这种转换的理解.由于我发现这种转换(使用几何形状的颜色模型的表示)很有趣,我会尽量做到尽可能彻底.首先,让我们从RGB开始.

RGB

嗯,这真的不需要太多解释.在最简单的形式中,您有3个值,R,G和B,范围为[0,255].例如,51,153,204.我们可以使用条形图来表示它:

RGB条形图

RGB立方体

我们还可以在3D空间中表示颜色.我们有三个值R,G,B对应于X,YZ.所有三个值都在该[0,255]范围内,从而产生一个立方体.但在创建RGB立方体之前,让我们首先处理2D空间.R,G,B的两种组合给出了:RG,RB,GB.如果我们要在飞机上绘制这些图表,我们会得到以下结果:

RGB 2D图形

这些是RGB立方体的前三个方面.如果我们将它们放在3D空间中,它会产生一个半立方体:

RGB立方体侧面

如果你检查上面的图表,通过混合两种颜色,我们得到一个新的颜色(255,255),这些是黄色,洋红色和青色.同样,这两种组合给我们:YM,YC和MC.这些是立方体缺失的一面.一旦我们添加它们,我们就会获得一个完整的立方

RGB立方体

并且51,153,204在这个立方体中的位置:

RGB立方体颜色位置

将RGB立方体投影到六边形上

现在我们有了RGB立方体,让它将它投射到六边形上.首先,我们将立方体倾斜45° x,然后再倾斜35.264° y.第二次倾斜后,黑色角落在底部,白色角落在顶部,它们都穿过z轴.

RGB立方体倾斜

如您所见,当我们从顶部查看立方体时,我们可以使用正确的色调顺序获得我们想要的六边形外观.但我们需要把它投射到一个真正的六边形上.我们所做的是绘制一个与立方体顶视图大小相同的六边形.六角形的所有角都对应于立方体的角和颜色,并且立方体的顶角是白色,投影到六边形的中心.黑色被省略.如果我们将每种颜色映射到六边形上,我们就会看到正确的.

立方体到六角形投影

51,153,204六角形的位置是:

色调位置

计算色调

在我们进行计算之前,让我们定义一下色调是什么.

色调大致是向量与投影中的点的角度,红色为0°.

......色调是六角形边缘的重点所在.

这是HSL和HSV wiki页面的计算结果.我们将在这个解释中使用它.

维基计算

检查六边形及其上的位置51,153,204.

六角基础知识

首先,我们缩放R,G,B值以填充[0,1]区间.

R = R / 255    R =  51 / 255 = 0.2
G = G / 255    G = 153 / 255 = 0.6
B = B / 255    B = 204 / 255 = 0.8
Run Code Online (Sandbox Code Playgroud)

接下来,找到max和的minR, G, B

M = max(R, G, B)    M = max(0.2, 0.6, 0.8) = 0.8
m = min(R, G, B)    m = min(0.2, 0.6, 0.8) = 0.2
Run Code Online (Sandbox Code Playgroud)

然后,计算C(色度).色度定义为:

...色度大致是点与原点的距离.

色度是通过点的六边形的相对大小......

C = OP / OP'
C = M - m
C = 0.8- 0.2 = 0.6
Run Code Online (Sandbox Code Playgroud)

现在,我们有R,G,B,和C值.如果我们检查条件,则if M = B返回true 51,153,204.所以,我们将使用H'= (R - G) / C + 4.

我们再来检查一下六边形.(R - G) / C给出了BP段的长度.

segment = (R - G) / C = (0.2 - 0.6) / 0.6 = -0.6666666666666666
Run Code Online (Sandbox Code Playgroud)

我们将这段放在内六角上.六边形的起点是0°处的R(红色).如果段长度为正,则应为on RY,如果为负,则应为on RM.在这种情况下,它是负面的-0.6666666666666666,并且在RM边缘.

细分位置和转变

接下来,我们需要移动段的位置,或者更P?确切地说是B(因为M = B).蓝色在240°.Hexagon有6个侧面.每一面对应60°.240 / 60 = 4.我们需要移动(递增)P?by 4(240°).转移之后,P?将会在P,我们将得到的长度RYGCP.

segment = (R - G) / C = (0.2 - 0.6) / 0.6 = -0.6666666666666666
RYGCP   = segment + 4 = 3.3333333333333335
Run Code Online (Sandbox Code Playgroud)

六边形的周长6对应于360°.53,151,204的距离3.3333333333333335.如果我们乘3.333333333333333560,我们会得到它的度的位置.

H' = 3.3333333333333335
H  = H' * 60 = 200°
Run Code Online (Sandbox Code Playgroud)

在这种情况下if M = R,由于我们将段的一端放在R(0°)处,如果段长度为正,我们不需要将段移动到R. 这个立场P?是积极的.但是如果段长度为负,我们需要将其移动6,因为负值意味着角位置大于180°并且我们需要进行完整旋转.

因此,荷兰维基解决方案hue = (g - b) / c;和Eng维基解决方案都不hue = ((g - b) / c) % 6;适用于负片段长度.只有SO答案hue = (g - b) / c + (g < b ? 6 : 0);适用于负值和正值.

JSFiddle:测试rgb的所有三种方法(255,71,99)


JSFiddle:在RGB Cube和色调六边形中直观地找到颜色的位置

工作色调计算:

console.log(rgb2hue(51,153,204));
console.log(rgb2hue(255,71,99));
console.log(rgb2hue(255,0,0));
console.log(rgb2hue(255,128,0));
console.log(rgb2hue(124,252,0));

function rgb2hue(r, g, b) {
  r /= 255;
  g /= 255;
  b /= 255;
  var max = Math.max(r, g, b);
  var min = Math.min(r, g, b);
  var c   = max - min;
  var hue;
  if (c == 0) {
    hue = 0;
  } else {
    switch(max) {
      case r:
        var segment = (g - b) / c;
        var shift   = 0 / 60;       // R° / (360° / hex sides)
        if (segment < 0) {          // hue > 180, full rotation
          shift = 360 / 60;         // R° / (360° / hex sides)
        }
        hue = segment + shift;
        break;
      case g:
        var segment = (b - r) / c;
        var shift   = 120 / 60;     // G° / (360° / hex sides)
        hue = segment + shift;
        break;
      case b:
        var segment = (r - g) / c;
        var shift   = 240 / 60;     // B° / (360° / hex sides)
        hue = segment + shift;
        break;
    }
  }
  return hue * 60; // hue is in [0,6], scale it up
}
Run Code Online (Sandbox Code Playgroud)

  • 嗨,我很震惊您的答案被大多数人忽略或看不到。非常感谢您的解释,我已经研究了一段时间,您的回答是最好的 (4认同)
  • @AlexanderGurevich 我只是喜欢追根究底,这是一项很好的研究。我想把它公开。我很高兴有人发现它有用:) (2认同)
  • @Binajmen您可以检查问题中的“rgb2hsl”。所有三种色调计算(M=R)都在那里。把前两个删掉就可以了 (2认同)

Cra*_*lot 10

该页面提供了颜色空间之间转换的功能,包括RGB到HSL。

\n
function RGBToHSL(r,g,b) {\n  // Make r, g, and b fractions of 1\n  r /= 255;\n  g /= 255;\n  b /= 255;\n\n  // Find greatest and smallest channel values\n  let cmin = Math.min(r,g,b),\n      cmax = Math.max(r,g,b),\n      delta = cmax - cmin,\n      h = 0,\n      s = 0,\n      l = 0;\n\n  // Calculate hue\n  // No difference\n  if (delta === 0)\n    h = 0;\n  // Red is max\n  else if (cmax === r)\n    h = ((g - b) / delta) % 6;\n  // Green is max\n  else if (cmax === g)\n    h = (b - r) / delta + 2;\n  // Blue is max\n  else\n    h = (r - g) / delta + 4;\n\n  h = Math.round(h * 60);\n    \n  // Make negative hues positive behind 360\xc2\xb0\n  if (h < 0)\n      h += 360;\n\n  // Calculate lightness\n  l = (cmax + cmin) / 2;\n\n  // Calculate saturation\n  s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));\n    \n  // Multiply l and s by 100\n  s = +(s * 100).toFixed(1);\n  l = +(l * 100).toFixed(1);\n\n  return "hsl(" + h + "," + s + "%," + l + "%)";\n}\n
Run Code Online (Sandbox Code Playgroud)\n