La función cv2.cvtColor()
cv2.cvtColor() es la función de OpenCV para convertir una imagen de un espacio de color a otro. Acepta la imagen de entrada y un código de conversión de la forma cv2.COLOR_ORIGEN2DESTINO.
import cv2
imagen = cv2.imread("foto.jpg")
# BGR a escala de grises
gris = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)
# BGR a HSV
hsv = cv2.cvtColor(imagen, cv2.COLOR_BGR2HSV)
# BGR a RGB (necesario para Matplotlib)
rgb = cv2.cvtColor(imagen, cv2.COLOR_BGR2RGB)
print(f"Original BGR: {imagen.shape}") # (alto, ancho, 3)
print(f"Gris: {gris.shape}") # (alto, ancho)
print(f"HSV: {hsv.shape}") # (alto, ancho, 3)
print(f"RGB: {rgb.shape}") # (alto, ancho, 3)
Espacio BGR — el nativo de OpenCV
OpenCV almacena las imágenes en color en el orden Azul-Verde-Rojo (BGR). Es el formato de trabajo habitual en OpenCV.
| Canal | Índice | Rango | |-------|--------|-------| | Blue (Azul) | 0 | 0–255 | | Green (Verde) | 1 | 0–255 | | Red (Rojo) | 2 | 0–255 |
import cv2
import numpy as np
# Crear colores puros en BGR
rojo = np.array([[[0, 0, 255]]], dtype=np.uint8) # R=255
verde = np.array([[[0, 255, 0]]], dtype=np.uint8) # G=255
azul = np.array([[[255, 0, 0]]], dtype=np.uint8) # B=255
blanco = np.array([[[255, 255, 255]]], dtype=np.uint8)
negro = np.array([[[0, 0, 0]]], dtype=np.uint8)
# Imagen de muestra de colores
demo = np.zeros((100, 500, 3), dtype=np.uint8)
demo[:, 0:100] = (0, 0, 255) # Rojo
demo[:, 100:200] = (0, 255, 0) # Verde
demo[:, 200:300] = (255, 0, 0) # Azul
demo[:, 300:400] = (0, 255, 255) # Amarillo (R+G)
demo[:, 400:500] = (255, 0, 255) # Magenta (R+B)
cv2.imwrite("colores_bgr.png", demo)
BGR → RGB (para Matplotlib)
Matplotlib y la mayoría de bibliotecas Python usan RGB, no BGR. La conversión es esencial para visualizar correctamente.
import cv2
import matplotlib.pyplot as plt
img_bgr = cv2.imread("foto.jpg")
# Sin conversión: colores incorrectos (R y B intercambiados)
# Con conversión: colores correctos
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
ax1.imshow(img_bgr)
ax1.set_title("BGR directo (colores incorrectos)")
ax2.imshow(img_rgb)
ax2.set_title("Convertido a RGB (correcto)")
for ax in [ax1, ax2]:
ax.axis("off")
plt.show()
Escala de grises
La conversión a escala de grises combina los tres canales BGR usando una fórmula ponderada que tiene en cuenta la percepción humana del brillo:
Gris = 0.299·R + 0.587·G + 0.114·B
import cv2
import numpy as np
img = cv2.imread("foto.jpg")
# Método OpenCV (recomendado)
gris_opencv = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Método NumPy (aproximado, sin la ponderación perceptual)
b, g, r = img[:,:,0], img[:,:,1], img[:,:,2]
gris_manual = (0.114*b + 0.587*g + 0.299*r).astype(np.uint8)
# Las imágenes en grises pueden mostrarse con colormaps
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0].set_title("Original")
axes[1].imshow(gris_opencv, cmap="gray")
axes[1].set_title("Escala de grises")
axes[2].imshow(gris_opencv, cmap="jet")
axes[2].set_title("Escala de grises (colormap jet)")
for ax in axes:
ax.axis("off")
plt.show()
Espacio HSV — Tono, Saturación, Valor
El espacio HSV (Hue-Saturation-Value) separa la información de color (tono) de la intensidad, lo que lo hace ideal para segmentar objetos por color de forma robusta ante cambios de iluminación.
| Canal | Descripción | Rango en OpenCV | |-------|-------------|-----------------| | H (Hue / Tono) | Color puro | 0–179 (no 0-360) | | S (Saturation / Saturación) | Pureza del color | 0–255 | | V (Value / Valor) | Brillo | 0–255 |
Importante: En OpenCV, el canal Hue va de 0 a 179 (no 0 a 360 como en otras representaciones), ya que se codifica en un byte.
import cv2
import numpy as np
img = cv2.imread("foto.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 4, figsize=(18, 5))
axes[0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0].set_title("Original BGR")
axes[1].imshow(h, cmap="hsv")
axes[1].set_title("Canal H (Tono) 0-179")
axes[2].imshow(s, cmap="gray")
axes[2].set_title("Canal S (Saturación)")
axes[3].imshow(v, cmap="gray")
axes[3].set_title("Canal V (Valor/Brillo)")
for ax in axes: ax.axis("off")
plt.show()
Segmentar por color con inRange en HSV
import cv2
import numpy as np
img = cv2.imread("foto.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Definir rango de color naranja en HSV
# Naranja: H entre 5 y 25, S>100, V>100
bajo_naranja = np.array([5, 100, 100])
alto_naranja = np.array([25, 255, 255])
mascara = cv2.inRange(hsv, bajo_naranja, alto_naranja)
# Aplicar la máscara a la imagen original
resultado = cv2.bitwise_and(img, img, mask=mascara)
cv2.imwrite("segmentado_naranja.png", resultado)
# Referencia de rangos HSV comunes en OpenCV
rangos_hsv = {
"Rojo (bajo)": ([0, 120, 70], [10, 255, 255]),
"Rojo (alto)": ([170, 120, 70], [180, 255, 255]),
"Verde": ([36, 100, 100], [86, 255, 255]),
"Azul": ([94, 80, 2], [126, 255, 255]),
"Amarillo": ([25, 50, 70], [35, 255, 255]),
"Naranja": ([5, 100, 100], [25, 255, 255]),
"Blanco": ([0, 0, 200], [180, 30, 255]),
"Negro": ([0, 0, 0], [180, 255, 30]),
}
Espacio HLS — Tono, Luminosidad, Saturación
HLS (Hue-Lightness-Saturation) es similar a HSV pero separa la luminosidad (Lightness) en lugar del Valor. Es útil en aplicaciones donde el brillo ambiental varía mucho.
| Canal | Descripción | Rango en OpenCV | |-------|-------------|-----------------| | H (Hue) | Tono | 0–179 | | L (Lightness) | Luminosidad | 0–255 | | S (Saturation) | Saturación | 0–255 |
import cv2
img = cv2.imread("foto.jpg")
hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
h, l, s = cv2.split(hls)
print(f"Canal H — min: {h.min()}, max: {h.max()}")
print(f"Canal L — min: {l.min()}, max: {l.max()}")
print(f"Canal S — min: {s.min()}, max: {s.max()}")
Espacio LAB — Luminosidad, A, B
El espacio LAB (o L*a*b*) separa completamente la luminosidad (L) de los componentes de color (A: verde-magenta, B: azul-amarillo). Es perceptualmente uniforme, lo que lo hace ideal para mejorar el contraste sin alterar los colores.
import cv2
import numpy as np
img = cv2.imread("foto.jpg")
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
# Mejorar contraste solo en el canal de luminosidad con CLAHE
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
l_mejorado = clahe.apply(l)
# Fusionar y reconvertir
lab_mejorado = cv2.merge([l_mejorado, a, b])
resultado = cv2.cvtColor(lab_mejorado, cv2.COLOR_LAB2BGR)
cv2.imwrite("contraste_mejorado.png", resultado)
Resumen de conversiones más usadas
import cv2
img = cv2.imread("foto.jpg")
# Tabla de conversiones más habituales
conversiones = {
"BGR → RGB": cv2.COLOR_BGR2RGB,
"BGR → Gris": cv2.COLOR_BGR2GRAY,
"BGR → HSV": cv2.COLOR_BGR2HSV,
"BGR → HLS": cv2.COLOR_BGR2HLS,
"BGR → LAB": cv2.COLOR_BGR2LAB,
"BGR → YCrCb": cv2.COLOR_BGR2YCrCb,
"HSV → BGR": cv2.COLOR_HSV2BGR,
"Gris → BGR": cv2.COLOR_GRAY2BGR,
}
for nombre, codigo in conversiones.items():
entrada = img if "HSV" not in nombre.split("→")[0] else cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
if "Gris →" in nombre:
entrada = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
convertida = cv2.cvtColor(entrada, codigo)
print(f"{nombre:20s} → shape: {convertida.shape}")
# Ver todos los códigos disponibles
todos_los_codigos = [x for x in dir(cv2) if x.startswith("COLOR_")]
print(f"\nTotal de códigos de conversión disponibles: {len(todos_los_codigos)}")
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, OpenCV es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de OpenCV
Explora más contenido relacionado con OpenCV y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender la diferencia entre los espacios de color BGR, RGB, HSV, HLS y LAB. Convertir imágenes entre espacios de color con cv2.cvtColor(). Conocer los rangos de valores de cada canal en HSV y HLS. Usar el espacio HSV para segmentar objetos por color con cv2.inRange(). Separar luminosidad de color con el espacio LAB para mejorar contraste.