Filtros lineares invariantes à translação - implementação

Uma teoria muito importante e também muito estudada é a de filtros ou transformações lineares invariantes à translação. Quando um filtro tiver as seguintes três propriedades:

  1. A filtragem da soma de duas imagens é igual à soma das imagens filtradas

  2. A filtragem de uma imagem multiplicada por uma constante é igual à multiplicação pela constante da imagem transformada

  3. A filtragem de uma imagem transladada é igual à translação da imagem filtrada

diz-se, então, que o filtro é linear e invariante à translação. Ainda mais importante é o seguinte:

Qualquer filtro linear e invariante à translação pode ser implementado por uma convolução.

Esta afirmação facilita em muito a eficiência de um programador de processamento de imagens, pois a implementação de todos os filtros lineares invariantes à translação é feita com apenas um função, bastando variar o seu parâmetro.

Apesar do nome ser estranho para quem nunca ouviu falar em convolução, a sua versão mais simples já foi certamente vista por todos: um filtro de média móvel é uma convolução.

Introdução à forma como a convolução está implementada utilizando NumPy

Assim, o objetivo desta atividade é olharmos com detalhes como está implementada a função

Existem duas dificuldades maiores relativas à implementação da convolução discreta:

  1. É uma operação de vizinhança, o que torna o processamento em NumPy potencialmente mais difícil. Uma das dicas para se implementar a convolução evitando ou diminuindo o número de laços explícitos é operar na imagem transladada e não na translação da janela.

  2. Como toda operação de vizinhança, é preciso definir qual é a vizinhança de um pixel que esteja nos limites da imagem, normalmente chamado borda da imagem. Existem várias soluções possíveis para este tratamento. A que usaremos aqui, no caso da convolução linear é supor que a imagem é infinita e os pixels que estão fora do seu shape tem seus valores zerados.

Na realidade, a convolução é uma operação entre duas imagens quaisquer, f e h, onde f é a imagem propriamente dita e h é o filtro linear. h tem muitas denominações: núcleo ou máscara da convolução, função de espalhamento, entre outras. Normalmente a imagem f é grande e núcleo h é pequeno. Por exemplo, se quero um filtro de média móvel onde a vizinhança ou janela a
aplicar-se a média consiste no pixel em questão e os seus dois pixels vizinhos à esquerda, a imagem h será um array de 3 elementos: [1/3, 1/3, 1/3]. No Python/NumPy podemos calcular esta média móvel de duas formas principais:

  • Varredura em todos os pixels e para cada pixel seleciona-se os pixels vizinhos por fatiamento para o cálculo da média.

  • Soma da imagem f com suas translações de 1 e 2 pixels para a direita, dividindo o resultado por 3.

Esta mudança de varredura da imagem para varredura nos elementos de h é possível porque a convolução possui uma propriedade importante: ela é comutativa. Isto é, fazer a convolução de f por h é igual a fazer a convolução de h por f. Cuidado, pois nem toda implementação de convolução respeita esta propriedade. As implementações da convolução feitas na toolbox ia898 são todas comutativas.

Exemplo de média móvel de 3 pixels vizinhos

Veja o exemplo a seguir desta implementação da média dos 3 pixels mencionada acima:


In [2]:
import numpy as np

f = np.arange(25).reshape(5,5)
h = np.array([1./3, 1./3, 1./3])
print('f:\n', f)
print('h:', h.round(2))


f:
 [[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]]
h: [ 0.33  0.33  0.33]

In [3]:
H,W = f.shape
g = np.zeros((H,W+2))
g[:,:-2] = f * h[0]
print('g:\n', g.round(2))
g[:,1:-1] += f * h[1]
print('g:\n', g.round(2))
g[:,2:] += f * h[2]
print('g:\n', g.round(2))


g:
 [[ 0.    0.33  0.67  1.    1.33  0.    0.  ]
 [ 1.67  2.    2.33  2.67  3.    0.    0.  ]
 [ 3.33  3.67  4.    4.33  4.67  0.    0.  ]
 [ 5.    5.33  5.67  6.    6.33  0.    0.  ]
 [ 6.67  7.    7.33  7.67  8.    0.    0.  ]]
g:
 [[  0.     0.33   1.     1.67   2.33   1.33   0.  ]
 [  1.67   3.67   4.33   5.     5.67   3.     0.  ]
 [  3.33   7.     7.67   8.33   9.     4.67   0.  ]
 [  5.    10.33  11.    11.67  12.33   6.33   0.  ]
 [  6.67  13.67  14.33  15.    15.67   8.     0.  ]]
g:
 [[  0.     0.33   1.     2.     3.     2.33   1.33]
 [  1.67   3.67   6.     7.     8.     5.67   3.  ]
 [  3.33   7.    11.    12.    13.     9.     4.67]
 [  5.    10.33  16.    17.    18.    12.33   6.33]
 [  6.67  13.67  21.    22.    23.    15.67   8.  ]]

Tutorial explicando passo-a-passo

Veja o

Tutorial das propriedades e uso da convolução

Adicionalmente, a convolução possui várias propriedades úteis tanto para o seu melhor entendimento como para uma programação e uso mais eficientes. Veja a ilustração de algumas propriedades da convolução no


In [ ]: