jueves, 18 de diciembre de 2014

Fundamentos para procesar imágenes con Pillow (y III)



Después de ver, en la primera parte de esta entrega, algunas funciones básicas de edición de imágenes del módulo Image de Pillow y, en la segunda parte, otras para convertir las imágenes y aplicar diferentes filtros, pasamos ahora a mostrar funciones del módulo ImageOps que se utilizan para añadir bordes a las imágenes y para aplicar efectos que crean imágenes inversas, espejadas, ecualizadas; o que permiten modificar sus colores, el contraste, etc.

Para finalizar se muestran algunos ejemplos que usan funciones del módulo ImageDraw que se utilizan para realizar dibujos vectoriales y escribir textos sobre una imagen.


Añadir bordes a una imagen


Para agregar un borde de color negro alrededor de la imagen de 5 píxeles de grosor:

from PIL import Image, ImageOps
imagen = Image.open("pantera.jpg")
imagen.size   # 800x600
imagen.mode   # RGB
imagenconborde = ImageOps.expand(imagen, border=5, fill=(0,0,255))
imagenconborde.show()
imagenconborde.save("panteraborde.jpg")




Lógicamente, si la imagen original tenía un tamaño de 800x600 ahora la obtenida será de 810x610, incrementándose en 5 píxeles cada margen de la imagen.

imagenconborde.size   # 810x610
imagenconborde = ImageOps.expand(imagen, border=5, fill="red")


La forma de asignar colores depende del tipo de imagen:

Tipo de imagenFormato color
1, L, Inúmero entero
RGBtupla con 3 números enteros (Rojo, Verdez y Azul) o nombres de color (en inglés)
Fnúmero entero o número de coma flotante
Pnúmero entero, tupla con 3 números enteros o nombres de color


Recortar los bordes de una imagen


La función crop() se utiliza para recortar las imágenes por todos sus márgenes un mismo número de píxeles:

recortar = ImageOps.crop(imagen, border=50)
recortar.show()




Convertir la imagen a escala de grises


grises = ImageOps.grayscale(imagen)
grises.show()



Obtener la imagen inversa

inversa = ImageOps.invert(imagen)
inversa.show()


 

Espejar una imagen (de izquierda a derecha)

espejo = ImageOps.mirror(imagen)
espejo.show()



Voltear una imagen verticalmente (de arriba a abajo)

flipeada = ImageOps.flip(imagen)
flipeada.show()



Reducir el número de bits para cada canal de color

posterizada = ImageOps.posterize(imagen, 1)
posterizada.show()



Invertir los valores de los píxeles por encima de un umbral

solarizada = ImageOps.solarize(imagen, threshold=64)
solarizada.show()



Maximizar contraste de una imagen (Autocontraste)




imagen = Image.open("nevando.jpg")
autocontraste = ImageOps.autocontrast(imagen, cutoff=0)
autocontraste.show()




La función calcula un histograma de la imagen de entrada y borra un porcentaje de los píxeles más claros y oscuros. Los píxeles más oscuros se convierten en negros (0) y los más claros en blancos (255).


Dar color a una imagen en blanco y negro



A los píxeles negros de la imagen se les asigna el primer color indicado en la función y a los píxeles blancos al segundo color.
imagen = Image.open("grises.jpg")
imagen.show()
coloriza = ImageOps.colorize(imagen, "black", "cyan")
coloriza.show()
coloriza.save("coloriza.jpg")




También, en este caso, es posible asignar un color RGB dando los valores en una tupla

coloriza = ImageOps.colorize(imagenbn, (0,0,0), (0,100,200))

Ecualizar una imagen


Ecualizar es sinónimo de igualar. En este caso la función iguala el histograma de la imagen de entrada aplicando un mapeo no lineal con el fin de crear una distribución uniforme de los valores en la escala de grises, en la imagen de salida.

imagen = Image.open("nevando.jpg")
ecualizada = ImageOps.equalize(imagen)
ecualizada.show()
ecualizada.save("ecualizada.jpg")



A continuación, los histogramas de la imagen sin ecualizar y de la ecualizada:




Otras funciones disponibles: deform(), fit(), flip()

Dibujar sobre una imagen

from PIL import Image, ImageDraw
imagen = Image.open("nevando.jpg")
dibujo = ImageDraw.Draw(imagen)
dibujo.line((0, 90) + (800, 90), fill="white")
imagen.show()
imagen.save("linea.jpg")




Otras funciones:

Dibujar punto:

ImageDraw.Draw.point(xy, fill=None)


