HSI 到 RGB 颜色转换

HSI to RGB color conversion

我正在尝试实现 HSI <=> RGB 颜色转换
wiki上有公式https://en.wikipedia.org/wiki/HSL_and_HSV#HSI_to_RGB

RGB 转 HSI 似乎工作正常。
但是,我对 HSI 到 RGB 有困难。

我会写在Ruby里,例子会在Ruby里,但是如果你写在JS/Python/etc里,我觉得也可以理解,因为它只是数学。
Online ruby Interpreter.

def hsi_to_rgb(hsi_arr)
  # to float
  hue, saturation, intensity = hsi_arr.map(&:to_f)

  hue /= 60
  z = 1 - (hue % 2 - 1).abs
  chroma = (3 * intensity * saturation) / (1 + z)
  x = chroma * z
  point = case hue
          when 0..1 then [chroma, x, 0]
          when 1..2 then [x, chroma, 0]
          when 2..3 then [0, chroma, x]
          when 3..4 then [0, x, chroma]
          when 4..5 then [x, 0, chroma]
          when 5..6 then [chroma, 0, x]
          else [0, 0, 0]
          end

  # calculation rgb & scaling into range 0..255
  m = intensity * (1 - saturation)
  point.map { |channel| ((channel + m) * 255).round }
end

因此,使用简单的 html 颜色,似乎一切正常。 直到我尝试这样的值:

p hsi_to_rgb([0,   1,   1])    # => [765, 0, 0]
p hsi_to_rgb([360, 1,   1])    # => [765, 0, 0]
p hsi_to_rgb([357, 1,   1])    # => [729, 0, 36]
p hsi_to_rgb([357, 1, 0.5])    # => [364, 0, 18]

得到的值明显不正确,超出了0..255的范围。

我也看到过使用三角函数的实现:
https://hypjudy.github.io/images/dip/hsi2rgb.jpg
但是,我也没有得到正确的结果。

我发现的唯一在线 RGB 到 HSI 转换器:https://www.picturetopeople.org/color_converter.html
只是为了有一些东西可以比较。

您的实施看起来是正确的(假设维基百科是正确的)。

唯一缺少的部分是将 RGB 输出限制为 [0, 255]。

  • 正如 Giacomo Catenazzi 评论的那样,如果最大值高于 255,最好将 R,G,B 除以 max(R, G, B),而不是剪裁到 [0, 255]。

在大多数颜色 space 转换公式中,有些值在源颜色 space 的有效范围内,但不在目标颜色 space 的有效范围内.
常见的解决方案是将结果限制在有效范围内。

在某些情况下存在未定义的值。
看看 examples table.
的前 3 行 色相标记为 N/A 表示白色、黑色和灰色。

您选择的所有样本 HSI 值:
[0, 1, 1]
[360, 1, 1]
[357, 1, 1]
[357, 1, 0.5]
超出 RGB 颜色的有效范围 space(在 HSI 到 RGB 转换后)。


我建议您测试 examples table:

中的有效元组
   H       S       I                R       G       B
  ---    ----    ----             ----    ----    ----
    0    100%   33.3%             100%      0%      0%
   60    100%     50%              75%     75%      0%
  120    100%   16.7%               0%     50%      0%
  180     40%   83.3%              50%    100%    100%
  240     25%   66.7%              50%     50%    100%
  300   57.1%   58.3%              75%     25%     75%
 61.8   69.9%   47.1%            62.8%   64.3%   14.2%
251.1   75.6%   42.6%            25.5%   10.4%   91.8%
134.9   66.7%   34.9%            11.6%   67.5%   25.5%
 49.5   91.1%   59.3%            94.1%   78.5%    5.3%
283.7   68.6%   59.6%            70.4%   18.7%   89.7%
 14.3   44.6%     57%            93.1%   46.3%   31.6%
 56.9   36.3%   83.5%            99.8%   97.4%   53.2%
162.4     80%   49.5%             9.9%   79.5%   59.1%
248.3   53.3%   31.9%            21.1%   14.9%   59.7%
240.5   13.5%     57%            49.5%   49.3%   72.1%

我不知道 Rubi 编程语言的语法,但你的实现看起来是正确的。

这是一个 Python 实现,与维基百科的转换公式相匹配:

