Python para Desenvolvedores

2ª edição, revisada e ampliada

Capítulo 35: Processamento de Imagem


Python Imaging Library (PIL) é uma biblioteca de processamento de imagens matriciais para Python.

PIL possui módulos que implementam:

  • Ferramentas para cortar, redimensionar e mesclar imagens.
  • Algoritmos de conversão, que suportam diversos formatos.
  • Filtros, tais como suavizar, borrar e detectar bordas.
  • Ajustes, incluindo brilho e contraste.
  • Operações com paletas de cores.
  • Desenhos simples em 2D.
  • Rotinas para tratamento de imagens: equalização, auto-contraste, deformar, inverter e outras.

Exemplo de tratamento de imagem:

In [ ]:
"""
Cria miniaturas suavizadas para cada
JPEG na pasta corrente
"""

import glob

# Módulo principal do PIL
from PIL import Image

# Módulo de filtros
from PIL import ImageFilter

# Para cada arquivo JPEG
for fn in glob.glob("*.jpg"):

    # Retorna o nome do arquivo sem extensão
    f = glob.os.path.splitext(fn)[0]

    print 'Processando:', fn
    imagem = Image.open(fn)

    # Cria thumbnail (miniatura) da imagem
    # de tamanho 256x256 usando antialiasing
    imagem.thumbnail((256, 256), Image.ANTIALIAS)

    # Filtro suaviza a imagem
    imagem = imagem.filter(ImageFilter.SMOOTH)

    # Salva como arquivo PNG
    imagem.save(f + '.png', 'PNG')

Exemplo de desenho:

In [1]:
"""
Cria uma imagem com vários gradientes de cores
"""

from PIL import Image

# Módulo de desenho
from PIL import ImageDraw

# Largura e altura
l, a = 512, 512

# Cria uma imagem nova com fundo branco
imagem = Image.new('RGBA', (l, a), 'white')

# O objeto desenho age sobre o objeto imagem
desenho = ImageDraw.Draw(imagem)

# Calcula a largura da faixa de cor
faixa = l / 256

# Desenha um gradiente de cor
for i in xrange(0, l):

    # Calcula a cor da linha
    rgb = (0.25 * i / faixa, 0.5 * i / faixa, i / faixa)
    cor = '#%02x%02x%02x' % rgb

    # Desenha uma linha colorida
    # Primeiro argumento é uma tupla com
    # as coordenadas de inicio e fim da linha
    desenho.line((0, i, l, i), fill=cor)

# Copia e cola recortes invertidos do gradiente
for i in xrange(l, l / 2, -l / 10):

    # Tamanho do recorte
    area = (l - i, a - i, i, i)
    # Copia e inverte
    flip = Image.FLIP_TOP_BOTTOM
    recorte = imagem.crop(area).transpose(flip)

    # Cola de volta na imagem original
    imagem.paste(recorte, area)

# Salva como arquivo PNG
imagem.save('desenho.png', 'PNG')

# Para exibir o gráfico no IPython Notebook
from IPython.display import Image as Img
img = Img(filename='desenho.png')
img
Out[1]:

É possível calcular os dados da imagem com o NumPy e usar o PIL para gerar a imagem real.

Exemplo com modulação de amplitude de onda:

In [3]:
"""
Criando uma imagem usando NumPy
"""

import numpy
from PIL import Image

def coords(xy, tam):
    """
    coords(xy, tam) => x, y
    Transforma as coordenadas normalizadas
    para o centro da imagem de tamanho "tam"
    """
    X, Y = tam

    x = int((1. + xy[0]) * (X - 1.) / 2.)
    y = int((1. + xy[1]) * (Y - 1.) / 2.)
    return x, y

if __name__ == '__main__':

    # Dimensões
    tam = 900, 600

    # Cria um arranjo apenas com zeros
    # com as dimensões transpostas
    # "tam[::-1]" é o reverso de "tam" e 
    # "(3,)" é uma tupla para representar "(R, G, B)"
    imag = numpy.zeros(tam[::-1] + (3,), numpy.uint8)

    # Preenche de branco
    imag.fill(255)

    # Dados do eixo X
    xs = numpy.arange(-1., 1., 0.00005)

    # Onda moduladora
    # Valor médio, amplitude e freqüência
    vmed = 0.6
    amp = 0.4
    fm = 2.
    mod = vmed + amp * numpy.cos(fm * numpy.pi * xs)

    # Frequência da portadora
    fc = 8.
    # Número de curvas internas
    ci = 32.
    # Contador
    i = 0

    # Gera um conjunto de curvas
    for delta_y in numpy.arange(1. / ci, 1. + 1. / ci,
        1. / ci):

        # Dados do eixo Y
        ys = mod * delta_y * numpy.sin(fc * numpy.pi * xs)

        # Pares x, y
        xys = zip(xs, ys)

        # Desenha a portadora e as curvas internas
        # Para cada ponto na lista
        for xy in xys:

            # Coordenadas invertidas
            x, y = coords(xy, tam)[::-1]

            # Aplica cor a xy
            imag[x, y] = (250 - 100 * delta_y,
                150 - 100 * delta_y,
                50 + 100 * delta_y)
            i += 1

    for x, y in zip(xs, mod):
        # Desenha as envoltórias
        imag[coords((x, y), tam)[::-1]] = (0, 0, 0)
        imag[coords((x, -y), tam)[::-1]] = (0, 0, 0)

        # Bordas superior e inferior
        imag[coords((x, 1.), tam)[::-1]] = (0, 0, 0)
        imag[coords((x, -1.), tam)[::-1]] = (0, 0, 0)
        i += 4

    for y in xs:

        # Bordas laterais
        imag[coords((1., y), tam)[::-1]] = (0, 0, 0)
        imag[coords((-1., y), tam)[::-1]] = (0, 0, 0)
        i += 2

    print i, 'pontos calculados'

    # Cria a imagem a partir do arranjo
    imagem = Image.fromarray(imag, 'RGB')
    imagem.save('curvas.png', 'PNG')
1520000 pontos calculados
In [4]:
# Para exibir o gráfico no IPython Notebook
from IPython.display import Image as Img
img = Img(filename='curvas.png')
img
Out[4]:

Observações:

  • A biblioteca trabalha com o conceito de bandas, que são camadas que compõem a imagem. Cada imagem pode ter várias bandas, mas todas devem ter as mesmas dimensões e profundidade.
  • A origem do sistema de coordenadas é no canto superior esquerdo.

Além do PIL, também é possível usar o ImageMagick com Python. Com uma proposta diferente, ImageMagick é um conjunto de utilitários para processar imagens raster, feito basicamente para uso através de linha de comando ou através de linguagens de programação.

In [1]:
 
Out[1]: