Pillow
Pillow es el fork mantenido de PIL, la biblioteca estándar para abrir, convertir, redimensionar, recortar y guardar imágenes en pipelines de Python, manejadores de carga web y preprocesamiento de ML.
Receta
from PIL import Image
with Image.open("photo.jpg") as img:
img = ImageOps.exif_transpose(img)
img.thumbnail((800, 800))
img.save("preview.webp", format="WEBP", quality=85)Cuándo usar esto:
- Generación de avatares/miniaturas de usuario en aplicaciones web
- Conversión por lotes de PNG/JPEG/WEBP para CDNs
- Preprocesamiento de imágenes antes del entrenamiento con PyTorch/torchvision
- Lectura de dimensiones y metadatos sin cargar la decodificación completa en la UI
Ejemplo de Trabajo
from __future__ import annotations
from io import BytesIO
from pathlib import Path
from PIL import Image, ImageOps, UnidentifiedImageError
MAX_BYTES = 5 * 1024 * 1024
ALLOWED = {"JPEG", "PNG", "WEBP"}
def make_thumbnail(data: bytes, max_edge: int = 256) -> bytes:
if len(data) > MAX_BYTES:
raise ValueError("file too large")
try:
with Image.open(BytesIO(data)) as img:
if img.format not in ALLOWED:
raise ValueError(f"unsupported format {img.format}")
img = ImageOps.exif_transpose(img)
img = img.convert("RGB")
img.thumbnail((max_edge, max_edge))
out = BytesIO()
img.save(out, format="WEBP", quality=80, method=6)
return out.getvalue()
except UnidentifiedImageError as exc:
raise ValueError("not an image") from exc
if __name__ == "__main__":
sample = Path("photo.jpg").read_bytes()
Path("thumb.webp").write_bytes(make_thumbnail(sample))Lo que esto demuestra:
- El límite de tamaño antes de la decodificación mitiga las bombas de descompresión
exif_transposecorrige las fotos de teléfono rotadasconvert("RGB")normaliza los modos antes de la exportación a WEBP- Pipeline
BytesIOen memoria para manejadores de API - Lista blanca explícita de formatos permitidos
Inmersión Profunda
Cómo Funciona
- Image.open - Carga perezosa; la decodificación ocurre en el primer acceso a píxeles.
- Modos -
RGB,RGBA,L(escala de grises),P(paleta) - convertir antes de formatos que no soportan alfa. - Transformaciones -
resize,thumbnail,crop,rotate, módulofilterpara desenfoque/nitidez. - Guardado - Formato a partir de la extensión o argumento
format=; la configuración de calidad varía según el códec.
Operaciones Comunes
| Tarea | API |
|---|---|
| Caja delimitadora máxima | thumbnail((w, h)) |
| Dimensiones exactas | resize((w, h), Image.Resampling.LANCZOS) |
| Eliminar EXIF | img.getexif().clear() o guardar sin exif |
| Dibujar texto | ImageDraw.Draw(img).text(...) |
Notas de Python
# Verificar dimensiones sin decodificación completa (donde sea compatible)
with Image.open(path) as img:
width, height = img.sizeTrampas
- Bombas de descompresión - Un PNG diminuto se expande a gigapíxeles. Solución: comprobaciones de bytes máximos y dimensiones máximas antes de la decodificación.
- Ignorar la orientación EXIF - Las miniaturas aparecen de lado. Solución:
ImageOps.exif_transpose. - RGBA a JPEG - El guardado falla o descarta el alfa de forma extraña. Solución:
convert("RGB")sobre un fondo blanco primero. - No cerrar archivos en Windows - Bloqueo de archivo al sobrescribir. Solución: gestores de contexto.
- Confiar en el Content-Type del cliente - Los atacantes falsifican el MIME. Solución: validar
img.formatdespués de abrir. - LANCZOS en imágenes enormes - Picos de memoria. Solución: primero crear miniatura, luego redimensionar opcionalmente.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
wand (ImageMagick) | Rasterización compleja de PDF/PSD/SVG | Dependencias más simples (se requiere binario de ImageMagick) |
opencv-python | Pipelines de CV, detección de rostros | Microservicio simple de miniaturas |
torchvision.transforms | Solo aumentación en tiempo de entrenamiento | Sanitización general de cargas |
| CDN de imágenes SaaS | Descargar transformaciones en el borde | Entornos aislados o cargas sensibles al costo |
Preguntas Frecuentes
¿Pillow lee fotos HEIC de iPhone?
Requiere el plugin pillow-heif; de lo contrario, convierte en el cliente o rechaza con un error claro.
¿Cómo preservo la transparencia en PNG?
Mantén el modo RGBA; no conviertas a RGB antes de guardar.
¿Puede Pillow ejecutarse en AWS Lambda?
Sí, con construcciones de capa delgadas; ten cuidado con el tamaño del paquete y las dependencias de bibliotecas nativas.
¿Cómo agrego una marca de agua a las imágenes?
Usa Image.alpha_composite con una capa de marca de agua PNG transparente.
¿Qué filtro de remuestreo para fotos?
Image.Resampling.LANCZOS para reducir; NEAREST para pixel art.
¿Cómo leo EXIF GPS de forma segura?
Elimina el EXIF de ubicación en las cargas de usuario antes de publicarlo; requisito de privacidad.
¿Pillow soporta AVIF?
Las versiones recientes de Pillow agregan AVIF si libavif está presente; verifica en la imagen de despliegue de destino.
¿Cómo pruebo sin fixtures de imagen?
Genera un PNG de color sólido de 10x10 en las pruebas mediante Image.new("RGB", (10, 10), "red").
¿Es seguro para hilos (thread-safe)?
No compartas objetos Image mutables entre hilos; abre por tarea.
¿Integrar con FastAPI UploadFile?
data = await file.read() luego pasa los bytes al pipeline BytesIO con límites de tamaño.
Relacionado
- PyTorch / Deep Learning Skill - pipelines de entrenamiento de imágenes
- Input Validation - seguridad de cargas
- FastAPI Basics - rutas de carga de archivos
- File and Directory Automation - procesamiento por lotes
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.