Processamento de Imagens

O processamento digital de imagens usa intensivamente vetores e matrizes. Uma imagem é geralmente representada como uma matriz de pixels, onde cada pixel é descrito por um ou mais valores inteiros que indicam seus níveis de cor ou de luminosidade. Por isso, processar imagens é uma ótima forma de exercitar o uso de matrizes.

O formato PGM

Para facilitar a leitura e escrita dos arquivos de imagem, neste projeto será adotado o formato de imagem Portable Gray Map (PGM), um formato de imagem em níveis de cinza bem simples e fácil de ler/escrever. Boa parte dos programas de tratamento de imagens reconhece o formato PGM.

Eis um exemplo de imagem no formato PGM:

feep.pgm
P2
# this is a PGM image
24 7
15
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
0  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  0  0  0  0
0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
Existem duas variantes do formato PGM: PGM plain (P2) e PGM raw (P5); ambas devem ser lidas pelos filtros implementados neste projeto. Mais detalhes sobre essas variantes podem ser obtidas nesta página.

Alguns exemplos de imagens no formato PGM para usar no projeto:

Filtros de imagem

Este projeto visa construir filtros simples para imagens digitais em níveis de cinza, no formato PGM (P2 e P5) com pixels de 8 bits (1 byte).


Filtro negativo

O filtro negativo consiste em converter cada pixel da imagem em seu complemento. Sendo max o valor máximo para um pixel na imagem, o complemento de um pixel com valor v seria max-v.

Forma de chamada:

pgmnega -i input -o output

Exemplo de entrada e de saída:


Filtro de rotação simples

O filtro de rotação gira uma imagem em 90° no sentido horário.

Forma de chamada:

pgmrotacao -i input -o output

Exemplo de entrada e de saída:


Filtro de rotação livre

O filtro de rotação gira uma imagem em um ângulo θ (>0) no sentido horário, em relação ao seu centro (vide rotação de imagens 2D).

Requisitos:

Forma de chamada:

pgmrotacao -a N -i input -o output

Exemplo de entrada e de saída para θ = 30º:


Filtro de limiar

O filtro de limiar (threshold filter) converte uma imagem em tons de cinza em um imagem em preto-e-branco (2 cores). A forma mais simples de fazer isso é comparar o valor de cada pixel com um limiar pré-definido: se o valor do pixel for igual ou superior ao limiar, ele vira branco (v=max), caso contrário ele vira preto (v=0).

Forma de chamada:

pgmlimiar -l N -i input -o output

onde N é um limiar real entre 0.0 (0% do valor máximo) e 1.0 (100% do valor máximo). Caso o limiar não esteja definido, assume-se que seja 50%.

Exemplo de entrada e de saídas com limiar de 50% e 75%:


Filtro de redução de ruído pela média

O filtro da média é usado para para “limpar” uma imagem, ou seja, reduzir o seu nível de ruído. O algoritmo é básico bem simples: para cada pixel, seu valor deve ser substituído pela média de todos os 9 pixels vizinhos (incluindo ele mesmo).

Deve ser tomado cuidado especial ao tratar os pixels nas primeiras e últimas linhas ou colunas, pois eles não têm todos os vizinhos. Nesses casos, devem ser considerados no cálculo somente os vizinhos existentes.

A imagem processada deve ser armazenada em uma segunda matriz, para que não se misturem valores novos e velhos no cálculo dos pixels.

Forma de chamada:

pgmmedia -i input -o output

Exemplo de entrada e de saída:


Filtro da mediana

O filtro da mediana reduz o nível de ruído de uma imagem sem prejudicar muito sua nitidez. Este filtro consiste basicamente em substituir o valor de um pixel pelo valor da mediana de seus pixels vizinhos. O número de vizinhos a considerar é definido por uma máscara, ou seja, a matriz de vizinhos que circunda o pixel a tratar (incluindo ele mesmo):

Máscaras de filtragem

Requisitos:

Forma de chamada:

pgmmediana -m N -i input -o output

Exemplo de entrada e de saída:

Imagem originalImagem filtrada

Filtro LBP (Local Binary Patterns)

LBP (Local Binary Patterns) ou Padrões Binários Locais, é um descritor de textura muito utilizado em algoritmos de reconhecimento de imagens. O funcionamento desse filtro é simples: para cada pixel de uma imagem com 256 níveis de cinza, seleciona-se uma vizinhança de 8 pixels. O valor LBP é então calculado para o pixel central e armazenado na imagem destino, que possui as mesmas dimensões da imagem original.

Para o cálculo do LBP de um pixel, usa-se uma máscara com valores 2n, com n = {0, …, 7}, ou seja: 1, 2, 4, 8, 16, 32, 64 e 128. O exemplo abaixo ilustra o cálculo de LBP para o pixel com valor 6:

Cálculo de LBP

Considere o pixel central com valor 6 e seus oito vizinhos na imagem (a). O valor do pixel central é utilizado como limiar e todos os pixels com valor de intensidade maior ou igual a ele recebem 1, caso contrário, 0 (b). Esses valores são então multiplicados pela máscara (c ), resultando então nos valores apresentados em (d). O valor LBP do pixel 6 é dado pela soma desses valores, ou seja LPB = 1+8+32+128 = 169. Note que essa codificação garante valores LBP entre 0 e 255.

Forma de chamada:

pgmlbp -i input -o output

Exemplo de entrada e de saída:

LBP inputLBP output


Outros filtros (ideias)


Atividade

Uso de arquivos

Essas opções podem ser usadas em qualquer combinação, ou seja:

// entrada e saída em arquivos
pgmmediana -i input.pgm -o output.pgm
pgmmediana -o output.pgm -i input.pgm

// entrada em arquivo, saída em stdout, vice-versa ou ambos
pgmmediana -i input.pgm > output.pgm
pgmmediana -o output.pgm < input.pgm
pgmmediana < input.pgm > output.pgm

// as opções podem estar em qualquer ordem
pgmmediana -m 5 -i input.pgm -o output.pgm
pgmmediana -i input.pgm -m 5 -o output.pgm
pgmmediana -o output.pgm -i input.pgm -m 5

Estrutura do código-fonte

O código-fonte deve ser estruturado em arquivos .c e .h que agrupem as diversas funcionalidades. A figura abaixo traz uma uma sugestão de estrutura para o código-fonte (as setas correspondem a includes):