登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python colorsys 怎样在 RGB 与 HLS 之间转换

来源:17golang原创

时间:2026-10-30 19:42:56 482浏览 收藏

如果手里拿的是常见的 (R, G, B) 三通道整数,Python 标准库 colorsys 的正确用法是:先把每个通道除以 255 归一化,再调用 rgb_to_hls;需要显示或保存时,使用 hls_to_rgb 转回 0 到 1 的 RGB,最后再裁剪、四舍五入到 0 到 255。

最容易出错的地方有两个:colorsys 接受的是 0 到 1 的浮点数,HLS 元组顺序是 (h, l, s),不是 HSV 的 (h, s, v)。

官方文档:https://docs.python.org/3/library/colorsys.html

先分清 colorsys 的数值范围

colorsys 同时提供 RGB、YIQ、HLS 和 HSV 之间的双向转换。对 RGB、HLS、HSV 来说,坐标都使用 0 到 1 的浮点数;因此网页颜色常见的 255 不能直接作为参数传入。

HLS 的三个位置分别是色相 h、亮度 l、饱和度 s。其中色相也是 0 到 1 的归一化坐标,如果业务界面需要角度,可以把它乘以 360;亮度和饱和度则继续保持 0 到 1。

RGB 0 到 255 归一化后通过 rgb_to_hls 得到 HLS h l s 的静态关系说明图
图1:RGB 到 HLS 的数值范围与坐标关系说明图,不是运行截图。

最小配方:RGB 转 HLS

先写一个只负责换算的函数,把外部常见的 8 位 RGB 和 colorsys 的内部坐标隔离开。这样后面无论颜色来自表单、接口还是图片元数据,都不会把两种尺度混在一起。

import colorsys


def rgb255_to_hls(rgb):
    # 把外部常见的 0 到 255 通道转换为 colorsys 使用的 0 到 1 浮点数。
    normalized = tuple(channel / 255 for channel in rgb)
    # HLS 的返回顺序固定是 hue、lightness、saturation。
    return colorsys.rgb_to_hls(*normalized)


color_hls = rgb255_to_hls((52, 152, 219))
# h 是 0 到 1 的色相坐标,乘 360 后更适合在角度界面中展示。
print(color_hls[0] * 360, color_hls[1], color_hls[2])

这个配方的关键不是把除法写在一行,而是明确边界:函数输入是 0 到 255,函数输出是 (h, l, s)。调用方如果需要色相角度,只在展示层转换,不要把角度再传回 hls_to_rgb。

HLS 里的亮度和饱和度怎样调整

HLS 适合表达“保持大致色相,只把颜色调亮一点”这类操作。可以先解包元组,再限制调整后的亮度和饱和度仍在 0 到 1。色相通常按环形坐标处理,超过 1 或小于 0 时用取模回到这个范围。

def shift_hls(hls, lightness_delta=0.0, saturation_delta=0.0):
    h, l, s = hls
    # 色相是环形坐标,取模可以处理超过 1 或小于 0 的情况。
    h = h % 1.0
    # 亮度和饱和度是区间值,调整后要限制在合法范围内。
    l = min(1.0, max(0.0, l + lightness_delta))
    s = min(1.0, max(0.0, s + saturation_delta))
    return h, l, s


brighter = shift_hls(color_hls, lightness_delta=0.08)
# brighter 仍然是 colorsys 可以接受的 (h, l, s) 三元组。
print(brighter)

这里的“限制”是业务层保护,不是 colorsys 额外提供的校验器。把参数集中在一个小函数里,能避免多个页面分别处理边界,最后出现同一颜色在不同页面亮度不一致的情况。

调整 HLS 后怎样转回 RGB

hls_to_rgb 返回的三个通道仍然是 0 到 1 的浮点数。要交给 CSS、JSON 接口或图像库时,再统一乘以 255,并用 round 得到整数。乘法前后都保留裁剪,可以防止浮点误差把边界推到合法范围之外。