Dibujar arco:

ImageDraw.Draw.arc(xy, start, end, fill=None)


Dibujar polígono:

ImageDraw.Draw.polygon([(x1, y1),(x2, y2),...], fill=None, outline=None)


Dibujar rectángulo:

Draw.rectangle([(x0, y0),(x1, y1)], fill=None, outline=None)


Dibujar bitmap

ImageDraw.Draw.bitmap(xy, bitmap, fill=None)


Dibujar elipse:

ImageDraw.Draw.ellipse(xy, fill=None, outline=None)


Escribir texto sobre una imagen


from PIL import Image, ImageDraw, ImageFont
imagen = Image.open("linea.jpg")
imagen = imagen.convert("RGBA")
texto = Image.new('RGBA', imagen.size, (255,255,255,0))
fuente = ImageFont.truetype("Arabic Magic.ttf", 40)
dibujo = ImageDraw.Draw(texto)
dibujo.text((10, 40), "Pillow", font=fuente, fill=(255,255,255,128))
dibujo.text((110,40), "Python", font=fuente, fill=(255,255,255,255))
final = Image.alpha_composite(imagen, texto)
final.show()
final.save("texto.jpg")





Cambiar tamaño de fuentes:



ImageDraw.Draw.textsize(text, font=None)


Hay fuentes para descargar en Fontriver.


martes, 16 de diciembre de 2014

Fundamentos para procesar imágenes con Pillow (II)




Después de ver en el capítulo anterior algunas funciones de edición de imágenes del módulo Image, vamos a mostrar qué hacer para convertir nuestras imágenes de formato y de tipo. Para ello, es importante comprender que no siempre es posible cambiar una imagen de formato. En ocasiones para poderlo hacer tendremos que cambiar antes su tipo.

Para concluir mostraremos algunos ejemplos en los que se utilizan funciones que aplican filtros a las imágenes, que pertenecen al módulo ImageFilter.

 

Tipos de imágenes y formatos



Los formatos de las imágenes que se pueden procesar con Pillow son los de uso más extendido (BMP, EPS, GIF, IM, JPEG, MSP, PCX, PNG, TIFF, PPM, WebP, ICO, PSD, PDF, etc). Los tipos de imágenes dependen sobre todo del número de bits que se utilizan para representar la unidad de información de una imagen o píxel.

ClaveTipo de imagen
11-bit por pixel, blanco y negro, almacena 1 pixel/byte
L8-bit por píxel, blanco y negro
P8-bit por píxel, asignada a cualquier otro modo usando una paleta de colores
RGB3x8-bit por píxel, color verdadero
RGBA4x8-bit por píxel, color verdadero con true color con máscara de transparencia
CMYK4x8-bit por píxel, separación de colores
YCbCr3x8-bit por píxel, formato de vídeo en color
LAB3x8-bit por píxel, the L*a*b color space
HSV3x8-bit por píxel, Hue, Saturation, Value color space)
I32-bit signed integer pixels
F32-bit floating point pixels


Convertir imágenes

 

Convertir el tipo de imagen y su formato:



No todos los tipos de imagen son compatibles con cualquier formato. En el ejemplo siguiente la imagen de tipo "P" y con formato PNG no puede convertirse a JPEG:

amapolas.png

imagen = Image.open("amapolas.png")  # Se abre la imagen PNG
imagen.mode  # Comprobamos el modo de la imagen: P
imagen.save("amapolas-8bit.jpg") # Se intenta cambiar formato, a JPEG


Se intenta cambiar de formato, a JPEG pero se produce la siguiente excepción porque no es posible convertir la imagen:

OSError: cannot write mode P as JPEG

Si embargo, si antes la convertimos a "RGB" podremos convertirla a JPEG.

imagenrgb = imagen.convert("RGB")
imagenrgb.mode   # RGB
imagenrgb.save("amapolasrgb.jpg")


amapolasrgb.jpg

 

Cambiar el formato de una imagen directamente:


Para convertir de formato una imagen también puede hacerse así:

try:
    Image.open("amapolas-in.png").save("amapolas-out.jpg")

except IOError:
    print("No se puede convertir la imagen")


Cambiar una imagen de color RGB a Blanco/Negro



imagen = Image.open("amapolasrgb.jpg")
imagen.mode -> RGB
imagenbn = imagen.convert("L")
imagenbn.show()
imagenbn.save("amapolasbn.jpg")




Filtros


Para aplicar con Pillow un filtro a una imagen se utiliza el módulo ImageFilter