def hsi_to_rgb(hsi):
    """
    Convert HSI tuple to RGB tuple (without scaling the result by 255) 
    Formula: https://en.wikipedia.org/wiki/HSL_and_HSV#HSI_to_RGB        
    H - Range [0, 360] (degrees)
    S - Range [0, 1]
    V - Range [0, 1]
    The R,G,B output range is [0, 1]
    """

    H, S, I = float(hsi[0]), float(hsi[1]), float(hsi[2])

    Htag = H / 60
    Z = 1 - abs(Htag % 2 - 1)
    C = (3 * I * S) / (1 + Z)
    X = C * Z

    if 0 <= Htag <= 1:
        R1, G1, B1 = C, X, 0
    elif 1 <= Htag <= 2:
        R1, G1, B1 = X, C, 0
    elif 2 <= Htag <= 3:
        R1, G1, B1 = 0, C, X
    elif 3 <= Htag <= 4:
        R1, G1, B1 = 0, X, C
    elif 4 <= Htag <= 5:
        R1, G1, B1 = X, 0, C
    elif 5 <= Htag <= 6:
        R1, G1, B1 = C, 0, X
    else:
        R1, G1, B1 = 0, 0, 0  # Undefined

    # Calculation rgb
    m = I * (1 - S)
    R, G, B = R1 + m, G1 + m, B1 + m

    # Limit R, G, B to valid range:
    #R = max(min(R, 1), 0)
    #G = max(min(G, 1), 0)
    #B = max(min(B, 1), 0)

    # Handling RGB values above 1:
    # -----------------------------
    # Avoiding weird colours - see the comment of Giacomo Catenazzi.
    # Find the maximum between R, G, B, and if the value is above 1, divide the 3 channels with such numbers.
    max_rgb = max((R, G, B))

    if max_rgb > 1:
        R /= max_rgb
        G /= max_rgb
        B /= max_rgb

    return (R, G, B)


def rgb2percent(rgb):
    """ Convert rgb tuple to percentage with one decimal digit accuracy """
    rgb_per = (round(rgb[0]*1000.0)/10, round(rgb[1]*1000.0)/10, round(rgb[2]*1000.0)/10)
    return rgb_per


print(rgb2percent(hsi_to_rgb([    0,    100/100,   33.3/100])))  # => (99.9,   0.0,   0.0)   Wiki:  100%      0%      0%
print(rgb2percent(hsi_to_rgb([   60,    100/100,     50/100])))  # => (75.0,  75.0,   0.0)   Wiki:   75%     75%      0%
print(rgb2percent(hsi_to_rgb([  120,    100/100,   16.7/100])))  # => ( 0.0,  50.1,   0.0)   Wiki:    0%     50%      0%
print(rgb2percent(hsi_to_rgb([  180,     40/100,   83.3/100])))  # => (50.0, 100.0, 100.0)   Wiki:   50%    100%    100%
print(rgb2percent(hsi_to_rgb([  240,     25/100,   66.7/100])))  # => (50.0,  50.0, 100.0)   Wiki:   50%     50%    100%
print(rgb2percent(hsi_to_rgb([  300,   57.1/100,   58.3/100])))  # => (74.9,  25.0,  74.9)   Wiki:   75%     25%     75%
print(rgb2percent(hsi_to_rgb([ 61.8,   69.9/100,   47.1/100])))  # => (62.8,  64.3,  14.2)   Wiki: 62.8%   64.3%   14.2%
print(rgb2percent(hsi_to_rgb([251.1,   75.6/100,   42.6/100])))  # => (25.5,  10.4,  91.9)   Wiki: 25.5%   10.4%   91.8%
print(rgb2percent(hsi_to_rgb([134.9,   66.7/100,   34.9/100])))  # => (11.6,  67.6,  25.5)   Wiki: 11.6%   67.5%   25.5%
print(rgb2percent(hsi_to_rgb([ 49.5,   91.1/100,   59.3/100])))  # => (94.1,  78.5,   5.3)   Wiki: 94.1%   78.5%    5.3%
print(rgb2percent(hsi_to_rgb([283.7,   68.6/100,   59.6/100])))  # => (70.4,  18.7,  89.7)   Wiki: 70.4%   18.7%   89.7%
print(rgb2percent(hsi_to_rgb([ 14.3,   44.6/100,     57/100])))  # => (93.2,  46.3,  31.6)   Wiki: 93.1%   46.3%   31.6%
print(rgb2percent(hsi_to_rgb([ 56.9,   36.3/100,   83.5/100])))  # => (99.9,  97.4,  53.2)   Wiki: 99.8%   97.4%   53.2%
print(rgb2percent(hsi_to_rgb([162.4,     80/100,   49.5/100])))  # => ( 9.9,  79.5,  59.1)   Wiki:  9.9%   79.5%   59.1%
print(rgb2percent(hsi_to_rgb([248.3,   53.3/100,   31.9/100])))  # => (21.1,  14.9,  59.7)   Wiki: 21.1%   14.9%   59.7%
print(rgb2percent(hsi_to_rgb([240.5,   13.5/100,     57/100])))  # => (49.5,  49.3,  72.2)   Wiki: 49.5%   49.3%   72.1%

