首页 > 解决方案 > HSI 到 RGB 颜色转换

问题描述

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

RGB 到 HSI 似乎工作正常。
但是,我在 HSI 到 RGB 方面遇到了困难。

我将使用 Ruby 编写,示例将使用 Ruby,但是如果您使用 JS/Python/etc 编写,我认为这也是可以理解的,因为它只是数学。
在线红宝石解释器。

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 只是为了比较它。

标签: algorithmcolorsrgbcolor-spacecolor-conversion

解决方案


您的实现看起来是正确的(假设 Wikipedia 是正确的)。

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

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

在大多数色彩空间转换公式中,有些值在源色彩空间的有效范围内,但超出了目标色彩空间的有效范围。
常见的解决方案是将结果裁剪到有效范围。

在某些情况下,存在未定义的值。
查看示例表的前 3 行。对于白色、黑色和灰色,
色调标记为N/A 。

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


我建议您测试示例表中的有效元组:

   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 编程语言的语法,但你的实现看起来是正确的。

这是一个与 Wikipedia 中的转换公式匹配的 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%

如您所见,结果与 Wikipedia 中的示例表匹配。


推荐阅读