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 值都已正确转换。
我正在尝试实现 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 值都已正确转换。