Debemos tener en cuenta que, a veces, cuando se aplica un filtro los cambios a observar serán más o menos sutiles en función al tipo de imagen que procesemos.

Para los ejemplos utilizaremos la siguiente imagen:



BLUR: Desenfocar una imagen:



from PIL import Image, ImageFilter
imagen = Image.open("pintando.jpg")
desenfocada = imagen.filter(ImageFilter.BLUR)
desenfocada.show()
desenfocada.save("desenfocada.jpg")



CONTOUR: Marcar contornos:



contorneada = imagen.filter(ImageFilter.CONTOUR)
contorneada.show()
contorneada.save("contorneada.jpg")



DETAIL: Resaltar detalle:



detallar = imagen.filter(ImageFilter.DETAIL)
detallar.show()
detallar.save("detallada.jpg")



EDGE_ENHANCE y EDGE_ENHANCE_MORE: Realzar bordes:



realzarbordes = imagen.filter(ImageFilter.EDGE_ENHANCE_MORE)
realzarbordes.show()
realzarbordes.save("realzarbordes.jpg")



EMBOSS: Obtener relieve:



relieve = imagen.filter(ImageFilter.EMBOSS)
relieve.show()
relieve.save("relieve.jpg")




FIND_EDGES: Buscar límites:



limites = imagen.filter(ImageFilter.FIND_EDGES)
limites.show()
limites.save("limites.jpg")




SMOOTH y SMOOTH_MORE: Suavizar la imagen:



suavizar = imagen.filter(ImageFilter.SMOOTH_MORE)
suavizar.show()
suavizar.save("suavizar.jpg")





SHARPEN: Afinar:


afinar = imagen.filter(ImageFilter.SHARPEN)
afinar.show()
afinar.save("afinar.jpg")





Continuar en: Fundamentos para procesar imágenes con Pillow (y III)

Ir al índice del tutorial de Python

domingo, 14 de diciembre de 2014

Fundamentos para procesar imágenes con Pillow (I)


Pillow es una variante (o fork) de la popular librería PIL (Python Image Library) que permite procesar con facilidad imágenes con Python 2.x/3.x. El proyecto lo inició Alex Clark cuando PIL se quedó sin desarrollo a finales del año 2009. Actualmente, Pillow es mantenido con la ayuda de otros colaboradores.

Con Pillow podemos consultar información básica de una imagen como su tamaño, el formato que tiene (jpg, png, gif, etc.), el tipo de imagen (bits/pixel, BN/color, etc.) y, también, es posible modificar su aspecto realizando operaciones para cambiar su tamaño, recortar un área, girar, aplicar filtros y efectos, convertir el tipo de imagen y su formato, etc.


Instalación

 

Comprobar si tenemos alguna versión de Pillow instalada:


$ pip3 show Pillow
---
Name: Pillow
Version: 2.3.0
Location: /usr/lib/python3/dist-packages

Instalar Pillow en un equipo con Windows/Linux con Pip:


$ pip3 install Pillow


Instalando en una red en la que se utiliza proxy:


$ pip3 install Pillow --proxy http://usuario:passw@servidor:puerto

 

Abrir imágenes

 

Abrir y mostrar una imagen:


from PIL import Image
imagen = Image.open("amapolas.jpg")
imagen.show()



Abrir y mostrar una imagen capturando excepciones:


from PIL import Image
try:
    imagen = Image.open("amapolas.jpg")
    imagen.show()
except:
    print("No ha sido posible cargar la imagen")


Consultar información de las imágenes

 

Formato:


print(imagen.format)   # JPEG

Tamaño:


print(imagen.size)   # Obtiene tupla con pixels horizontal/vertical (800, 600)

El módulo PIL utiliza un sistema en la que la coordenada (0, 0) se encuentra en la esquina superior izquierda de la imagen. Si una imagen tiene un tamaño de 800x600 píxeles la coordenada (800, 600) se encontrará en la esquina inferior derecha de la imagen.

Tipo de imagen:


print(imagen.mode)   # Tipo de imagen: RGB, CMYK, L, P, etc.

Histograma:


print(imagen.histogram)   # Obtiene datos del histograma de una imagen




El histograma de una imagen representa la frecuencia relativa de los niveles de gris o de los colores básicos (rojo, azul, verde) de la imagen.

Una de las técnicas básicas de retoque de fotos consiste en modificar el histograma para aumentar el contraste de las imágenes.

Otros métodos para consultar información de una imagen: imagen.info, imagen.palette.


