Prazo de entrega recomendado:
Iremos construir um editor interativo de imagens capaz de realizar diversas operações, como destaque e aplicação de filtros. Para isso, será necessário manipular matrizes.
A visão é um dos sentidos mais complexos do ser humano. Ela permite capturar as mais diferentes imagens ao nosso redor. Nós percebemos essas imagens através da luz, que traz uma quantidade abundante de dados. Com o tempo, aprendemos a processar esses dados, identificar e classificar objetos de maneira tão simples, que encaramos a visão de maneira natural e a entendemos como um processo elementar.
Replicar esse processo com os computadores, no entanto, é desafiador. A primeira dificuldade ocorre ao representar uma imagem na memória de nosso computador, que nada mais é do que uma sequência de bits. Depois disso, ainda precisamos saber como processar esses dados para manipular a imagem e extrair informações.
Tradicionalmente, representamos uma imagem como uma matriz de pixels. Cada pixel corresponde a uma cor ou à intensidade da luz refletida em uma região pequenininha da imagem correspondente ao pixel. Assim, manipular essa imagem nada mais é do que realizar operações sobre essa matriz.
Nesta tarefa, representaremos imagens no formato PGM, que é utilizado para armazenar imagens em escala de cinza sem compressão de dados em um arquivo de texto. Nesse formato, uma imagem é uma matriz de inteiros no intervalo $[0, …, 255]$. Cada entrada da matriz representa a intensidade da luz em um determinado pixel, de forma que, quanto mais próximo de $0$, mais escuro é o pixel e, quanto mais próximo de $255$, mais claro ele é.
O formato PGM começa com uma linha com o texto P2
, que representa
uma imagem em escala de cinza. Depois, pode haver um comentário
arbitrário, começando com #
e a próxima linha deve conter dois
números representando, respectivamente, a largura e a altura da
matriz. Na próxima linha há o número $255$, que indica a intensidade
do branco. Em seguida deve haver uma sequência de números inteiros,
cada um representando uma entrada da matriz, lidos da esquerda para a
direita e de cima para baixo. Abaixo, há uma imagem e o arquivo PGM
correspondente.
[Arquivo content/tarefas/07-calculadora-imagens/mc102.pgm não existe]
Calculadora de imagens
Sua tarefa é construir um programa interativo calculadora_imagens.py
capaz de carregar imagens de arquivos PGM da pasta dados
, realizar operações sobre
elas e gravar uma imagem resultante em um novo arquivo dentro da pasta
teste
. O programa mantém em memória um banco de imagens, cada uma
identificada por um índice inteiro começando em $0$. Cada operação pode
criar, acessar e modificar uma ou mais imagens desse banco de imagens.
Entrada
Cada linha da entrada conterá o nome de uma operação a ser realizada e uma sequência de parâmetros correspondentes. Por exemplo, a entrada abaixo carrega duas fotos na memória. A segunda foto é transformada em preto e branco utilizando um limiar de $50$. Em seguida, a primeira imagem é modificada, utilizando a segunda como máscara. Finalmente, a imagem resultante é gravada em um novo arquivo.
carregar dados/selfie.pgm
carregar dados/disco.pgm
binarizar 1 50
multiplicar 0 1
gravar testes/avatar.pgm 0
A seguir mostramos o que esta acontecendo nos passos anteriores.
carregar selfie.pgm | carregar disco.pgm | binarizar 1 50 | multiplicar 0 1 |
Saída
A cada operação processada, deverá ser mostrada uma ou mais linhas na saída, dependendo do comando. A saída para o exemplo acima será.
Carregado arquivo dados/selfie.pgm em imagem 0.
Carregado arquivo dados/disco.pgm em imagem 1.
Imagem 1 modificada: 24244 pixels maiores que zero.
Imagem 0 modificada: 24244 pixels maiores que zero.
Gravado arquivo testes/avatar.pgm com imagem 0.
Operações pixel a pixel
Podemos realizar operações entre duas imagens pixel por pixel. Por exemplo, se multiplicamos as intensidades de duas imagens, alguns pixels terão a intensidade aumentada e outros, diminuída. Podemos usar essa operação para destacar determinadas regiões de uma imagem e esconder as demais. Também podemos realizar operações lógicas, como OR, AND, XOR, SUB. Elas servem como instrumento para extração de componentes da imagem, descrever formas de uma região, como fronteiras, esqueletos e fecho convexo.
Normalizar
Às vezes uma imagem pode estar muito escura porque todos os pixels são menores que 255; ou muito clara, porque todos os pixels são maiores que 0. Para ajustar o branco e o preto, podemos normalizar os valores dos pixels de uma imagem $A$ linearmente. Para isso, você deve descobrir os valores máximo e mínimo de intensidade na imagem e interpolar o valor de cada pixel de forma que o mais intenso seja $255$ e o mais escuro seja $0$. Descarte frações, caso a intensidade resultante da normalização não seja inteira.
Entrada
carregar dog.pgm
normalizar <id imagem a>
Saída
Imagem <id imagem a> modificada: xx pixels maiores que zero.
Binarizar
Dado um limiar, defina como 0
todos pixels com valor menor ou igual
ao limiar e como 255
os demais.
Entrada
binarizar <id imagem> <limiar>
Saída
Imagem <id imagem> modificada: xx pixels maiores que zero.
carregar impressao.pgm | binarizar 0 91 |
Multiplicar
Dadas duas imagens com as mesmas dimensões, modifique a primeira de forma que o novo pixel tenha o produto dos pixels correspondentes nas imagens originais. Repare que a imagem resultante pode ter pixels com intensidades maiores do que $255$.
Entrada
carregar dog.pgm
carregar mascara3.pgm
multiplicar <id imagem a> <id imagem b>
Saída
Imagem <id imagem a> modificada: xx pixels maiores que zero.
carregar dog.pgm | mascara3.pgm | multiplicar 0 1 |
Somar
Computa a média dos pixels de duas matrizes de mesma dimensão, descartando a fração não inteira.
Entrada
somar <id imagem a> <id imagem b>
Saída
Imagem <id imagem a> modificada: xx pixels maiores que zero.
carregar dog.pgm | carregar eiffel.pgm | somar 0 1 |
Operações lógicas
Realiza operações lógicas pixel por pixel entre duas imagens com a
mesma dimensão e altera a primeira. Nessas operações, garantimos que
todo pixel tem valor 0
ou 255
, representando verdadeiro e
falso, respectivamente.
Entrada
<OPERADOR> <id imagem a> <id imagem b>
O nome do comando OR
, AND
, XOR
, SUB
.
Saída
Imagem <id imagem a> modificada: xx pixels maiores que zero.
Filtros em imagens
Uma operação comum em imagens é a aplicação de filtros digitais. Abaixo, aplicamos diferentes filtros em uma imagem de exemplo, cada um com um objetivo.
suavização de imagem | remover ruído | melhorar a imagem | destacar bordas |
Para aplicar um filtros, realizamos uma operação chamada convolução, que consiste em percorrer a matriz original sobrepondo cada pixel com uma matriz menor (chamada filtro ou kernel). Para computar o valor resultante em cada pixel, multiplicamos os pixels sobrepostos e somamos.
Suponha que o kernel seja a matriz à esquerda, enquanto a matriz à direita seja uma parte da imagem, representando os valores dos pixels. Neste momento, estamos calculando o pixel da posição cujo valor na imagem original é 25. O valor deste pixel na imagem com filtro, após a convolução, será
$$ a \times 10 + b \times 10 + c \times 10 + d \times 10 + e \times 25 + f \times 10 + g \times 10 + h \times 10 + i \times 10. $$
Quando realizamos esse procedimento em todos os pixels da imagem, temos a imagem filtrada.
A imagem filtrada deve substituir a imagem. Repare que não é possível aplicar o filtro nos pixels próximos da borda da imagem, quando o kernel não couber completamente sobre a imagem. Nesses casos, defina o pixel da imagem filtrada como preto.
Entrada
fitrar <id> <nome do filtro>
O <nome do filtro>
define qual o kernel será utilizado. Você precisa
implementar pelo menos o kernel Laplaciano
(para detectar bordas).
dado pela matriz $L$. Umo outro filtro comum é o Gaussiano
(para
suavizado), dado pela matriz $G$. Note que, após aplicar um filtro, o
valor de um pixel pode ser negativo ou maior que $255$.
$$ L = \begin{pmatrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \end{pmatrix} $$
$$ G = \begin{pmatrix} 1 & 4 & 7 & 4 & 1 \\ 4 & 16 & 26 & 16 & 4 \\ 7 & 26 & 41 & 26 & 7 \\ 4 & 16 & 26 & 16 & 4 \\ 1 & 4 & 7 & 4 & 1 \end{pmatrix} $$
Saída
Imagem <id> modificada: xx pixels maiores que zero.
Dicas
-
Pode ser útil olhar para a imagem em um visualizador. Em distribuições GNU/Linux, a maioria dos visualizadores pode abrir arquivos PBM facilmente. Em outros sistemas operacionais, você talvez precise instalar um visualizador ou editor de imagens, ou pode usar uma ferramenta de conversão online, como essa. Se seu visualizador não funcionar, você tentar fazer download desse.
-
Vocês também podem visualizar uma imagem PBM ou PGM pequena com ferramentas online, e.g., NetpbmViewer.
-
Não tente fazer tudo de uma vez. Sempre escreva cada um dos algoritmos com calma antes de programá-lo. Experimente começar se preocupando com a leitura da entrada e saída. Pesquise sobre como ler todas as linhas da entrada padrão. Por exemplo, se você tentar ler uma linha quando a entrada já acabou, ocorrerá uma exceção. Reveja os exercícios da lista 5 para relembrar como tratar exceções.
Critérios
Para obter conceito B
é obrigatório implementar pelo menos os
comandos carregar, gravar, binarizar, normalizar, multiplicar e
aplicar o filtro laplaciano. Para obter conceito A
, implemente
também as operações lógicas AND e SUB.
Correção
Esta tarefa será corrigida automaticamente sempre que você realizar um
git push
. Depois de terminada a tarefa, deve-se utilizar o botão na
interface de notas para solicitar a correção de um monitor.