HLS 参数调整后通过 hls_to_rgb 和 clip round 转回 0 到 255 RGB 的静态关系说明图
图2:HLS 回到 8 位 RGB 的静态关系说明图,不是运行截图。
def hls_to_rgb255(hls):
    h, l, s = hls
    # hls_to_rgb 要求 h、l、s 都是 0 到 1 的坐标,而不是色相角度。
    rgb01 = colorsys.hls_to_rgb(h % 1.0, l, s)
    # 裁剪并四舍五入,得到 CSS 或接口常用的 8 位整数通道。
    return tuple(round(min(1.0, max(0.0, channel)) * 255) for channel in rgb01)


rgb255 = hls_to_rgb255(brighter)
print(rgb255)

如果你要生成 CSS,可以把结果拼成 rgb(r g b);如果接口约定的是小数 RGB,则不要做最后的乘 255,直接保留 rgb01 即可。

HLS 和 HSV 不要把位置顺序混用

同一个模块里还有 rgb_to_hsv 和 hsv_to_rgb。HLS 使用 (h, l, s),HSV 使用 (h, s, v),第二、第三个值的含义不同。把 HSV 元组直接传给 HLS 的逆转换,代码通常不会报错,却会得到明显不同的颜色。

目标调用返回顺序坐标范围
RGB 转 HLSrgb_to_hls(r, g, b)h, l, s都在 0 到 1
HLS 转 RGBhls_to_rgb(h, l, s)r, g, b都在 0 到 1
RGB 转 HSVrgb_to_hsv(r, g, b)h, s, v都在 0 到 1

几个实际使用时容易踩到的坑

  • 把 0 到 255 直接传入。先在入口归一化,返回显示值时再反向换算。
  • 把色相角度传给逆函数。界面上的 180 度要先除以 360,才能成为 colorsys 的 h。
  • 混淆 HLS 和 HSV 的顺序。看到第二个值时先确认它是 lightness 还是 saturation。
  • 忽略灰度色。当饱和度接近 0 时,色相对视觉颜色区分的作用很弱,业务上不应依赖灰度色的 h 值做分类。
  • 直接截断浮点数。显示层优先使用 round,并在乘 255 前后保持 0 到 1 的边界保护。

可以直接复用的完整片段

如果项目只需要一条稳定的 RGB/HLS 往返路径,可以把归一化、调整和反归一化收拢成下面这个小工具。它不负责读取图片文件,输入输出也保持为普通 Python 元组,便于在 Web 接口或颜色选择器中复用。

import colorsys


def rgb255_to_hls(rgb):
    # 统一把 8 位 RGB 通道送入 0 到 1 的标准库坐标。
    r, g, b = (channel / 255 for channel in rgb)
    return colorsys.rgb_to_hls(r, g, b)


def hls_to_rgb255(hls):
    h, l, s = hls
    # 亮度和饱和度由调用方控制,色相按环形坐标归一化。
    rgb01 = colorsys.hls_to_rgb(h % 1.0, l, s)
    # 将标准库输出转换为可显示的整数 RGB,并抵抗边界浮点误差。
    return tuple(round(min(1.0, max(0.0, value)) * 255) for value in rgb01)


original = (52, 152, 219)
hls = rgb255_to_hls(original)
# 只调整亮度,保持色相和饱和度的业务含义不变。
adjusted_hls = (hls[0], min(1.0, hls[1] + 0.08), hls[2])
adjusted_rgb = hls_to_rgb255(adjusted_hls)
print(adjusted_rgb)

这套写法的边界很清楚:输入输出层使用 0 到 255,转换层使用 0 到 1,HLS 内部始终按 h、l、s 解包。后续如果换成 HSV,只需要同时替换函数名和元组语义,不要只改一个调用点。

相关问题

colorsys 的色相为什么不是 0 到 360?

它把色相也表示为 0 到 1 的归一化坐标。需要角度时在展示边界乘以 360,传回转换函数前再除以 360。

能不能把十六进制颜色直接传给 colorsys?

不能直接传入。先把十六进制拆成三个 0 到 255 的通道,再按本文的归一化方式转换。

只想调亮颜色,为什么不直接给 RGB 每个通道加值?

直接改 RGB 会同时改变颜色的综合色相和饱和度。先转到 HLS,可以把亮度调整表达得更清楚,再统一转回显示通道。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>