Introdução

A filtragem é um processo bastante útil na transformação de imagens, com o intuito de melhorar a qualidade da imagem, ou destacar características da mesma. Essa atividade se propõe a expor alguns tipos de filtragem, capturando a imagem fornecida pela webcam e apresentando o resultado de um tipo de filtragem na tela do computador. Serão aplicadas diferentes filtragens, a cada uma será escolhida através do pressionamento de uma tecla específica.

Desenvolvimento da atividade

O código implementado baseia-se em apenas identificar a última tecla pressionada pelo usuário e, a partir disso, aplicar um determinado filtro até que outra tecla seja pressionada. A aplicação dos filtros se baseiam na realização de uma convolução de uma imagem com uma matriz, e cada tipo de filtro possui uma matriz característica. Dessa forma, as matrizes são definidas no código e então, usando as ferramentas oferecidas pelo OpenCV, realiza-se a filtragem da imagem.

O programa também irá implementar a aplicação de de um filtro após o outro. No caso, será feita a filtragem de uma imagem com o filtro gaussiano, e então será aplicado o filtro laplaciano a esse resultado.

Código no OpenCV

O código implementado pode ser visto a seguir:

Listagem 1. equalize.cpp
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void menu(){
  cout << "\npressione a tecla para ativar o filtro: \n"
			"a - calcular modulo\n"
    		"m - media\n"
    		"g - gauss\n"
    		"v - vertical\n"
			"h - horizontal\n"
    		"l - laplaciano\n"
    		"j - laplaciano do gaussiano\n"
			"esc - sair\n" << endl;
}

