Material de Auxílio Didático
EA978 - Sistemas de informações gráficas
Primeiro semestre de 2003
2o Projeto
Atividade 2 - Processamento de Imagens
Objetivo: Aplicar um filtro de suavização sobre uma imagem da cena wireframe gerada no projeto anterior e detectar as bordas da imagem usando filtros por derivadas.
Filtragem de suavização (antialiasing e blurring)
Use o seguinte esqueleto de código para implementar essa tarefa: antialias.c
O procedimento de filtragem apresentado no esqueleto de código é uma convolução no domínio espacial usando o buffer de acumulação do OpenGL. O funcionamento dessa estratégia pode ser entendido através de uma comparação com o método de convolução tradicional. No método tradicional, o kernel é "carimbado" sobre cada pixel da imagem e em cada um desses passos os valores do kernel são multiplicados com os valores dos pixels alinhados com essa matriz. Em cada passo somam-se os produtos para gerar o valor do pixel de saída. Com o método do buffer de acumulação, a imagem de entrada é acumulada sucessivas vezes no buffer através do comando glAccum(GL_ACCUM, valor), onde "valor" corresponde aos valores do kernel do filtro. Cada imagem acumulada é deslocada (na vertical e na horizontal) um número de pixels que corresponde ao índice do kernel que contém o valor atual do filtro. Em resumo, no método tradicional o kernel é deslocado no domínio da imagem, enquanto no método do buffer de acumulação, o buffer de acumulação é deslocado no domínio do kernel. A convolução não é feita em um pixel de cada vez. Ao invés disso, em cada passo todos os pixels são convoluídos de uma só vez com a contribuição de um dos elementos do kernel. Por exemplo, se o tamanho do kernel é 3x3, a imagem deve ser acumulada 9 vezes, cada uma tendo sido multiplicada por um valor do kernel. Obviamente, o resultado da convolução no método tradicional e no buffer de acumulação é o mesmo.
O esqueleto de código disponível acima contém os procedimentos init(), display() e reshape() que devem ser modificados com o código gerado no projeto anterior de modo a exibir a cena. Observe que na função display() não é necessário inserir o comando glutSwapBuffers(), pois ele já é chamado em outra função. Ao executar o programa, o usuário pode acessar um menu pop-up clicando com o botão direito do mouse sobre a imagem. Nesse menu é possível escolher qual filtro deve ser aplicado. Nesse exemplo dois filtros de média foram implementados: filtro box 3x3 e box 5x5. Ambos são filtros de suavização. O objetivo dessa tarefa é aplicar esses filtros na cena do projeto renderizada em wireframe. O resultado deverá ficar semelhante ao mostrado abaixo:
Cena wireframe processada por um filtro de média 3x3.
Algumas dicas de implementação são dadas abaixo:
Dicas:
Filtragem por derivadas (detecção de bordas)
Use o seguinte esqueleto de código para implementar essa tarefa: border.c
O método de filtragem proposto no esqueleto de código acima é o mesmo que o anterior, isto é, uma convolução no domínio espacial através do buffer de acumulação do OpenGL. Nesse código são mostrados dois filtros por derivadas: o filtro laplaciano e o filtro de Sobel. Algumas imagens processadas por esses filtros são mostradas abaixo. O que se pede nessa tarefa é a implementação de um filtro de detecção de bordas utilizando algum filtro por derivada, como o filtro de Roberts, Prewitt ou mesmo o filtro de Sobel e laplaciano já implementados. O filtro escolhido deve ser aplicado sobre uma imagem da cena de modo que as bordas fiquem destacadas (como mostram as figuras abaixo). As técnicas de quantização apresentadas na primeira tarefa também podem ser utilizadas para pré-processar a imagem antes de sua filtragem e dessa forma melhorar a detecção de bordas.
Imagem da cena processada por um filtro laplaciano.
Imagem da cena processada pelo filtro de Sobel.
Como no programa anterior, o esqueleto de código disponível no início da página contém os procedimentos init(), display() e reshape() que devem ser modificados com o código implementado no projeto anterior para modelar e desenhar a cena. Outras dicas de implementação são dadas a seguir:
Dicas:
É importante notar que, ao contrário do filtro laplaciano, o filtro de Sobel é um filtro de duas passagens, isto é, é um filtro composto por dois kernels. Enquanto um kernel detecta as derivadas em X, o outro detecta as derivadas em Y. É por esse motivo que, no esqueleto de código apresentado, o filtro de Sobel é apresentado como duas funções: sobelx() e sobely(). Após a filtragem da imagem original por esses dois filtros, o resultado final é a soma pixel-a-pixel em valores absolutos das duas imagens. Todo esse procedimento de filtragem por dois kernels e acumulação das imagens resultantes é mostrado na função redisplay(). Nessa função também é possível observar o uso dos comandos glReadPixels() e glDrawPixels() na leitura do resultado intermediário e na visualização da imagem acumulada. Os filtros de Roberts e Prewitts também são filtros de duas passagens e devem ser implementados de forma semelhante ao filtro de Sobel. Em redisplay(), basta copiar o código utilizado no filtro de Sobel e renomear as funções sobelx() e sobely() para as novas funções desejadas.
Autor: Harlen Costa Batagelo (harlen@dca.fee.unicamp.br)