Las interfaces, gráficos, herramientas de imagen y sistemas de diseño necesitan transformar colores entre representaciones diferentes. colorsys en Python ofrece conversiones bidireccionales entre RGB y los modelos HSV, HLS y YIQ usando únicamente la biblioteca estándar.
El módulo es pequeño, pero exige atención a la escala: casi todos los componentes son floats entre cero y uno. Tampoco administra perfiles ICC, gamma, espacios Lab, canales alpha ni diferencias perceptuales. Esta guía explica normalización, paletas, rotación de matiz, cambios de saturación y brillo, validación, pruebas y límites de gestión profesional del color.
El contenido complementa nuestras guías sobre Decimal, fractions, statistics, filecmp y mimetypes.
Modelos disponibles
El módulo convierte RGB a tres sistemas:
- HSV: hue, saturation y value;
- HLS: hue, lightness y saturation;
- YIQ: luminancia y dos componentes de crominancia.
Las funciones inversas convierten cada modelo nuevamente a RGB.
Escala normalizada
Los componentes RGB, HSV y HLS normalmente van de cero a uno. Esto es diferente del RGB habitual de 8 bits, que usa 0 a 255.
def rgb_255_a_unitario(r, g, b):
return r / 255, g / 255, b / 255
def rgb_unitario_a_255(r, g, b):
return tuple(round(c * 255) for c in (r, g, b))Normaliza en la entrada y vuelve a enteros solamente al producir la salida.
RGB a HSV
import colorsys
rgb = rgb_255_a_unitario(51, 102, 102)
h, s, v = colorsys.rgb_to_hsv(*rgb)
print(h, s, v)Hue representa una posición en el círculo cromático, saturation la intensidad y value se basa en el componente RGB mayor.
HSV a RGB
r, g, b = colorsys.hsv_to_rgb(h, s, v)
print(rgb_unitario_a_255(r, g, b))Una ida y vuelta puede producir pequeñas diferencias por punto flotante y redondeo a 8 bits.
Hue es circular
Cero y uno representan la misma dirección. Usa módulo al rotar.
nuevo_h = (h + 30 / 360) % 1.0Sumar 30 grados equivale a añadir 30/360 en la escala normalizada.
Crear una paleta
def paleta(h_inicial, cantidad):
colores = []
for indice in range(cantidad):
h = (h_inicial + indice / cantidad) % 1.0
colores.append(colorsys.hsv_to_rgb(h, 0.7, 0.9))
return coloresDistribuir matices por igual no garantiza diferencia perceptual, contraste ni accesibilidad.
Cambiar saturación
def cambiar_saturacion(rgb, factor):
h, s, v = colorsys.rgb_to_hsv(*rgb)
s = min(1.0, max(0.0, s * factor))
return colorsys.hsv_to_rgb(h, s, v)Factor cero produce gris. Los valores mayores intensifican hasta el límite. Decide si el clipping es aceptable.
Cambiar value
def cambiar_valor(rgb, delta):
h, s, v = colorsys.rgb_to_hsv(*rgb)
v = min(1.0, max(0.0, v + delta))
return colorsys.hsv_to_rgb(h, s, v)Value no es luminancia perceptual. Incrementarlo no genera la misma sensación de brillo en todos los colores.
RGB a HLS
h, l, s = colorsys.rgb_to_hls(*rgb)La función devuelve H, L, S. Muchas herramientas web usan la sigla HSL y muestran hue, saturation, lightness. Confundir el orden produce colores incorrectos.
Cambiar lightness
def aclarar_hls(rgb, cantidad):
h, l, s = colorsys.rgb_to_hls(*rgb)
l = min(1.0, l + cantidad)
return colorsys.hls_to_rgb(h, l, s)HLS es práctico para variantes claras y oscuras, pero tampoco es perceptualmente uniforme.
HSV frente a HLS
HSV usa value y HLS usa lightness. Los modelos reorganizan RGB de manera diferente. Saturación del 100% no tiene exactamente el mismo significado visual.
HSV es común en selectores e intensidad; HLS puede resultar intuitivo para variantes claras y oscuras.
Modelo YIQ
YIQ fue utilizado por la televisión NTSC. Y representa luminancia aproximada, mientras I y Q transportan crominancia.
y, i, q = colorsys.rgb_to_yiq(*rgb)
r, g, b = colorsys.yiq_to_rgb(y, i, q)Y permanece entre cero y uno, pero I y Q pueden ser negativos. No apliques clamp de cero a uno a esos componentes.
Colores hexadecimales
def hex_a_rgb(color):
valor = color.lstrip("#")
if len(valor) != 6:
raise ValueError("usa #RRGGBB")
return tuple(int(valor[i:i+2], 16) / 255 for i in (0, 2, 4))
def rgb_a_hex(rgb):
r, g, b = rgb_unitario_a_255(*rgb)
return f"#{r:02X}{g:02X}{b:02X}"Valida el formato y define si se permiten versiones abreviadas o alpha.
Clamping seguro
def clamp(valor, minimo=0.0, maximo=1.0):
return min(maximo, max(minimo, valor))El clamp es útil después de cálculos controlados, no para ocultar una entrada API inválida que debería generar excepción.
Precisión flotante
Valores como 0.30000000000000004 son normales. Compara con tolerancia.
import math
assert math.isclose(restaurado, original, abs_tol=1e-9)Mantén floats durante el cálculo y redondea una vez al producir 8 bits.
Colores acromáticos
Cuando la saturación es cero, hue no tiene significado visible. La función devuelve un número, normalmente cero.
Un selector que conserve intención puede guardar el hue anterior por separado.
Alpha no está soportado
colorsys trabaja con tres componentes. Conserva alpha fuera de la conversión.
r, g, b, a = rgba
h, s, v = colorsys.rgb_to_hsv(r, g, b)
# a se mantieneGamma y espacio RGB
Los valores de pantalla suelen estar codificados en sRGB. Colorsys aplica fórmulas directamente a los números; no lineariza sRGB ni convierte perfiles ICC.
Para mezcla física, iluminación, impresión o imagen científica utiliza una biblioteca con gestión de color.
Contraste y accesibilidad
Diferentes matices HSV no garantizan contraste suficiente. Calcula contraste con fórmulas de accesibilidad y prueba deficiencias visuales, temas claros y oscuros.
No comuniques estados solo mediante matiz; añade texto, iconos, formas o patrones.
Color complementario
def complementario(rgb):
h, s, v = colorsys.rgb_to_hsv(*rgb)
return colorsys.hsv_to_rgb((h + 0.5) % 1.0, s, v)El opuesto matemático no siempre es la mejor elección de diseño. Úsalo como punto de partida.
Colores análogos
def analogos(rgb, grados=30):
h, s, v = colorsys.rgb_to_hsv(*rgb)
desplazamiento = grados / 360
return [
colorsys.hsv_to_rgb((h - desplazamiento) % 1, s, v),
rgb,
colorsys.hsv_to_rgb((h + desplazamiento) % 1, s, v),
]Procesar muchas cores
El módulo realiza conversiones escalares. Los loops Python pueden ser lentos para millones de píxeles. Usa bibliotecas vectorizadas para imágenes completas.
Para paletas, temas y configuración, la sencillez estándar es excelente.
Pruebas de ida y vuelta
def test_hsv_round_trip():
original = (0.2, 0.4, 0.7)
hsv = colorsys.rgb_to_hsv(*original)
restaurado = colorsys.hsv_to_rgb(*hsv)
for a, b in zip(original, restaurado):
assert math.isclose(a, b, abs_tol=1e-9)Prueba negro, blanco, grises, primarios, límites y valores próximos a cero y uno.
Validar entrada
def validar_rgb(rgb):
if len(rgb) != 3:
raise ValueError("RGB necesita tres componentes")
if not all(0.0 <= c <= 1.0 for c in rgb):
raise ValueError("componente fuera del rango")Rechaza NaN e infinito con math.isfinite().
Errores frecuentes
- Pasar 0 a 255 sin normalizar.
- Confundir HLS con el orden de una API HSL.
- Limitar I y Q entre cero y uno.
- Redondear en cada paso.
- Perder alpha accidentalmente.
- Usar HSV como modelo perceptual.
- Ignorar perfiles y gamma en flujos profesionales.
- Depender solo del matiz para accesibilidad.
Buenas prácticas
- Normaliza en los límites de la aplicación.
- Mantén floats durante los cálculos.
- Usa módulo para hue.
- Valida finitud y rango.
- Conserva alpha por separado.
- Mide contraste independientemente.
- Usa bibliotecas vectorizadas para imágenes grandes.
- Documenta modelo y escala de cada función.
Conclusión
colorsys en Python ofrece conversiones directas entre RGB, HSV, HLS y YIQ. Es ideal para paletas, temas, pequeñas herramientas gráficas y transformaciones de configuración.
Úsalo entendiendo sus límites: valores normalizados, modelos no uniformes perceptualmente y ausencia de gestión de perfiles, gamma o alpha. Consulta la documentación oficial de colorsys y las orientaciones de contraste de W3C.







