Redimensionar imágenes es una tarea frecuente en automatización, comercio electrónico, blogs, aplicaciones web y procesamiento de datos. Con Pillow, la biblioteca de imágenes más utilizada en Python, puedes cambiar dimensiones, conservar proporciones, crear miniaturas, convertir formatos y procesar carpetas completas sin depender de programas gráficos.
Para complementar este tutorial, consulta las guías sobre archivos en Python, listas, funciones, try y except y automatización de tareas. Como referencias externas, revisa la documentación oficial de Pillow Image y la documentación oficial de pathlib.
Instalar Pillow
python -m pip install Pillow
El nombre del paquete es Pillow, pero la importación utiliza PIL por compatibilidad histórica:
from PIL import Image
Instálalo dentro de un entorno virtual para evitar conflictos entre proyectos.
Abrir una imagen
from PIL import Image
with Image.open("foto.jpg") as imagen:
print(imagen.size)
print(imagen.mode)
print(imagen.format)
size devuelve una tupla con ancho y alto. mode describe los canales, por ejemplo RGB o RGBA, y format identifica JPEG, PNG u otro formato detectado.
Redimensionamiento básico
from PIL import Image
with Image.open("foto.jpg") as imagen:
nueva = imagen.resize((800, 600))
nueva.save("foto-800x600.jpg", quality=90)
resize() crea una imagen nueva. En este ejemplo se fuerza una proporción de 4:3. Si el archivo original tiene otra proporción, el contenido quedará estirado o comprimido.
Conservar la relación de aspecto
Para mantener la proporción, calcula la nueva altura a partir del ancho:
def redimensionar_por_ancho(imagen, ancho_objetivo):
ancho, alto = imagen.size
proporcion = ancho_objetivo / ancho
alto_objetivo = round(alto * proporcion)
return imagen.resize((ancho_objetivo, alto_objetivo), Image.Resampling.LANCZOS)
LANCZOS ofrece buena calidad cuando reduces fotografías. Pillow también dispone de otros filtros, como NEAREST, BILINEAR y BICUBIC.
Crear miniaturas
El método thumbnail() modifica la imagen para que quepa dentro de un cuadro máximo sin deformarla:
with Image.open("foto.jpg") as imagen:
imagen.thumbnail((400, 400), Image.Resampling.LANCZOS)
imagen.save("miniatura.jpg", quality=88)
Una imagen de 1200 por 800 terminaría en 400 por 267. Ninguna dimensión supera el límite indicado.
resize() frente a thumbnail()
Usa resize() cuando necesitas dimensiones exactas y controlas el cálculo de proporción. Usa thumbnail() cuando quieres encajar la imagen dentro de un tamaño máximo. Recuerda que thumbnail() modifica el objeto actual, mientras que resize() devuelve uno nuevo.
Recortar para dimensiones exactas
En ocasiones necesitas una miniatura cuadrada sin deformación. Primero ajusta la imagen para cubrir el área y después recorta el centro:
from PIL import Image, ImageOps
with Image.open("foto.jpg") as imagen:
cuadrada = ImageOps.fit(
imagen,
(500, 500),
method=Image.Resampling.LANCZOS,
centering=(0.5, 0.5),
)
cuadrada.save("foto-cuadrada.jpg", quality=90)
ImageOps.fit() evita franjas vacías y conserva la proporción, aunque elimina parte de los bordes.
Procesar transparencia
PNG puede incluir un canal alfa. JPEG no admite transparencia. Si conviertes una imagen RGBA directamente a JPEG, puedes recibir un error:
with Image.open("logo.png") as imagen:
if imagen.mode in {"RGBA", "LA"}:
fondo = Image.new("RGB", imagen.size, "white")
alfa = imagen.getchannel("A")
fondo.paste(imagen, mask=alfa)
fondo.save("logo.jpg", quality=90)
El fondo blanco reemplaza las áreas transparentes antes de guardar.
Corregir orientación EXIF
Las fotografías de móviles pueden guardar la orientación en metadatos. Sin corrección, una imagen puede aparecer girada:
from PIL import Image, ImageOps
with Image.open("movil.jpg") as imagen:
corregida = ImageOps.exif_transpose(imagen)
corregida.thumbnail((1200, 1200), Image.Resampling.LANCZOS)
corregida.save("movil-corregida.jpg", quality=90)
Aplica exif_transpose() antes del redimensionamiento.
Procesar una carpeta completa
from pathlib import Path
from PIL import Image, ImageOps, UnidentifiedImageError
ENTRADA = Path("imagenes-originales")
SALIDA = Path("imagenes-redimensionadas")
SALIDA.mkdir(parents=True, exist_ok=True)
EXTENSIONES = {".jpg", ".jpeg", ".png", ".webp"}
for ruta in ENTRADA.iterdir():
if not ruta.is_file() or ruta.suffix.lower() not in EXTENSIONES:
continue
destino = SALIDA / f"{ruta.stem}-web.webp"
try:
with Image.open(ruta) as imagen:
imagen = ImageOps.exif_transpose(imagen)
imagen.thumbnail((1600, 1600), Image.Resampling.LANCZOS)
imagen.save(destino, format="WEBP", quality=85, method=6)
except (OSError, UnidentifiedImageError) as error:
print(f"No se pudo procesar {ruta.name}: {error}")
El script filtra extensiones, crea la carpeta de salida, corrige orientación, conserva proporciones y convierte los resultados a WebP.
No sobrescribir originales
Durante las primeras pruebas, guarda siempre en otra carpeta. Sobrescribir imágenes originales puede causar pérdidas irreversibles, especialmente si reduces resolución o cambias compresión. Añade un modo de simulación para mostrar qué archivos serían procesados.
Evitar nombres duplicados
Dos carpetas distintas pueden contener archivos con el mismo nombre. Si recorres subdirectorios, conserva parte de la estructura original o genera un nombre único. Otra opción es comprobar si el destino ya existe:
if destino.exists():
print(f"Omitido: {destino.name}")
continue
Calidad y tamaño
En JPEG, una calidad entre 80 y 92 suele ofrecer un equilibrio razonable. Valores máximos producen archivos grandes sin una mejora visual proporcional. En WebP, prueba valores entre 75 y 90. La configuración ideal depende del tipo de imagen y del uso final.
Metadatos
Al guardar una imagen nueva, algunos metadatos pueden perderse. Esto puede ser beneficioso para privacidad, porque elimina información de cámara y ubicación, pero debes decidirlo conscientemente. Si necesitas conservar perfiles de color o datos EXIF, copia únicamente lo necesario.
Imágenes muy grandes
Pillow puede emitir advertencias ante imágenes con cantidades enormes de píxeles para proteger contra archivos maliciosos o bombas de descompresión. No desactives esa protección sin validar el origen, tamaño y memoria disponible.
Funciones reutilizables
def preparar_para_web(origen, destino, limite=(1600, 1600), calidad=85):
with Image.open(origen) as imagen:
imagen = ImageOps.exif_transpose(imagen)
imagen.thumbnail(limite, Image.Resampling.LANCZOS)
if imagen.mode not in {"RGB", "RGBA"}:
imagen = imagen.convert("RGB")
imagen.save(destino, format="WEBP", quality=calidad, method=6)
Separar la transformación en una función facilita pruebas, reutilización y cambios futuros.
Errores frecuentes
Los fallos más comunes son deformar la proporción, olvidar cerrar archivos, guardar RGBA como JPEG, ignorar EXIF, sobrescribir originales y utilizar rutas construidas manualmente. También es un error asumir que una extensión garantiza que el contenido es una imagen válida.
Pruebas recomendadas
Prueba imágenes horizontales, verticales, cuadradas, transparentes, con orientación EXIF, formatos diferentes y archivos corruptos. Comprueba que ninguna dimensión supere el límite, que las proporciones se mantengan y que los originales permanezcan intactos.
Conclusión
Pillow permite crear flujos completos de preparación de imágenes con pocas dependencias. Utiliza thumbnail() para límites máximos, ImageOps.fit() para recortes exactos y exif_transpose() para fotografías correctamente orientadas. Procesa los archivos en una carpeta separada, valida entradas y elige calidad mediante pruebas reales, no por valores máximos.






