Lectura, visualización y escritura de imágenes con OpenCV

Básico
OpenCV
OpenCV
Actualizado: 18/04/2026

Leer imágenes con cv2.imread()

La función cv2.imread() carga una imagen desde disco y la devuelve como un array NumPy. Si la imagen no puede cargarse (ruta incorrecta, formato no soportado), devuelve None sin lanzar ninguna excepción, por lo que siempre es necesario verificar el resultado.

import cv2

# Lectura básica: devuelve array BGR uint8 o None
imagen = cv2.imread("foto.jpg")

# Siempre verificar que la carga fue exitosa
if imagen is None:
    print("ERROR: no se pudo cargar la imagen. Verifica la ruta.")
else:
    print(f"Imagen cargada: {imagen.shape}, dtype={imagen.dtype}")

Flags de lectura

El segundo argumento de imread() controla cómo se interpreta la imagen:

import cv2

# Color BGR (por defecto) — 3 canales
img_color = cv2.imread("foto.jpg", cv2.IMREAD_COLOR)
# Equivalente: cv2.imread("foto.jpg", 1)
print(f"Color: {img_color.shape}")  # (alto, ancho, 3)

# Escala de grises — 1 canal
img_gris = cv2.imread("foto.jpg", cv2.IMREAD_GRAYSCALE)
# Equivalente: cv2.imread("foto.jpg", 0)
print(f"Grises: {img_gris.shape}")  # (alto, ancho)

# Sin cambios: mantiene canal alfa si existe (BGRA)
img_rgba = cv2.imread("icono.png", cv2.IMREAD_UNCHANGED)
# Equivalente: cv2.imread("icono.png", -1)
print(f"Sin cambios: {img_rgba.shape}")  # (alto, ancho, 4) si tiene alfa

# Leer en float (valores 0.0-1.0 en lugar de 0-255)
img_float = cv2.imread("foto.jpg", cv2.IMREAD_COLOR | cv2.IMREAD_ANYDEPTH)

Gestión de rutas con os.path y pathlib

import cv2
import os
from pathlib import Path

# Con os.path — compatible con Python 2 y 3
ruta = os.path.join("imagenes", "vacaciones", "playa.jpg")
img = cv2.imread(ruta)

# Con pathlib — preferida en Python 3.4+
ruta_p = Path("imagenes") / "vacaciones" / "playa.jpg"
img_p = cv2.imread(str(ruta_p))

# Rutas absolutas
ruta_abs = Path.home() / "Imágenes" / "foto.jpg"
img_abs = cv2.imread(str(ruta_abs))

# Verificar que el archivo existe antes de leer
archivo = Path("foto.jpg")
if not archivo.exists():
    print(f"El archivo {archivo} no existe")
else:
    img = cv2.imread(str(archivo))

Visualizar imágenes

cv2.imshow() — ventana nativa

cv2.imshow() abre una ventana del sistema operativo para mostrar la imagen. Requiere un waitKey() para que la ventana permanezca visible.

import cv2

imagen = cv2.imread("foto.jpg")

# Mostrar en ventana nativa
cv2.imshow("Mi imagen", imagen)

# Esperar tecla indefinidamente (0) o N milisegundos
tecla = cv2.waitKey(0)
print(f"Tecla pulsada: {tecla}")

# Cerrar todas las ventanas OpenCV
cv2.destroyAllWindows()
import cv2

# Bucle de visualización interactiva
imagen = cv2.imread("foto.jpg")
cv2.namedWindow("Imagen", cv2.WINDOW_NORMAL)  # Ventana redimensionable
cv2.resizeWindow("Imagen", 800, 600)

cv2.imshow("Imagen", imagen)
while True:
    tecla = cv2.waitKey(1) & 0xFF
    if tecla == ord('q') or tecla == 27:  # 'q' o Escape para salir
        break

cv2.destroyAllWindows()

Matplotlib — recomendado para Jupyter

import cv2
import matplotlib.pyplot as plt

def mostrar_bgr(imagen, titulo="Imagen"):
    """Muestra una imagen BGR de OpenCV con Matplotlib."""
    imagen_rgb = cv2.cvtColor(imagen, cv2.COLOR_BGR2RGB)
    plt.figure(figsize=(8, 6))
    plt.imshow(imagen_rgb)
    plt.title(titulo)
    plt.axis("off")
    plt.show()

def mostrar_gris(imagen, titulo="Imagen en grises"):
    """Muestra una imagen en escala de grises."""
    plt.figure(figsize=(8, 6))
    plt.imshow(imagen, cmap="gray")
    plt.title(titulo)
    plt.axis("off")
    plt.colorbar(label="Intensidad")
    plt.show()

