Voltar ao Glossario
Glossario Python

Pillow: O que É e Como Funciona | Python Brasil

Aprenda Pillow em Python: abrir, manipular e salvar imagens, redimensionar, filtros, texto, thumbnails e boas praticas para processamento de imagens.

O que e Pillow?

Pillow e a biblioteca mais popular para processamento de imagens em Python. Ela e um fork moderno e ativamente mantido da PIL (Python Imaging Library) e suporta uma ampla variedade de formatos de imagem, incluindo JPEG, PNG, GIF, TIFF, BMP e WebP. Com Pillow, voce pode abrir, manipular, transformar e salvar imagens de forma programatica.

Pillow e usada em aplicacoes web para redimensionar imagens de upload, gerar thumbnails, aplicar marcas dagua, converter entre formatos e criar graficos simples.

Instalacao e Operacoes Basicas

# Instalacao
# pip install Pillow

from PIL import Image

# Abrir uma imagem
img = Image.open('foto.jpg')
print(f'Formato: {img.format}')     # JPEG
print(f'Tamanho: {img.size}')       # (1920, 1080)
print(f'Modo: {img.mode}')          # RGB

# Salvar em outro formato
img.save('foto.png')
img.save('foto.webp', quality=80)

# Mostrar a imagem (abre no visualizador padrao)
# img.show()

# Informacoes detalhadas
print(f'Largura: {img.width}')
print(f'Altura: {img.height}')

Redimensionamento e Recorte

from PIL import Image

img = Image.open('foto.jpg')

# Redimensionar (pode distorcer a proporcao)
redimensionada = img.resize((800, 600))
redimensionada.save('redimensionada.jpg')

# Redimensionar mantendo proporcao
img.thumbnail((800, 800))  # modifica in-place, mantendo aspecto
img.save('thumbnail.jpg')

# Redimensionar com proporcao usando calculo
largura_desejada = 800
proporcao = largura_desejada / img.width
nova_altura = int(img.height * proporcao)
redimensionada = img.resize((largura_desejada, nova_altura), Image.LANCZOS)

# Recortar (crop) — (esquerda, topo, direita, baixo)
recortada = img.crop((100, 100, 500, 400))
recortada.save('recortada.jpg')

# Recortar o centro da imagem
def recortar_centro(img, largura, altura):
    """Recorta a regiao central da imagem."""
    esq = (img.width - largura) // 2
    topo = (img.height - altura) // 2
    dir = esq + largura
    baixo = topo + altura
    return img.crop((esq, topo, dir, baixo))

centro = recortar_centro(img, 400, 400)
centro.save('centro.jpg')

Rotacao e Transformacoes

from PIL import Image, ImageOps

img = Image.open('foto.jpg')

# Rotacao
rotacionada = img.rotate(45, expand=True, fillcolor='white')
rotacionada.save('rotacionada.jpg')

# Espelhamento
espelhada_h = img.transpose(Image.FLIP_LEFT_RIGHT)
espelhada_v = img.transpose(Image.FLIP_TOP_BOTTOM)

# Rotacoes de 90 graus
rotacionada_90 = img.transpose(Image.ROTATE_90)
rotacionada_180 = img.transpose(Image.ROTATE_180)

# Corrigir orientacao EXIF (fotos de celular)
img_corrigida = ImageOps.exif_transpose(img)
img_corrigida.save('corrigida.jpg')

Filtros e Ajustes

from PIL import Image, ImageFilter, ImageEnhance

img = Image.open('foto.jpg')

# Filtros
borrada = img.filter(ImageFilter.GaussianBlur(radius=5))
nitida = img.filter(ImageFilter.SHARPEN)
bordas = img.filter(ImageFilter.FIND_EDGES)
contornos = img.filter(ImageFilter.CONTOUR)
relevo = img.filter(ImageFilter.EMBOSS)

# Ajustes de brilho, contraste, cor e nitidez
brilho = ImageEnhance.Brightness(img).enhance(1.3)     # 30% mais brilho
contraste = ImageEnhance.Contrast(img).enhance(1.5)    # 50% mais contraste
saturacao = ImageEnhance.Color(img).enhance(0.5)       # 50% menos saturacao
nitidez = ImageEnhance.Sharpness(img).enhance(2.0)     # dobrar nitidez

# Converter para escala de cinza
cinza = img.convert('L')
cinza.save('cinza.jpg')

