Aprende a Manipular Imágenes con Pillow en Python: Una Guía para Procesamiento Digital
Este tutorial te guiará a través de la biblioteca Pillow en Python, una potente herramienta para el procesamiento de imágenes. Aprenderás desde la apertura y guardado de imágenes hasta la aplicación de transformaciones complejas, permitiéndote automatizar y mejorar tus tareas de edición.
La manipulación de imágenes es una tarea común en muchos campos, desde el desarrollo web y móvil hasta la ciencia de datos y la inteligencia artificial. Python, con su vasta colección de bibliotecas, ofrece una solución robusta para esta necesidad: Pillow. Pillow es una fork amigable de la legendaria biblioteca PIL (Python Imaging Library), que proporciona poderosas capacidades de procesamiento de imágenes.
En este tutorial, exploraremos las funcionalidades clave de Pillow, desde las operaciones básicas como abrir y guardar imágenes, hasta transformaciones más avanzadas como redimensionar, rotar, recortar y aplicar filtros.
🚀 ¿Por Qué Pillow?
Pillow es la elección de facto para la manipulación de imágenes en Python por varias razones:
- Facilidad de Uso: Su API es intuitiva y fácil de aprender.
- Amplias Funcionalidades: Soporta una gran variedad de formatos de imagen y ofrece numerosas operaciones de manipulación.
- Rendimiento: Está optimizada para trabajar eficientemente con imágenes.
- Comunidad Activa: Cuenta con una comunidad vibrante y documentación excelente.
🛠️ Instalación de Pillow
Antes de empezar a programar, necesitamos instalar la biblioteca Pillow. Abre tu terminal o línea de comandos y ejecuta el siguiente comando:
pip install Pillow
📖 Conceptos Básicos de Imágenes
Antes de sumergirnos en el código, es útil entender algunos conceptos fundamentales de las imágenes digitales:
- Modo (Mode): Describe el tipo y profundidad de píxeles de una imagen. Ejemplos comunes son
L(escala de grises),RGB(color verdadero),RGBA(color verdadero con transparencia),CMYK. - Tamaño (Size): Representado como una tupla
(ancho, alto)en píxeles. - Formato (Format): El tipo de archivo de imagen (JPEG, PNG, GIF, BMP, etc.).
🎯 Primeros Pasos: Abriendo y Guardando Imágenes
El primer paso en cualquier tarea de manipulación de imágenes es cargar la imagen en la memoria y, una vez modificada, guardarla de nuevo.
Abrir una Imagen
Usamos la función Image.open() para cargar una imagen. Asegúrate de tener una imagen de prueba en el mismo directorio que tu script de Python, o proporciona la ruta completa.
from PIL import Image
try:
# Reemplaza 'imagen_original.jpg' con la ruta de tu imagen
img = Image.open('imagen_original.jpg')
print(f"Formato: {img.format}")
print(f"Modo: {img.mode}")
print(f"Tamaño: {img.size}")
img.show() # Abre la imagen con el visor predeterminado del sistema
except FileNotFoundError:
print("Error: La imagen 'imagen_original.jpg' no se encontró.")
except Exception as e:
print(f"Ocurrió un error al abrir la imagen: {e}")
Guardar una Imagen
Para guardar una imagen modificada, usamos el método save(). Puedes especificar el formato de salida simplemente cambiando la extensión del archivo.
from PIL import Image
# Suponiendo que 'img' es una imagen abierta previamente
try:
img = Image.open('imagen_original.jpg')
img.save('imagen_copia.png') # Guarda como PNG
print("Imagen guardada como imagen_copia.png")
except FileNotFoundError:
print("Error: La imagen 'imagen_original.jpg' no se encontró.")
except Exception as e:
print(f"Ocurrió un error al guardar la imagen: {e}")
Tabla de Formatos Comunes Soportados:
| Formato | Extensión Común | Descripción |
|---|---|---|
| --- | --- | --- |
| JPEG | .jpg, .jpeg | Compresión con pérdida, ideal para fotografías. |
| PNG | .png | Compresión sin pérdida, soporta transparencia. |
| --- | --- | --- |
| GIF | .gif | Soporta animaciones y transparencia, ideal para gráficos simples. |
| BMP | .bmp | Formato sin compresión, grande. |
| --- | --- | --- |
| TIFF | .tif, .tiff | Alta calidad, usado en impresión. |
📏 Transformaciones Básicas de Imágenes
Pillow ofrece una gran variedad de transformaciones para modificar el aspecto y el tamaño de tus imágenes.
Redimensionar Imágenes
El método resize() te permite cambiar el tamaño de una imagen. Recibe una tupla (ancho, alto) como argumento.
from PIL import Image
try:
img = Image.open('imagen_original.jpg')
# Redimensionar a 300x200 píxeles
img_redimensionada = img.resize((300, 200))
img_redimensionada.save('imagen_redimensionada.jpg')
print("Imagen redimensionada y guardada.")
img_redimensionada.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
Rotar Imágenes
El método rotate() permite girar una imagen un número específico de grados. Puedes especificar un ángulo de rotación.
from PIL import Image
try:
img = Image.open('imagen_original.jpg')
# Rotar la imagen 90 grados en sentido horario
img_rotada = img.rotate(90)
img_rotada.save('imagen_rotada_90.jpg')
print("Imagen rotada 90 grados y guardada.")
img_rotada.show()
# Rotar con expansión (para que la imagen completa sea visible)
img_rotada_expandida = img.rotate(45, expand=True)
img_rotada_expandida.save('imagen_rotada_45_expandida.jpg')
print("Imagen rotada 45 grados (expandida) y guardada.")
img_rotada_expandida.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
Recortar Imágenes (Cropping)
El método crop() te permite extraer una sección rectangular de una imagen. Necesita una tupla de 4 valores (izquierda, arriba, derecha, abajo) para definir el área a recortar.
from PIL import Image
try:
img = Image.open('imagen_original.jpg')
ancho, alto = img.size
# Definir el área de recorte: un cuadrado de 100x100 en el centro
left = (ancho - 100) / 2
top = (alto - 100) / 2
right = (ancho + 100) / 2
bottom = (alto + 100) / 2
# Asegurarse de que las coordenadas sean enteros
area_recorte = (int(left), int(top), int(right), int(bottom))
img_recortada = img.crop(area_recorte)
img_recortada.save('imagen_recortada.jpg')
print("Imagen recortada y guardada.")
img_recortada.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
except Exception as e:
print(f"Ocurrió un error: {e}")
Voltear Imágenes (Flipping)
Puedes voltear una imagen horizontal o verticalmente usando transpose() junto con Image.FLIP_LEFT_RIGHT o Image.FLIP_TOP_BOTTOM.
from PIL import Image
try:
img = Image.open('imagen_original.jpg')
# Voltear horizontalmente
img_flip_h = img.transpose(Image.FLIP_LEFT_RIGHT)
img_flip_h.save('imagen_volteada_h.jpg')
print("Imagen volteada horizontalmente y guardada.")
img_flip_h.show()
# Voltear verticalmente
img_flip_v = img.transpose(Image.FLIP_TOP_BOTTOM)
img_flip_v.save('imagen_volteada_v.jpg')
print("Imagen volteada verticalmente y guardada.")
img_flip_v.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
✨ Aplicando Filtros y Mejoras
Pillow incluye el módulo ImageFilter que proporciona una variedad de filtros predefinidos para mejorar o modificar el aspecto visual de las imágenes.
Desenfoque (Blur)
El filtro BLUR es útil para suavizar una imagen o para efectos artísticos.
from PIL import Image, ImageFilter
try:
img = Image.open('imagen_original.jpg')
# Aplicar un filtro de desenfoque
img_blur = img.filter(ImageFilter.BLUR)
img_blur.save('imagen_desenfocada.jpg')
print("Imagen desenfocada y guardada.")
img_blur.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
Nitidez (Sharpen)
Para realzar los bordes y detalles de una imagen, puedes usar el filtro SHARPEN.
from PIL import Image, ImageFilter
try:
img = Image.open('imagen_original.jpg')
# Aplicar un filtro de nitidez
img_sharpen = img.filter(ImageFilter.SHARPEN)
img_sharpen.save('imagen_nitida.jpg')
print("Imagen nítida y guardada.")
img_sharpen.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
Otros Filtros Comunes
Aquí hay algunos otros filtros populares disponibles en ImageFilter:
CONTOUR: Encuentra los contornos de la imagen.EMBOSS: Crea un efecto de relieve.FIND_EDGES: Detecta los bordes en la imagen.SMOOTH: Suaviza la imagen.
Ajuste de Color, Brillo, Contraste y Nitidez (ImageEnhance)
El módulo ImageEnhance permite realizar ajustes más finos en propiedades como el brillo, el contraste o la saturación.
from PIL import Image, ImageEnhance
try:
img = Image.open('imagen_original.jpg')
# Ajustar el brillo (factor > 1 para más brillo, < 1 para menos)
enhancer_brillo = ImageEnhance.Brightness(img)
img_brillo = enhancer_brillo.enhance(1.5) # Aumentar brillo 50%
img_brillo.save('imagen_brillo.jpg')
print("Brillo ajustado.")
img_brillo.show()
# Ajustar el contraste
enhancer_contraste = ImageEnhance.Contrast(img)
img_contraste = enhancer_contraste.enhance(1.8) # Aumentar contraste 80%
img_contraste.save('imagen_contraste.jpg')
print("Contraste ajustado.")
img_contraste.show()
# Ajustar la saturación (color)
enhancer_color = ImageEnhance.Color(img)
img_saturacion = enhancer_color.enhance(2.0) # Duplicar saturación
img_saturacion.save('imagen_saturacion.jpg')
print("Saturación ajustada.")
img_saturacion.show()
except FileNotFoundError:
print("Error: Asegúrate de que 'imagen_original.jpg' existe.")
🎨 Dibujando Formas y Texto en Imágenes
Pillow también te permite dibujar formas geométricas y añadir texto a tus imágenes usando el módulo ImageDraw.
Dibujar Rectángulos y Círculos
from PIL import Image, ImageDraw
# Crear una imagen en blanco (fondo blanco, RGB, 400x300)
img_dibujo = Image.new('RGB', (400, 300), color = 'white')
draw = ImageDraw.Draw(img_dibujo)
# Dibujar un rectángulo rojo
# Coordenadas: (x1, y1, x2, y2)
draw.rectangle((50, 50, 200, 150), fill='red', outline='black', width=2)
# Dibujar un círculo azul
# Coordenadas: (x1, y1, x2, y2) para la caja delimitadora
draw.ellipse((250, 100, 350, 200), fill='blue', outline='green', width=3)
img_dibujo.save('imagen_con_formas.png')
print("Imagen con formas dibujadas guardada.")
img_dibujo.show()
Añadir Texto
Para añadir texto, también usas ImageDraw, pero necesitas una fuente. Pillow puede cargar fuentes TrueType (.ttf).
from PIL import Image, ImageDraw, ImageFont
# Crear una imagen en blanco
img_texto = Image.new('RGB', (500, 200), color = 'lightblue')
draw = ImageDraw.Draw(img_texto)
# Cargar una fuente (asegúrate de que la ruta sea correcta o usa una fuente del sistema)
# En Linux/macOS, puedes usar 'arial.ttf' o 'Roboto-Regular.ttf' si lo tienes
# En Windows, puedes probar con 'C:/Windows/Fonts/arial.ttf'
# Si la fuente no se encuentra, Pillow usará una fuente predeterminada
try:
# Intenta cargar una fuente común
font = ImageFont.truetype("arial.ttf", 30)
except IOError:
print("Fuente 'arial.ttf' no encontrada. Usando fuente predeterminada.")
font = ImageFont.load_default() # Carga la fuente por defecto si no encuentra 'arial.ttf'
# Añadir texto
# Coordenadas: (x, y) del texto
draw.text((50, 70), "¡Hola, Pillow!", fill=(0, 0, 0), font=font)
img_texto.save('imagen_con_texto.png')
print("Imagen con texto guardada.")
img_texto.show()
🔄 Procesamiento por Lotes (Batch Processing)
Una de las mayores ventajas de Pillow es su capacidad para automatizar tareas. Podemos aplicar las mismas operaciones a múltiples imágenes de una sola vez.
from PIL import Image
import os
def procesar_imagenes_en_carpeta(ruta_entrada, ruta_salida, nuevo_ancho=None, nuevo_alto=None, aplicar_filtro=False):
if not os.path.exists(ruta_salida):
os.makedirs(ruta_salida)
for filename in os.listdir(ruta_entrada):
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp')):
filepath_entrada = os.path.join(ruta_entrada, filename)
filepath_salida = os.path.join(ruta_salida, f"procesada_{filename}")
try:
img = Image.open(filepath_entrada)
# Redimensionar si se especifica
if nuevo_ancho and nuevo_alto:
img = img.resize((nuevo_ancho, nuevo_alto))
elif nuevo_ancho:
# Mantener la proporción si solo se especifica el ancho
ancho_original, alto_original = img.size
nuevo_alto = int((nuevo_ancho / ancho_original) * alto_original)
img = img.resize((nuevo_ancho, nuevo_alto))
elif nuevo_alto:
# Mantener la proporción si solo se especifica el alto
ancho_original, alto_original = img.size
nuevo_ancho = int((nuevo_alto / alto_original) * ancho_original)
img = img.resize((nuevo_ancho, nuevo_alto))
# Aplicar filtro si se especifica
if aplicar_filtro:
from PIL import ImageFilter # Importar aquí para evitar circular imports si no se usa
img = img.filter(ImageFilter.SHARPEN)
img.save(filepath_salida)
print(f"Procesada: {filename} -> {os.path.basename(filepath_salida)}")
except Exception as e:
print(f"Error al procesar {filename}: {e}")
# Uso del procesamiento por lotes
# Crea una carpeta 'imagenes_originales' con tus imágenes de prueba
# y una carpeta 'imagenes_procesadas' vacía
ruta_entrada = 'imagenes_originales'
ruta_salida = 'imagenes_procesadas'
# Ejemplo 1: Redimensionar todas las imágenes a 600px de ancho (manteniendo proporción)
procesar_imagenes_en_carpeta(ruta_entrada, ruta_salida + '_redimensionadas_600px', nuevo_ancho=600)
# Ejemplo 2: Aplicar filtro de nitidez a todas las imágenes
procesar_imagenes_en_carpeta(ruta_entrada, ruta_salida + '_nitidas', aplicar_filtro=True)
# Ejemplo 3: Redimensionar y aplicar filtro
procesar_imagenes_en_carpeta(ruta_entrada, ruta_salida + '_redimensionadas_y_nitidas', nuevo_ancho=400, nuevo_alto=300, aplicar_filtro=True)
Para ejecutar este script, crea una carpeta llamada imagenes_originales en el mismo directorio que tu script de Python y coloca algunas imágenes dentro. Luego, ejecuta el script. Verás cómo se crean carpetas nuevas con las imágenes procesadas.
📈 Optimización de Imágenes para Web
Una tarea muy común es optimizar imágenes para su uso en la web, reduciendo el tamaño del archivo sin sacrificar demasiada calidad. Pillow facilita esto.
from PIL import Image
import os
def optimizar_imagen_para_web(input_path, output_path, calidad=85, max_ancho=1200):
try:
img = Image.open(input_path)
ancho_original, alto_original = img.size
# Redimensionar si es más grande que el ancho máximo
if ancho_original > max_ancho:
nuevo_alto = int((max_ancho / ancho_original) * alto_original)
img = img.resize((max_ancho, nuevo_alto), Image.LANCZOS)
# Convertir a RGB si la imagen tiene canal alfa (RGBA) para JPG
if img.mode == 'RGBA':
img = img.convert('RGB')
# Guardar con calidad reducida (solo para JPEG)
img.save(output_path, quality=calidad, optimize=True)
print(f"Imagen optimizada: {os.path.basename(input_path)} -> {os.path.basename(output_path)}")
except Exception as e:
print(f"Error al optimizar {input_path}: {e}")
# Ejemplo de uso:
input_folder = 'imagenes_originales'
output_folder = 'imagenes_web'
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.lower().endswith(('.jpg', '.jpeg')):
input_filepath = os.path.join(input_folder, filename)
output_filepath = os.path.join(output_folder, f"web_{filename}")
optimizar_imagen_para_web(input_filepath, output_filepath, calidad=75, max_ancho=800)
elif filename.lower().endswith('.png'): # Los PNGs se tratan diferente, no usan 'quality' de la misma forma
input_filepath = os.path.join(input_folder, filename)
output_filepath = os.path.join(output_folder, f"web_{filename}")
try:
img = Image.open(input_filepath)
if img.width > 800: # Redimensionar PNG si es grande
ancho_original, alto_original = img.size
nuevo_alto = int((800 / ancho_original) * alto_original)
img = img.resize((800, nuevo_alto), Image.LANCZOS)
img.save(output_filepath, optimize=True) # optimize=True para PNG reduce el tamaño de archivo
print(f"PNG optimizado: {os.path.basename(input_filepath)} -> {os.path.basename(output_filepath)}")
except Exception as e:
print(f"Error al optimizar PNG {input_filepath}: {e}")
En este ejemplo, ajustamos la quality para JPGs (un valor entre 1 y 95 es común, con 75-85 siendo un buen equilibrio). Para PNGs, optimize=True ayuda a reducir el tamaño del archivo sin pérdida de calidad. También se usa Image.LANCZOS como filtro de remuestreo de alta calidad para el redimensionado.
❓ Preguntas Frecuentes (FAQ)
¿Pillow reemplaza por completo a PIL?
Sí, Pillow es el sucesor *de facto* de PIL. Mantiene la misma API y es activamente mantenida, mientras que PIL ha dejado de tener soporte oficial. Si encuentras código que usa `PIL`, lo más probable es que funcione sin cambios con `Pillow` instalado.¿Cómo puedo convertir una imagen a escala de grises?
Es muy sencillo. Solo tienes que usar el método `convert()` con el modo `'L'`: ```python from PIL import Image img = Image.open('imagen_original.jpg') img_gris = img.convert('L') img_gris.save('imagen_gris.jpg') ```¿Pillow soporta imágenes GIF animadas?
Pillow puede abrir y guardar GIFs animados, pero su capacidad para manipular *fotogramas individuales* dentro de un GIF animado es más limitada que la de otras bibliotecas específicas para GIFs. Puedes iterar a través de los fotogramas para aplicar operaciones a cada uno.✅ Conclusión
Has llegado al final de esta guía sobre cómo manipular imágenes con Pillow en Python. Ahora tienes las herramientas y el conocimiento para realizar una amplia gama de operaciones, desde básicas hasta más complejas, en tus imágenes. Ya sea para automatizar el redimensionamiento, aplicar efectos creativos o preparar imágenes para la web, Pillow es un aliado invaluable en tu kit de herramientas de Python.
¡Experimenta, crea y sigue explorando el vasto mundo del procesamiento de imágenes con Python!
Tutoriales relacionados
- Desarrolla Interfaces Gráficas con Tkinter: Guía Completa de GUI en Pythonbeginner15 min
- Desarrolla tu Primer Bot de Telegram con Python y `python-telegram-bot`beginner20 min
- Gestiona Archivos y Directorios con el Módulo `os` en Python: Una Guía Prácticaintermediate25 min
- Automatiza la Gestión de Datos con Pandas: El Arte de Limpiar y Transformar CSVsbeginner20 min
- Optimiza Tareas Repetitivas con Decoradores en Python: Una Guía Avanzadaadvanced20 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!