algorithm - 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
只是为了比较它。
解决方案
您的实现看起来是正确的(假设 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 中的示例表匹配。
推荐阅读
- python - Eikon API - 用于索引的 ek.get_data
- python - 更改嵌套类的静态属性
- integration-testing - 如何使用 Wiremock 模拟连接超时场景
- python - Selenium 驱动 Python 拖放,元素被放置在指针所在的位置而不是放置区域
- neo4j - Neo4J 密码:从扁平结果集中删除循环
- html - 如何在 html 选择表单中使用拉丁字符导航特定语言的字符?
- amazon-web-services - 集成 cognito 和 dynamoDB 表
- javascript - React-更改复选框以动态取消选中
- javascript - 如何将javascript对象存储为自定义类
- python - python write_videofile 导致黑屏视频