int main(int argvc, char** argv){

	float media[] 		= {	1, 1, 1,
				   			1, 1, 1,
				   			1, 1, 1 };

	float gauss[] 		= {	1, 2, 1,
				   			2, 4, 2,
				   			1, 2, 1 };

	float horizontal[]	= {-1, 0, 1,
					       -2, 0, 2,
					       -1, 0, 1 };

	float vertical[]	= {-1,-2,-1,
					    	0, 0, 0,
					    	1, 2, 1 };

	float laplacian[]	= { 0,-1, 0,
					       -1, 4,-1,
					        0,-1, 0 };

	float nada[]		= {	0, 0, 0,
					   		0, 1, 0,
					   		0, 0, 0 };

	VideoCapture video;
	Mat cap, frame, frame32f, frameFiltered, frameFiltered2;
	Mat mask(3,3,CV_32F), mask1;
	Mat result;
	double width, height, min, max;
	char key;
	int absolut;

	video.open(0);
  	if(!video.isOpened())
    	return -1;
  	width  = video.get(CV_CAP_PROP_FRAME_WIDTH);
  	height = video.get(CV_CAP_PROP_FRAME_HEIGHT);
  	cout << "largura = " << width  << "\n";
  	cout << "altura  = " << height << "\n";

  	menu();

  	namedWindow("Original",CV_WINDOW_KEEPRATIO);
  	namedWindow("Filtro",CV_WINDOW_KEEPRATIO);

  	while(1){

  		switch(key){
  			case 'm':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, media);
  					scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
      				mask = mask1;
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_media_entrada.png",frame);
  					imwrite("filtro_media_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'g' || key == 'h' || key == 'v' || key == 'l' || key == 'j') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			case 'g':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, gauss);
  					scaleAdd(mask, 1/16.0, Mat::zeros(3,3,CV_32F), mask1);
      				mask = mask1;
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_gaussiano_entrada.png",frame);
  					imwrite("filtro_gaussiano_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'm' || key == 'h' || key == 'v' || key == 'l' || key == 'j') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			case 'h':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, horizontal);
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_horizontal_entrada.png",frame);
  					imwrite("filtro_horizontal_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'm' || key == 'g' || key == 'v' || key == 'l' || key == 'j') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			case 'v':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, vertical);
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_vertical_entrada.png",frame);
  					imwrite("filtro_vertical_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'm' || key == 'g' || key == 'h' || key == 'l' || key == 'j') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			case 'l':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, laplacian);
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//minMaxLoc(frameFiltered, &min, &max);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					//result = result + abs(min);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_laplaciano_entrada.png",frame);
  					imwrite("filtro_laplaciano_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'm' || key == 'g' || key == 'h' || key == 'v' || key == 'j') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			case 'j':
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro gaussiano
  					mask = Mat(3, 3, CV_32F, gauss);
  					scaleAdd(mask, 1/16.0, Mat::zeros(3,3,CV_32F), mask1);
      				mask = mask1;
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Cálculo do filtro laplaciano
  					mask = Mat(3, 3, CV_32F, laplacian);
  					filter2D(frameFiltered, frameFiltered2, frame32f.depth(), mask, Point(1,1), 0);

  					//minMaxLoc(frameFiltered, &min, &max);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered2 = abs(frameFiltered2);

  					//Apresentação do resultado
  					frameFiltered2.convertTo(result, CV_8U);
  					//result = result + abs(min);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					imwrite("filtro_lapl_gaussiano_entrada.png",frame);
  					imwrite("filtro_lapl_gaussiano_saida.png",result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 'm' || key == 'g' || key == 'h' || key == 'v' || key == 'l') break;
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  				}
  				break;

  			default:
  				while(1){
  					//Captura da imagem
  					video >> cap;
  					cvtColor(cap, frame, CV_BGR2GRAY);		//Deixa imagem em grayscale
  					flip(frame, frame, 1);					//Flip para ter efeito de espelho na webcam
  					frame.convertTo(frame32f, CV_32F);		//Para realizar cálculos com numeros decimais, converte para float

  					//Cálculo do filtro
  					mask = Mat(3, 3, CV_32F, nada);
  					filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);

  					//Teste se a opção de absoluto está habilitada
  					if(absolut)
  						frameFiltered = abs(frameFiltered);

  					//Apresentação do resultado
  					frameFiltered.convertTo(result, CV_8U);
  					imshow("Original", frame);
  					imshow("Filtro", result);

  					//Leitura do teclado
  					key = (char) waitKey(10);
  					if(key == 27) return 0;
  					if(key == 'a')
  						absolut=!absolut;
  					if(key !=  0) break;

  				}
  				break;
  		}
  	}
  	return 0;
}

O código está sempre verificando o pressionamento de uma tecla para mudar o tipo de filtro a ser aplicado. Tendo a escolha do filtro realizada, o código entra em um loop de captura, aplicação do filtro e exibição do resultado, até que outra tecla seja pressionada.

Para a realização da filtragem, utilizou-se a função filter2D(). Essa função recebe como argumento a imagem a ser processada, a matriz de convolução, onde o resultado será armazenado, etc. Para mais informações, consultar a documentação do OpenCV. E, para a criação da matriz de convolução, também foi usada a função [scaleAdd]. Assim, a diferença entre cada tipo de filtro é apenas a fonte de dados para a criação da matriz de convolução. Também é utlizada a opção de calcular o valor absoludo do resultado, que deixa alguns detalhes mais notáveis.

Resultados

Podemos ver o resultado da aplicação dos filtros a seguir:

450
Figura 1. Entrada dos filtros.
450
Figura 2. Saída do filtro da média.
450
Figura 3. Saída do filtro Gaussiano.
450
Figura 4. Saída do filtro de borda vertical.
450
Figura 5. Saída do filtro de borda horizontal.
450
Figura 6. Saída do filtro laplaciano.
450
Figura 7. Saída do filtro laplaciano do gaussiano.

Podemos então observar que cada filtro destaca diferentes características de uma imagem, desde realizar a suavização de uma imagem até detectar bordas que ocorram na mesma. Para o caso onde é aplicado um filtro após o outro (Laplaciano do Gaussiano, por exemplo), vê-se que as característica de ambos os filtros se mantém. Isso pode ser útil para diversas aplicações, onde podemos combinar características de filtros para obter um resultado desejado.

No caso do filtro Laplaciano do Gaussiano, obtemos a vantagem de ter uma imagem mais suavizada. Quando aplicamos apenas o filtro laplaciano isolado, as bordas são detectadas, mas com um certo nível de ruídos. Quando é aplicado uma suavização antes (filtro Gaussiano), o resultado do Laplaciano torna-se mais "limpo", sem muito ruído, aumentando assim a qualidade da filtragem.