Manipulação de Imagens com Python (Pillow)

Laboratório de Programação — Ciência da Computação

Conceitos de imagem digital e operações com Pillow — com syntax highlighting

Imagens Digitais — Visão Geral

  • Imagem digital = matriz de pixels (valores discretos).
  • Coordenadas (x, y) e intensidade/cor por pixel.
  • Propriedades: resolução, profundidade de cor, formato, proporção, canais, metadados.

Baseado no material “Manipulando Imagens com o Python”.

Resolução & Profundidade

  • Resolução: largura × altura (px).
  • Profundidade de cor: 8, 16, 24, 48 bits (faixa de cores).
  • Canal alfa: transparência.

Formatos Típicos

  • JPEG: compressão com perdas (fotos).
  • PNG: sem perdas, suporta alfa (gráficos/logos).
  • GIF: paleta reduzida, animação simples.
  • TIFF: alta qualidade/arquivamento.

Pillow (PIL) — Introdução

  • Biblioteca para abrir, manipular e salvar múltiplos formatos.
  • Operações: pixel a pixel, máscaras, transparência, filtros, desenho de texto/formas.
  • Documentação: pillow.readthedocs.io
pip install Pillow

Import e Abertura

from PIL import Image

img = Image.open("foto.jpg")    # abre
img.show()                      # exibe (visualizador do SO)
print(img.size, img.mode, img.format)

Salvar em Diferentes Formatos

# Conversões simples
img_png = img.convert("RGBA")
img_png.save("saida.png")       # mantém alfa
img_jpg = img.convert("RGB")
img_jpg.save("saida.jpg", quality=90, optimize=True)

Redimensionar, Cortar, Rotacionar

from PIL import Image

img = Image.open("input.jpg")

# Redimensionar
thumb = img.resize((128, 128))            # tamanho fixo
thumb.save("thumb.jpg")

# Cortar (crop): caixa (esq, topo, dir, base)
crop = img.crop((100, 100, 400, 400))
crop.save("recorte.jpg")

# Rotacionar
rot = img.rotate(45, expand=True)
rot.save("rotacao_45.png")

Qualidade, Modo e Metadados

# JPEG com qualidade
img.save("foto_q80.jpg", quality=80, subsampling=1)

# Modo de cor
print(img.mode)         # "RGB", "RGBA", "L" (grayscale)...
g = img.convert("L")    # escala de cinza
g.save("cinza.png")

# Metadados (EXIF) se disponíveis
exif = img.getexif()
print(dict(exif))

Combinação de Imagens (lado a lado)

Exemplo inspirado no seu notebook/Colab.

from PIL import Image

# Abrir duas imagens
img1 = Image.open("my_bike.jpeg")
img2 = Image.open("my_bike2.jpeg")

# Definir largura combinada
largura_combinada = img1.width + img2.width
altura = max(img1.height, img2.height)

# Criar nova imagem (fundo branco)
img_comb = Image.new("RGB", (largura_combinada, altura), color=(255,255,255))
img_comb.paste(img1, (0, 0))
img_comb.paste(img2, (img1.width, 0))

img_comb.save("imagem_combinada.jpg")
img_comb.show()

Texto & Bordas em Imagens

from PIL import Image, ImageDraw, ImageFont

img = Image.open("my_bike2.jpeg").convert("RGB")
draw = ImageDraw.Draw(img)

# Fonte (padrão ou .ttf)
# fonte = ImageFont.truetype("arial.ttf", 24)
fonte = ImageFont.load_default()

# Texto
draw.text((10, 10), "Estilo Pablo Picasso", font=fonte, fill=(255,255,255))

# Borda (retângulo)
esp = 5
draw.rectangle([(0, 0), (img.width-1, img.height-1)],
               outline=(0,0,0), width=esp)

img.save("imagem_modificada.jpg")
img.show()

Dicas de Tipografia

  • Prefira ImageFont.truetype para controle de tipo/tamanho.
  • Use stroke_width/stroke_fill (versões recentes) para contorno do texto.
  • Escolha cores contrastantes; avalie sombra/contorno para legibilidade.
  • Considere centralização: calcule textbbox para alinhar.

Marca d’água (logo com transparência)

A imagem do logo deve ter fundo transparente (canal alfa) para melhor fusão.

from PIL import Image

# Base e logo com RGBA (alfa)
img  = Image.open("Software Engineer.jpeg").convert("RGBA")
logo = Image.open("my_logo.png").convert("RGBA")

