1. Objetivo

Esse projeto visa nos permitir aprender mais sobre um filtro que realça bordas e detalhes em objetos: O filtro laplaciano do gaussiano.

2. O Laplaciano do Gaussiano

Como vimos nas aulas da disciplina, o filtro Laplaciano é um filtro que implementa uma derivada de segunda ordem da imagem, dessa forma detectando regiões de alta variação de cor, ou seja, bordas. Geralmente o filtro Laplaciano é aplicado após passarmos um filtro suavizante, para eliminarmos os ruídos. Um exemplo de filtro suavizante é o filtro Gaussiano.

O filtro gaussiano pode ser equacionado como uma distribuição normal, e pode ser implementado digitalmente com um kernel:

\$G_\sigma (x,y) = \frac{1}{\sqrt{2*\pi*\sigma^2}}*e^({\frac{-(x^2+y^2)}{2*\sigma^2}}) \Leftrightarrow \$ \$\begin{bmatrix} 1 & 2 & 1\\ 2 & 4 & 2\\ 1 & 2 & 1 \end{bmatrix}\$

Quando aplicamos o laplaciano (\$\nabla ^2 f(x,y)\$), obtemos que o laplaciano do gaussiano pode ser equacionado como:

\$LoG = \frac{-1}{\pi*\sigma^4}*(1 - \frac{x^2+y^2}{2*\sigma^2} )*e^({\frac{-(x^2+y^2)}{2*\sigma^2}}) \$

que pode ser representado em um kernel 5x5 como:

\$\begin{bmatrix} 0 & 0 & 1 & 0 & 0\\ 0 & 1 & 2 & 1 & 0\\ 1 & 2 & -16 & 2 & 1\\ 0 & 1 & 2 & 1 & 0\\ 0 & 0 & 1 & 0 & 0 \end{bmatrix}\$

Também podemos realizar o filtro laplaciano do gaussiano aplicando, na imagem original, o filtro gaussiano seguido do filtro laplaciano. Aplicaremos os dois e compararemos os resultados.

3. Código Base

Nosso código tem como base o algoritmo implementado pelo professor Agostinho, disponível aqui. Explicaremos apenas as funcionalidades adicionais implementadas.

4. O Código

O código utilizado é mostrado na listagem abaixo.

lapgauss.cpp
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void printmask(Mat &m){
  for(int i=0; i<m.size().height; i++){
    for(int j=0; j<m.size().width; j++){
      cout << m.at<float>(i,j) << ",";
    }
    cout << endl;
  }
}

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"
      "z - laplaciano do gaussiano 2\n"
      "p - laplaciano do gaussiano\n"
      "c - adicionar LG\n"
      "d - adicionar LG2\n"
      "f - printar\n"
      "esc - sair\n";
}

int main(int argvc, char** argv){
  VideoCapture video;
  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 lapgauss[]={0,0,1,0,0,
                     0,1,2,1,0,
                    1,2,-16,2,1,
                     0,1,2,1,0,
                     0,0,1,0,0};
  float lapgauss1[]={0,1,1,2,2,2,1,1,0,
                     1,2,4,5,5,5,4,2,1,
                     1,4,5,3,0,3,5,4,1,
                     2,5,3,-12,-24,-12,3,5,2,
                     2,5,0,-24,-40,-24,0,5,2,
                     2,5,3,-12,-24,-12,3,5,2,
                     1,4,5,3,0,3,5,4,1,
                     1,2,4,5,5,5,4,2,1,
                     0,1,1,2,2,2,1,1,0};

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

  Mat cap, frame, frame32f, frameFiltered;
  Mat mask(3,3,CV_32F), mask1;
  Mat result, result1;
  double width, height;
  int absolut;
  char key;
  bool lg=0,addLG=0,addLG2=0,lp = 0;

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

  namedWindow("filtroespacial",1);

  mask = Mat(3, 3, CV_32F, media);
  absolut=1; // calcs abs of the image

  menu();
  for(;;){
    video >> cap;
    cvtColor(cap, frame, CV_BGR2GRAY);
    flip(frame, frame, 1);
    imshow("original", frame);
    frame.convertTo(frame32f, CV_32F);
    filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);
    if(absolut){
      frameFiltered=abs(frameFiltered);
    }
    if(lg){
      filter2D(frame32f,frameFiltered,frame32f.depth(),mask,Point(2,2),0);
      if(addLG2) frameFiltered = frameFiltered + frame32f;
    }
    if(lp){
      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);
      mask = Mat(3, 3, CV_32F, laplacian);
      filter2D(frameFiltered, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);
      if (addLG) frameFiltered = frameFiltered + frame32f;
    }
    frameFiltered.convertTo(result, CV_8U);
    imshow("filtroespacial", result);
    key = (char) waitKey(10);
    if( key == 27 ) break; // esc pressed!
    switch(key){
    case 'a':
	  menu();
      absolut=!absolut;
      break;
    case 'm':
	  menu();
      mask = Mat(3, 3, CV_32F, media);
      scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
      mask = mask1;
      printmask(mask);
      break;
    case 'g':
	  menu();
      mask = Mat(3, 3, CV_32F, gauss);
      scaleAdd(mask, 1/16.0, Mat::zeros(3,3,CV_32F), mask1);
      mask = mask1;
      printmask(mask);
      break;
    case 'h':
	  menu();
      mask = Mat(3, 3, CV_32F, horizontal);
      printmask(mask);
      break;
    case 'v':
	  menu();
      mask = Mat(3, 3, CV_32F, vertical);
      printmask(mask);
      break;
    case 'l':
	  menu();
      mask = Mat(3, 3, CV_32F, laplacian);
      printmask(mask);
      break;
    case 'z':
      menu();
      mask = Mat(5,5,CV_32F,lapgauss);
      printmask(mask);
      lg = !lg;
      break;
    case 'c':
      menu();
      mask = Mat(3,3,CV_32F,noMask);
      addLG = !addLG;
      lp = addLG;
      break;
    case 'p':
      menu();
      mask = Mat(3,3,CV_32F,noMask);
      lp = !lp;
      break;
    case 'd':
      menu();
      mask = Mat(3,3,CV_32F,noMask);
      addLG2 = !addLG2;
      lg = addLG2;
      break;
    case 'f':
      imwrite("in.png",frame);
      imwrite("out.png",result);
    default:
      break;
    }
  }
  return 0;
}