# Converter para preto e branco
pb = img.convert('1')

# Converter para RGBA (com transparencia)
rgba = img.convert('RGBA')

Adicionar Texto e Marca D’agua

from PIL import Image, ImageDraw, ImageFont

img = Image.open('foto.jpg')
draw = ImageDraw.Draw(img)

# Texto simples
draw.text((10, 10), 'Python Brasil', fill='white')

# Texto com fonte customizada
try:
    fonte = ImageFont.truetype('arial.ttf', size=36)
except IOError:
    fonte = ImageFont.load_default()

draw.text((50, 50), 'Titulo da Imagem', fill='white', font=fonte)

# Marca dagua semi-transparente
def adicionar_marca_dagua(img_path: str, texto: str, saida: str):
    """Adiciona marca dagua de texto a uma imagem."""
    img = Image.open(img_path).convert('RGBA')

    # Criar camada transparente
    marca = Image.new('RGBA', img.size, (0, 0, 0, 0))
    draw = ImageDraw.Draw(marca)

    try:
        fonte = ImageFont.truetype('arial.ttf', size=48)
    except IOError:
        fonte = ImageFont.load_default()

    # Posicionar no canto inferior direito
    bbox = draw.textbbox((0, 0), texto, font=fonte)
    largura_texto = bbox[2] - bbox[0]
    altura_texto = bbox[3] - bbox[1]
    x = img.width - largura_texto - 20
    y = img.height - altura_texto - 20

    draw.text((x, y), texto, fill=(255, 255, 255, 128), font=fonte)
    resultado = Image.alpha_composite(img, marca)
    resultado.convert('RGB').save(saida)

adicionar_marca_dagua('foto.jpg', 'Python Brasil', 'com_marca.jpg')

Composicao de Imagens

from PIL import Image

# Combinar duas imagens lado a lado
def combinar_horizontal(img1_path: str, img2_path: str, saida: str):
    img1 = Image.open(img1_path)
    img2 = Image.open(img2_path)

    # Ajustar alturas
    altura = min(img1.height, img2.height)
    img1 = img1.resize((int(img1.width * altura / img1.height), altura))
    img2 = img2.resize((int(img2.width * altura / img2.height), altura))

    largura_total = img1.width + img2.width
    resultado = Image.new('RGB', (largura_total, altura))
    resultado.paste(img1, (0, 0))
    resultado.paste(img2, (img1.width, 0))
    resultado.save(saida)

# Gerar thumbnails em lote
from pathlib import Path

def gerar_thumbnails(pasta_entrada: str, pasta_saida: str, tamanho=(200, 200)):
    """Gera thumbnails para todas as imagens de uma pasta."""
    entrada = Path(pasta_entrada)
    saida = Path(pasta_saida)
    saida.mkdir(parents=True, exist_ok=True)

    extensoes = {'.jpg', '.jpeg', '.png', '.webp'}
    for arquivo in entrada.iterdir():
        if arquivo.suffix.lower() in extensoes:
            img = Image.open(arquivo)
            img.thumbnail(tamanho, Image.LANCZOS)
            img.save(saida / f'thumb_{arquivo.name}')
            print(f'Thumbnail criado: {arquivo.name}')

Erros Comuns

O erro mais frequente e tentar salvar uma imagem RGBA como JPEG — o formato JPEG nao suporta transparencia. Converta para RGB antes com img.convert('RGB'). Outro erro e usar resize() sem manter a proporcao, distorcendo a imagem. Tambem e comum nao fechar imagens apos o uso, consumindo memoria em loops. Esquecer de instalar Pillow e importar PIL gera confusao, ja que o nome do pacote no pip difere do nome no import.

Boas Praticas

Use Image.LANCZOS como filtro de redimensionamento para melhor qualidade. Prefira thumbnail() quando quiser manter a proporcao. Feche imagens com img.close() ou use context managers. Especifique quality ao salvar JPEG para controlar tamanho do arquivo. Use o formato WebP para web por oferecer melhor compressao. Para processamento em lote, considere processar em paralelo com multiprocessing.

Quando Usar

Pillow e ideal para operacoes basicas e intermediarias de processamento de imagens: redimensionamento, recorte, conversao de formatos, filtros, texto e composicao. Para visao computacional e processamento avancado, considere OpenCV. Para manipulacao de imagens em conjunto com arrays NumPy, Pillow se integra bem atraves de numpy.array(img).