如您所见,结果与维基百科中的 examples table 匹配。

与WIKI颜色对比table:

def print_rgb(rgb)
  puts "[%s]" % rgb.map {|val| "%5.1f" % ((val / 255.0) * 100) }.join(", ")
end

print_rgb hsi_to_rgb([    0,    100/100.0,   33.3/100.0])  # => [100.0,   0.0,   0.0]   Wiki:  100%      0%      0%
print_rgb hsi_to_rgb([   60,    100/100.0,     50/100.0])  # => [ 74.9,  74.9,   0.0]   Wiki:   75%     75%      0%
print_rgb hsi_to_rgb([  120,    100/100.0,   16.7/100.0])  # => [  0.0,  50.2,   0.0]   Wiki:    0%     50%      0%
print_rgb hsi_to_rgb([  180,     40/100.0,   83.3/100.0])  # => [ 49.8, 100.0, 100.0]   Wiki:   50%    100%    100%
print_rgb hsi_to_rgb([  240,     25/100.0,   66.7/100.0])  # => [ 50.2,  50.2, 100.0]   Wiki:   50%     50%    100%
print_rgb hsi_to_rgb([  300,   57.1/100.0,   58.3/100.0])  # => [ 74.9,  25.1,  74.9]   Wiki:   75%     25%     75%
print_rgb hsi_to_rgb([ 61.8,   69.9/100.0,   47.1/100.0])  # => [ 62.7,  64.3,  14.1]   Wiki: 62.8%   64.3%   14.2%
print_rgb hsi_to_rgb([251.1,   75.6/100.0,   42.6/100.0])  # => [ 25.5,  10.6,  91.8]   Wiki: 25.5%   10.4%   91.8%
print_rgb hsi_to_rgb([134.9,   66.7/100.0,   34.9/100.0])  # => [ 11.8,  67.5,  25.5]   Wiki: 11.6%   67.5%   25.5%
print_rgb hsi_to_rgb([ 49.5,   91.1/100.0,   59.3/100.0])  # => [ 94.1,  78.4,   5.1]   Wiki: 94.1%   78.5%    5.3%
print_rgb hsi_to_rgb([283.7,   68.6/100.0,   59.6/100.0])  # => [ 70.6,  18.8,  89.8]   Wiki: 70.4%   18.7%   89.7%
print_rgb hsi_to_rgb([ 14.3,   44.6/100.0,     57/100.0])  # => [ 93.3,  46.3,  31.8]   Wiki: 93.1%   46.3%   31.6%
print_rgb hsi_to_rgb([ 56.9,   36.3/100.0,   83.5/100.0])  # => [100.0,  97.3,  53.3]   Wiki: 99.8%   97.4%   53.2%
print_rgb hsi_to_rgb([162.4,     80/100.0,   49.5/100.0])  # => [  9.8,  79.6,  59.2]   Wiki:  9.9%   79.5%   59.1%
print_rgb hsi_to_rgb([248.3,   53.3/100.0,   31.9/100.0])  # => [ 21.2,  14.9,  59.6]   Wiki: 21.1%   14.9%   59.7%
print_rgb hsi_to_rgb([240.5,   13.5/100.0,     57/100.0])  # => [ 49.4,  49.4,  72.2]   Wiki: 49.5%   49.3%   72.1%

值略有不同,因为方法 returns 0..255

范围内的整数 RGB 值

Rotem所说,我尝试转换为 RGB 的 HSI 值超出了 RGB 范围。
1670 万种颜色的所有其他 RGB 值都已正确转换。