# Usar las funciones de utilidad
img = cv2.imread("foto.jpg")
mostrar_bgr(img, "Foto original")

img_gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mostrar_gris(img_gris, "Escala de grises")

Comparar varias imágenes en una figura

import cv2
import matplotlib.pyplot as plt

img = cv2.imread("foto.jpg")
img_gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_bordes = cv2.Canny(img_gris, 50, 150)
img_invertida = cv2.bitwise_not(img)

imagenes = [
    (img, "Original BGR"),
    (img_gris, "Escala de grises"),
    (img_bordes, "Bordes Canny"),
    (img_invertida, "Invertida"),
]

fig, axes = plt.subplots(2, 2, figsize=(14, 10))
for ax, (imagen, titulo) in zip(axes.flat, imagenes):
    if len(imagen.shape) == 3:
        ax.imshow(cv2.cvtColor(imagen, cv2.COLOR_BGR2RGB))
    else:
        ax.imshow(imagen, cmap="gray")
    ax.set_title(titulo, fontsize=12)
    ax.axis("off")

plt.suptitle("Comparación de procesamiento de imágenes", fontsize=14)
plt.tight_layout()
plt.show()

Guardar imágenes con cv2.imwrite()

cv2.imwrite() guarda un array NumPy como archivo de imagen. El formato se determina por la extensión del nombre de archivo.

import cv2
import numpy as np

img = cv2.imread("foto.jpg")
img_gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Guardar en PNG (sin pérdida)
cv2.imwrite("foto_gris.png", img_gris)

# Guardar en JPEG
cv2.imwrite("foto_copia.jpg", img)

# Verificar resultado: imwrite devuelve True si tuvo éxito
ok = cv2.imwrite("salida.png", img)
print(f"Guardado: {ok}")

Parámetros de calidad y compresión

import cv2

img = cv2.imread("foto.jpg")

# JPEG: calidad del 0 (peor) al 100 (mejor). Default: 95
cv2.imwrite("alta_calidad.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 95])
cv2.imwrite("baja_calidad.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 30])

# PNG: compresión del 0 (sin compresión) al 9 (máxima). Default: 3
cv2.imwrite("sin_compresion.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 0])
cv2.imwrite("max_compresion.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 9])

# WebP: calidad del 0 al 100
cv2.imwrite("imagen.webp", img, [cv2.IMWRITE_WEBP_QUALITY, 80])

Comparar tamaños de archivo según calidad

import cv2
import os

img = cv2.imread("foto.jpg")

calidades = [10, 30, 50, 70, 90, 95]
for q in calidades:
    nombre = f"calidad_{q}.jpg"
    cv2.imwrite(nombre, img, [cv2.IMWRITE_JPEG_QUALITY, q])
    tamanio = os.path.getsize(nombre) / 1024  # KB
    print(f"Calidad {q:3d}%: {tamanio:8.1f} KB")

Salida típica:

Calidad  10%:    156.3 KB
Calidad  30%:    312.7 KB
Calidad  50%:    487.2 KB
Calidad  70%:    720.1 KB
Calidad  90%: 1,521.6 KB
Calidad  95%: 2,183.4 KB

Formatos soportados

OpenCV admite los formatos más habituales:

| Formato | Extensión | Característica | |---------|-----------|----------------| | JPEG | .jpg, .jpeg | Compresión con pérdida, ideal para fotos | | PNG | .png | Sin pérdida, soporta transparencia (alfa) | | BMP | .bmp | Sin compresión, muy rápido de lectura | | TIFF | .tiff, .tif | Sin pérdida, soporte de 16 bits por canal | | WebP | .webp | Compresión moderna con y sin pérdida | | PBM/PGM/PPM | .pbm, .pgm, .ppm | Formatos portables simples |

import cv2

img = cv2.imread("foto.jpg")

# Guardar en múltiples formatos
formatos = {
    "bmp":  ("foto.bmp",  []),
    "png":  ("foto.png",  [cv2.IMWRITE_PNG_COMPRESSION, 3]),
    "webp": ("foto.webp", [cv2.IMWRITE_WEBP_QUALITY, 85]),
    "tiff": ("foto.tiff", []),
}

for fmt, (nombre, params) in formatos.items():
    cv2.imwrite(nombre, img, params)
    print(f"Guardado {nombre}")
Alan Sastre - Autor del tutorial

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

Leer imágenes con cv2.imread() y sus flags (COLOR, GRAYSCALE, UNCHANGED). Visualizar imágenes con cv2.imshow() en ventana nativa y con Matplotlib en Jupyter. Guardar imágenes con cv2.imwrite() con parámetros de calidad JPEG y compresión PNG. Manejar errores de lectura cuando el archivo no existe o la ruta es incorrecta. Trabajar con rutas de archivo usando os.path y pathlib.