# Redimensionar logo até 25% da imagem
lw, lh = logo.size
max_w, max_h = int(img.width*0.25), int(img.height*0.25)
escala = min(max_w/lw, max_h/lh)
logo_rs = logo.resize((int(lw*escala), int(lh*escala)))

# Posição (canto inferior direito, margem 10px)
pos = (img.width - logo_rs.width - 10, img.height - logo_rs.height - 10)

# Compor preservando alfa do logo
img.paste(logo_rs, pos, mask=logo_rs)

# Salvar (PNG preserva transparência)
img.save("imagem_com_marca_dagua.png")

Thumbnails Rápidos

from PIL import Image

img = Image.open("my_bike.jpeg")
tamanho = (128, 128)

# resize: força o tamanho exato
thumb = img.resize(tamanho)
thumb.save("thumbnail.jpg")

# alternativa: thumbnail mantém proporção e "encaixa"
img2 = Image.open("my_bike.jpeg")
img2.thumbnail(tamanho)              # altera in-place
img2.save("thumbnail_proporcional.jpg")

Quando usar cada um?

  • resize: quando você precisa de dimensões exatas (pode distorcer).
  • thumbnail: quando precisa preservar a proporção original.
  • Para recorte não distorcido: combine thumbnail + crop central.

Filtros & Melhorias

from PIL import Image, ImageFilter, ImageEnhance

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

# Filtros
blur   = img.filter(ImageFilter.GaussianBlur(2))
edges  = img.filter(ImageFilter.FIND_EDGES)
sharp  = ImageEnhance.Sharpness(img).enhance(1.5)
bright = ImageEnhance.Brightness(img).enhance(1.2)
contr  = ImageEnhance.Contrast(img).enhance(1.2)

blur.save("blur.jpg"); edges.save("edges.jpg")
sharp.save("sharp.jpg")

Desenho de Formas

from PIL import Image, ImageDraw

img = Image.open("fundo.png").convert("RGBA")
draw = ImageDraw.Draw(img)

# Linha, retângulo e círculo
draw.line((10, 10, 200, 50), fill=(255,0,0,255), width=4)
draw.rectangle((20, 70, 220, 170), outline=(0,0,255,255), width=3)
draw.ellipse((250, 40, 340, 130), outline=(0,128,0,255), width=3)

img.save("formas.png")

Pipeline Reprodutível ( funções )

from PIL import Image, ImageDraw, ImageFont

def combinar_lado_a_lado(a: str, b: str, out: str):
    A, B = Image.open(a), Image.open(b)
    w, h = A.width + B.width, max(A.height, B.height)
    dest = Image.new("RGB", (w, h), (255,255,255))
    dest.paste(A, (0, 0)); dest.paste(B, (A.width, 0))
    dest.save(out); return out

def marca_dagua(base: str, logo: str, out: str, escala=0.25, margem=10):
    img  = Image.open(base).convert("RGBA")
    log  = Image.open(logo).convert("RGBA")
    lw, lh = log.size
    max_w, max_h = int(img.width*escala), int(img.height*escala)
    s = min(max_w/lw, max_h/lh)
    log = log.resize((int(lw*s), int(lh*s)))
    pos = (img.width - log.width - margem, img.height - log.height - margem)
    img.paste(log, pos, mask=log)
    img.save(out); return out

Transforme seus passos em funções reutilizáveis para automação e clareza.

Boas Práticas

  • Trabalhe em cópias (não sobrescreva o original).
  • Escolha formatos conforme a necessidade (PNG para transparência, JPEG para fotos).
  • Controle qualidade/otimização em JPEG (quality, optimize).
  • Preserve alfa em PNG ao compor marcas d’água.
  • Valide dimensões e proporção antes de redimensionar/recortar.

Desempenho

  • Evite múltiplas conversões desnecessárias de modo (RGB↔RGBA).
  • Para lotes grandes, vetorize decisões e processe em pipelines.
  • Minimize I/O: agrupe leituras e escritas de arquivo.
  • Documente parâmetros e versões dos artefatos gerados.

Exercícios (Lab)

  1. Abra duas imagens e gere uma composição lado a lado (com borda externa).
  2. Adicione um título centralizado com contorno e salve em PNG.
  3. Crie uma marca d’água com transparência e teste duas posições.
  4. Gere thumbnails proporcionais para uma pasta de fotos.
  5. Aplique um filtro de desfoque e ajuste brilho/contraste, comparando resultados.

Conclusões

  • Pillow fornece um conjunto completo para abrir, editar e compor imagens.
  • Domine operações básicas (resize/crop/rotate), desenho (texto/formas) e composição com alfa.
  • Construa funções reutilizáveis e siga boas práticas para resultados consistentes.