Edicíón básica (tamaños, rotar, girar...) y guardar imágenes

 

Cortar una región de una imagen y guardarla:


La región a obtener se define con una tupla con las coordenadas siguientes:(izquierda, superior, derecha, inferior)




En el ejemplo siguiente de la imagen original, que tiene un tamaño de 800x600 píxeles, vamos a "desechar" un margen de 100 píxeles alrededor de la misma; quedándonos pues con la región central que tendrá un tamaño de 600x400. Con la función crop() obtendremos la imagen resultante que será guardada en el sistema de ficheros con la función save().  

# Define tupla con región
caja = (100, 100, 700, 500)

# Obtener de la imagen original la región de la caja
region = imagen.crop(caja)  

region.show()  # Mostrar imagen de la region
region.size   # Mostrar tamaño de imagen final 600x400

# Guarda la imagen obtenida con el formato JPEG.
region.save("region.jpg")

# Guarda la imagen obtenida con el formato PNG.
region.save("region.png")

Cambiar el tamaño de una imagen:


Podemos cambiar el tamaño de una imagen para hacerla más grande o más pequeña. En este caso como la imagen original tiene una proporción de 4x3 vamos a reducirla manteniendo estas proporciones. Como la imagen original tiene un tamaño de 800x600 para hacerla 4 veces más pequeña indicamos el nuevo tamaño 400x300 en la función resize()

# Obtener imagen con el tamaño indicado
reducida = imagen.resize((400, 300))

# Mostrar imagen
reducida.show()

# Guardar imagen obtenida con el formato JPEG
reducida.save("reducida.jpg")




Rotar una imagen:


Para rotar una imagen utilizaremos el método rotate() y tendremos que indicar el número de grados teniendo en cuenta que el giro seguirá el sentido contrario a las agujas del reloj. En el ejemplo siguiente la imagen obtenida (a partir de "reducida.jpg") estará girada 45 grados.

# Obtener imagen girada 45º
girada1 = reducida.rotate(45)

# Mostrar imagen
girada1.show()

# Guardar la imagen con el formato JPEG
girada1.save("girada.jpg")




Trasponer una imagen:


Otra posibilidad para rotar una imagen es con el método transpose(). El módulo Image provee funciones para girar una imagen con rotaciones de grados múltiples de 90 (ROTATE_90, ROTATE_180, ROTATE_270) y otras que permiten darles la vuelta (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM).

# Obtener imagen girada 90º
girada2 = reducida.transpose(Image.ROTATE_90)
girada2.show()
girada2.save("girada2.jpg")




# Voltear imagen de arriba a abajo
volteada = reducida.transpose(Image.FLIP_TOP_BOTTOM)
volteada.show()
volteada.save("volteada.jpg") 




Girar una región dentro de una imagen:





En el siguiente ejemplo después de obtener una imagen girada 90º la pegamos en la imagen original con la función paste().

# Abrir la imagen "pantera.jpg"
imagen = Image.open("pantera.jpg")

# Mostrar tanaño de la imagen:
imagen.size  # 800x600

# Definir tupla con las coordenadas de la región
caja = (200, 100, 600, 500)

# Obtener región 
region = imagen.crop(caja)

# Girar región 90º
region = region.transpose(Image.ROTATE_90)

# Pegar región girada en la imagen original
imagen.paste(region, caja)

# Mostrar imagen final y guardar
imagen.show()
imagen.save("regiongirada90.jpg")




Unir imágenes:



 


La función paste() la podemos utilizar para unir varias imágenes en una. En el siguiente ejemplo se utiliza el método new() para crear una imagen vacía "RGB" con un tamaño de 800x600 píxeles y con el fondo negro. Después se abren dos imágenes con el mismo tamaño 400x600 y se pegan horizontalmente, una a continuación de la otra, sobre la primera.

final = Image.new("RGB",(800,600),"black")
imagen1 = Image.open("nevando2-izq.jpg")
imagen2 = Image.open("nevando2-der.jpg")
final.paste(imagen1, (0,0))
final.paste(imagen2, (399,0))
final.show()
final.save("nevando2-unidas.jpg")




Si utilizamos dos imágenes transparentes del mismo tamaño y pegamos una en la coordenada (0, 0) de la otra obtentrámos una imagen mezclada.


Crear miniaturas:


La función thumbnail() se utiliza para crear miniaturas de las imágenes:

imagen = Image.open("amapolas.jpg")
miniatura = (160, 120)
imagen.thumbnail(miniatura)
imagen.save("miniatura.jpg")