Processamento de Imagens com Python: Fundamentos e Técnicas Avançadas

Este artigo explora as principais bibliotecas e técnicas para processamento de imagens em Python, com foco prático em manipulação, transformação, análise e filtragem de imagens digitais. Abordamos ferramentas essenciais como PIL/Pillow, NumPy, Matplotlib e SciPy, demontsrando como combiná-las para resolver tarefas comuns em visão computacional.

Manipulação Básica com Pillow

A biblioteca Pillow (sucessora ativa do PIL) é a escolha padrão para operações fundamentais de imagem. Ela permite carregar, converter, redimensionar e salvar imagens em diversos formatos.

Para abrir uma imagem e convertê-la para tons de cinza:

from PIL import Image

img = Image.open("landscape.jpg")
gray_img = img.convert("L")

O método convert("L") aplica uma conversão ponderada dos canais RGB para luminância, gerando uma imagem monocromática. Para salvar em outro formato — por exemplo, converter todos os arquivos PNG de um diretório para JPEG — podemos usar:

import os
from pathlib import Path
from PIL import Image

input_dir = Path("raw_images")
output_dir = Path("converted_jpegs")
output_dir.mkdir(exist_ok=True)

for path in input_dir.glob("*.png"):
    with Image.open(path) as im:
        jpeg_path = output_dir / f"{path.stem}.jpg"
        im.save(jpeg_path, quality=95)

Geração de Miniaturas

Criar miniaturas preservando a proporção é simples com o método thumbnail():

# Redimensiona in-place mantendo a proporção máxima de 200×200
img.thumbnail((200, 200))

Corte e Colagem com Precisão

O corte usa uma tupla (esquerda, superior, direita, inferior), com origem no canto superior esquerdo:

# Extrai região central de 150×150 pixels
w, h = img.size
box = (w//2 - 75, h//2 - 75, w//2 + 75, h//2 + 75)
region = img.crop(box)

# Rotaciona 90 graus e cola de volta na posição original
rotated = region.rotate(90, expand=True)
img.paste(rotated, box)

Visualização com Matplotlib

O Matplotlib oferece controle refinado sobre a exibição de imagens e sobreposições gráficas. Ao contrário do Pillow, ele opera sobre arrays NumPy — ideal para análise quantitativa.

Para exibir uma imagem com anotações interativas:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

# Carrega como array NumPy
data = np.array(Image.open("portrait.jpg"))

plt.figure(figsize=(8, 6))
plt.imshow(data)
plt.title("Imagem Original com Pontos Marcados")
plt.axis("off")  # Oculta eixos para visualização limpa

# Adiciona três pontos vermelhos
points = [(120, 85), (240, 110), (180, 200)]
for x, y in points:
    plt.plot(x, y, "ro", markersize=8)

plt.show()

Contornos e Histogramas

Para gerar contornos de nível de intensidade (útil em segmentação):

gray_data = np.array(Image.open("portrait.jpg").convert("L"))
plt.figure()
plt.contour(gray_data, levels=12, cmap="viridis", alpha=0.8)
plt.axis("equal")
plt.axis("off")
plt.show()

E para visualizar a distribuição de intensidades:

plt.figure()
plt.hist(gray_data.ravel(), bins=256, range=(0, 255), density=True, color="steelblue", alpha=0.7)
plt.xlabel("Intensidade de Pixel")
plt.ylabel("Densidade de Probabilidade")
plt.title("Histograma de Intensidade")
plt.grid(True, alpha=0.3)
plt.show()

Transformações com NumPy

O NumPy fornece a base numérica para operações vetoriais eficientes em imagens. Cada imagem é representada como um array multidimensional — por exemplo, (altura, largura, canais) para RGB ou (altura, largura) para tons de cinza.

Exemplos de transformações de brilho e contraste:

arr = np.array(Image.open("portrait.jpg").convert("L"), dtype=np.float32)

# Inversão de tons
inverted = 255.0 - arr

# Ajuste linear para intervalo [40, 220]
scaled = np.clip((arr - arr.min()) / (arr.max() - arr.min()) * 180 + 40, 0, 255)

# Elevação ao quadrado (realce de sombras)
quadratic = np.clip((arr / 255.0) ** 2 * 255, 0, 255)

Equalização de Histograma

Implementação eficiente usando funções NumPy nativas:

def equalize_histogram(image_array):
    """Equaliza o histograma de uma imagem em tons de cinza."""
    flat = image_array.ravel()
    hist, _ = np.histogram(flat, bins=256, range=(0, 256), density=False)
    cdf = hist.cumsum().astype(np.float64)
    cdf = 255 * cdf / cdf[-1]  # Normaliza para 0–255
    
    # Mapeia cada valor de pixel para seu novo valor
    equalized = np.interp(flat, range(256), cdf)
    return equalized.reshape(image_array.shape).astype(np.uint8)

# Uso
eq_img = equalize_histogram(gray_data)

Filtragem com SciPy

O módulo scipy.ndimage oferece filtros especializados para suavização, detecção de bordas e morfologia matemática.

Suavização Gaussiana

Aplica um kernel gaussiano para reduzir ruído:

from scipy.ndimage import gaussian_filter

blurred = gaussian_filter(gray_data, sigma=3.0)

Detecção de Bordas com Gradientes

Calcula derivadas parciais usando filtros Sobel:

from scipy.ndimage import sobel

grad_x = sobel(gray_data, axis=1, mode="constant")
grad_y = sobel(gray_data, axis=0, mode="constant")
magnitude = np.hypot(grad_x, grad_y)  # Magnitude do gradiente

Análise Morfológica em Imagens Binárias

Para contar objetos após limiarização:

from scipy.ndimage import label, binary_opening
from scipy.ndimage.morphology import generate_binary_structure

# Converte para binária com limiar fixo
binary = gray_data > 128

# Remove pequenos ruídos com abertura morfológica
structure = generate_binary_structure(2, 1)
cleaned = binary_opening(binary, structure, iterations=2)

# Rotula componentes conectados
labeled, num_objects = label(cleaned)
print(f"Objetos detectados: {num_objects}")

Remoção de Ruído com Modelo ROF

O modelo Rudin-Osher-Fatemi (ROF) preserva bordas enquanto remove ruído. Sua implementação baseada em algoritmo de Chambolle:

def rof_denoise(image, weight=10, tolerance=1e-3, step=0.125):
    """Implementação do algoritmo ROF para remoção de ruído."""
    u = image.astype(np.float64)
    px, py = np.zeros_like(u), np.zeros_like(u)
    
    while True:
        u_old = u.copy()
        
        # Gradientes
        ux = np.roll(u, -1, axis=1) - u
        uy = np.roll(u, -1, axis=0) - u
        
        # Atualiza variáveis duais
        px_new = px + (step / weight) * ux
        py_new = py + (step / weight) * uy
        norm = np.maximum(1, np.sqrt(px_new**2 + py_new**2))
        px, py = px_new / norm, py_new / norm
        
        # Atualiza primal
        rx = np.roll(px, 1, axis=1)
        ry = np.roll(py, 1, axis=0)
        div = (px - rx) + (py - ry)
        u = image + weight * div
        
        # Verifica convergência
        error = np.linalg.norm(u - u_old) / u.size
        if error < tolerance:
            break
    
    return np.clip(u, 0, 255).astype(np.uint8)

# Aplicação
noisy = gray_data + 20 * np.random.normal(size=gray_data.shape)
denoised = rof_denoise(noisy, weight=15)

Tags: Pillow NumPy matplotlib SciPy image-processing

Publicado em 9-30 04:35