4.1. Implementando o Filtro Laplaciano do Gaussiano - Modo 1

A primeira forma de implementarmos o filtro laplaciano do gaussiano é aplicarmos o filtro gaussiano seguido do laplaciano. Fazemos isso através do seguinte trecho de código:

if(lp){
  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);
  mask = Mat(3, 3, CV_32F, laplacian);
  filter2D(frameFiltered, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);
  if (addLG) frameFiltered = frameFiltered + frame32f;
}

Os parâmetros lp e addLG são variáveis booleanas, cujo valor é alterado quando pressionamos uma tecla. Caso a addLG seja verdadeiro, somamos a matriz filtrada com a matriz original. Utilizamos a matriz mask como máscara de convolução (ou kernel). As máscaras são definidas no começo do código. Como sempre é rodado um filtro com uma máscara pré-estabelecida, definimos uma máscara nula (apenas um elemento com valor 1), que não tem efeito na convolução (como um impulso). As máscaras são mostradas abaixo:

  float media[] = {1,1,1,
				           1,1,1,
				           1,1,1};
  float gauss[] = {1,2,1,
				           2,4,2,
				           1,2,1};
  float vertical[] = {-1,0,1,
					            -2,0,2,
					            -1,0,1};
  float horizontal[]={-1,-2,-1,
					             0, 0, 0,
					             1, 2, 1};
  float laplacian[]={ 0,-1, 0,
					           -1, 4,-1,
					            0,-1, 0};
  float lapgauss[]={0,0,1,0,0,
                     0,1,2,1,0,
                    1,2,-16,2,1,
                     0,1,2,1,0,
                     0,0,1,0,0};
  float noMask[] = {0,0,0,
                    0,1,0,
                    0,0,0};

As matrizes de entrada e saída são mostradas abaixo. Podemos verificar que o filtro laplaciano do gaussiano realça melhor as bordas que somente o filtro laplaciano sozinho (devido à eliminação do ruído).

in1
Figura 1. Imagem Capturada - Entrada do Programa
out1
Figura 2. Filtro Laplaciano
out2
Figura 3. Filtro Laplaciano do Gaussiano
out3
Figura 4. Imagens Somadas

Podemos notar um expressivo aumento no contraste e realce nas bordas.

4.2. Implementando o Filtro Laplaciano do Gaussiano - Modo 2

O segundo modo de implementação é utilizando o kernel 5x5 mostrado anteriormente. Variando os valores de sigma podemos obter outros kernels maiores e resultados mais precisos, mas para comparação nos limitamos apenas à matriz mostrada. Também podemos utilizar uma matriz 9x9, com sigma = 1.4, como a mostrada abaixo:

  float lapgauss1[]={0,1,1,2,2,2,1,1,0,
                     1,2,4,5,5,5,4,2,1,
                     1,4,5,3,0,3,5,4,1,
                     2,5,3,-12,-24,-12,3,5,2,
                     2,5,0,-24,-40,-24,0,5,2,
                     2,5,3,-12,-24,-12,3,5,2,
                     1,4,5,3,0,3,5,4,1,
                     1,2,4,5,5,5,4,2,1,
                     0,1,1,2,2,2,1,1,0};

Os resultados são mostrados abaixo:

in4
Figura 5. Entrada
out4
Figura 6. Saída

5. Referências