Skip to content
This repository was archived by the owner on Nov 6, 2024. It is now read-only.
Thales Castro edited this page May 10, 2016 · 22 revisions

Processamento Digital de Imagens

Thales Castro <thalescast@gmail.com>

Brevik Rafael < brevik_3@yahoo.com.br>

Tarefa 1

O filtro homomórfico controla as componentes de alta e baixa frequências de uma imagem, iluminação e refletância, de modo a separá-las através da aplicação do logaritmo ao modelo iluminação-refletância. Dessa maneira, aplica-se a transformada de fourier a cada componente, separadamente, e um filtro adequado às características que se deseja obter na imagem. Na sequência é aplicado ao resultado filtrado a transformada inversa e o cálculo da exponencial, de modo a formar a imagem de saída. O código que segue abaixo executa os passos para a aplicação do filtro homomórfico.

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#define RADIUS 20
using namespace cv;
using namespace std;
// troca os quadrantes da imagem da DFT
void deslocaDFT(Mat& image ){
  Mat tmp, A, B, C, D;
  // se a imagem tiver tamanho impar, recorta a regiao para
  // evitar cópias de tamanho desigual
  image = image(Rect(0, 0, image.cols & -2, image.rows & -2));
  int cx = image.cols/2;
  int cy = image.rows/2;
  // reorganiza os quadrantes da transformada
  // A B   ->  D C
  // C D       B A
  A = image(Rect(0, 0, cx, cy));
  B = image(Rect(cx, 0, cx, cy));
  C = image(Rect(0, cy, cx, cy));
  D = image(Rect(cx, cy, cx, cy));
  // A <-> D
  A.copyTo(tmp);  D.copyTo(A);  tmp.copyTo(D);
  // C <-> B
  C.copyTo(tmp);  B.copyTo(C);  tmp.copyTo(B);
}
int main(int , char**){
  VideoCapture cap;
  Mat imaginaryInput, complexImage, multsp;
  Mat padded, filter, mag;
  Mat image, imagegray, tmp;
  Mat_<float> realInput, zeros;
  vector<Mat> planos;
  // habilita/desabilita ruido
  int noise=0;
  // frequencia do ruido
  int freq=10;
  // ganho inicial do ruido
  float gain=1;
  // valor do ruido
  float mean;
  // guarda tecla capturada
  char key;
  // valores ideais dos tamanhos da imagem
  // para calculo da DFT
  int dft_M, dft_N;
  // abre a câmera default
  cap.open(0);
  if(!cap.isOpened())
    return -1;
  // captura uma imagem para recuperar as
  // informacoes de gravação
  cap >> image;
  // identifica os tamanhos otimos para
  // calculo do FFT
  dft_M = getOptimalDFTSize(image.rows);
  dft_N = getOptimalDFTSize(image.cols);
  // realiza o padding da imagem
  copyMakeBorder(image, padded, 0,
                 dft_M - image.rows, 0,
                 dft_N - image.cols,
                 BORDER_CONSTANT, Scalar::all(0));
  // parte imaginaria da matriz complexa (preenchida com zeros)
  zeros = Mat_<float>::zeros(padded.size());
  // prepara a matriz complexa para ser preenchida
  complexImage = Mat(padded.size(), CV_32FC2, Scalar(0));
  // a função de transferência (filtro frequencial) deve ter o
  // mesmo tamanho e tipo da matriz complexa
  filter = complexImage.clone();
  // cria uma matriz temporária para criar as componentes real
  // e imaginaria do filtro ideal
  tmp = Mat(dft_M, dft_N, CV_32F);
  float d;
  int yh, yhMax=20, yl, ylMax=20, d0=5, d0Max=50, c, cMax=20;
  char *nome = "window";
  // prepara o filtro passa-baixas ideal
  namedWindow(nome, CV_WINDOW_AUTOSIZE);
  createTrackbar("Gama H: ",nome, &yh, yhMax);
  createTrackbar("Gama L: ",nome, &yl, ylMax);
  createTrackbar("D0: ",nome, &d0, d0Max);
  createTrackbar("c: ",nome, &c, cMax);
  // cria a matriz com as componentes do filtro e junta
  // ambas em uma matriz multicanal complexa
  Mat comps[]= {tmp, tmp};
  for(;;){
    cap >> image;
    for(int i=0; i<dft_M; i++){
       for(int j=0; j<dft_N; j++){
	d = pow(pow(i - dft_M/2, 2) + pow(j - dft_N/2, 2), 0.5);
	tmp.at<float>(i,j)  = (yh - yl)*(1 - exp(-1*c*(pow(d,2)/pow(d0,2)))) + yl;// (yh - yl)*(1 - exp(-c*(pow(d, 2)/pow(d0,2)))) + yl;
       }
    }
    comps[0] = tmp;
    comps[1] = tmp;
    merge(comps, 2, filter);
    cvtColor(image, imagegray, CV_BGR2GRAY);
    imshow("original", imagegray);
    // realiza o padding da imagem
    copyMakeBorder(imagegray, padded, 0,
                   dft_M - image.rows, 0,
                   dft_N - image.cols,
                   BORDER_CONSTANT, Scalar::all(0));
    // limpa o array de matrizes que vao compor a
    // imagem complexa
    planos.clear();
    // cria a compoente real
    realInput = Mat_<float>(padded);
    // insere as duas componentes no array de matrizes
    planos.push_back(realInput);
    planos.push_back(zeros);
    // combina o array de matrizes em uma unica
    // componente complexa
    merge(planos, complexImage);
    for (int i = 0; i < dft_M; i++){
      for (int j = 0; j  < dft_N;j++){
        complexImage.at<char>(i,j) = log(complexImage.at<char>(i,j)+1);
      }
    }
    // calcula o dft
    dft(complexImage, complexImage);
    // realiza a troca de quadrantes
    deslocaDFT(complexImage);
    // aplica o filtro frequencial
    mulSpectrums(complexImage,filter,complexImage,0);
    // limpa o array de planos
    planos.clear();
    // separa as partes real e imaginaria para modifica-las
    split(complexImage, planos);
    // usa o valor medio do espectro para dosar o ruido
    mean = abs(planos[0].at<float> (dft_M/2,dft_N/2));
    // insere ruido coerente, se habilitado
    if(noise){
      // F(u,v) recebe ganho proporcional a F(0,0)
      planos[0].at<float>(dft_M/2 +freq, dft_N/2 +freq) +=
        gain*mean;
      planos[1].at<float>(dft_M/2 +freq, dft_N/2 +freq) +=
        gain*mean;
      // F*(-u,-v) = F(u,v)
      planos[0].at<float>(dft_M/2 -freq, dft_N/2 -freq) =
        planos[0].at<float>(dft_M/2 +freq, dft_N/2 +freq);
      planos[1].at<float>(dft_M/2 -freq, dft_N/2 -freq) =
        -planos[1].at<float>(dft_M/2 +freq, dft_N/2 +freq);
    }
    // recompoe os planos em uma unica matriz complexa
    merge(planos, complexImage);
    // troca novamente os quadrantes
    deslocaDFT(complexImage);
    // calcula a DFT inversa

    idft(complexImage, complexImage);

    for (int i = 0; i < dft_M; i++){
          for (int j = 0; j  < dft_N;j++){
            complexImage.at<char>(i,j) = exp((complexImage.at<char>(i,j)));
          }
    }
    // limpa o array de planos
    planos.clear();
    // separa as partes real e imaginaria da
    // imagem filtrada
    split(complexImage, planos);
    // normaliza a parte real para exibicao
    normalize(planos[0], planos[0], 0, 1, CV_MINMAX);
    imshow(nome, planos[0]);
    key = (char) waitKey(10);
    if( key == 27 ) break; // esc pressed!
    switch(key){
      // aumenta a frequencia do ruido
    case 'q':
      freq=freq+1;
      if(freq > dft_M/2-1)
        freq = dft_M/2-1;
      break;
      // diminui a frequencia do ruido
    case 'a':
      freq=freq-1;
      if(freq < 1)
        freq = 1;
      break;
      // amplifica o ruido
    case 'x':
      gain += 0.1;
      break;
      // atenua o ruido
    case 'z':
      gain -= 0.1;
      if(gain < 0)
        gain=0;
      break;
      // insere/remove ruido
    case 'e':
      noise=!noise;
      break;
    }
  }
  return 0;
}

Tarefa 2

Começamos essa tarefa por introduzir, resumidamente, o filtro de Canny ou o detector de bordas de Canny. Trata-se de um algoritmo que utiliza os conceitos de convolução digital e de cálculo do gradiente. A princípio, Canny buscava um algoritmo ótimo que, como provado, conseguiu, porém conseguiu para os três critérios que ele estabeleceu. O primeiro critério estabelecido por Canny foi o de que as bordas encontradas devem ser o mais próximas possíveis das verdadeiras, ou seja, ter baixa taxa de erro. O segundo foi que a distância entre um ponto marcado como uma borda e o centro da borda verdadeira deve ser a menor possível. E o último foi que o número de máximos locais retornados por uma borda deve ser mínimo. O filtro de Canny consiste em suavizar a imagem por um filtro Gaussiano, obter a magnitude do gradiente, bem como os ângulos da imagem, aplicar a supressão de não máximos, que tem por finalidade afinar as cristas da imagem e a limiarização por histeresse que se utiliza de dois limiares.

Esta tarefa une os conceitos utilizados pelo detector de bordas de Canny e a técnica de Pontilhismo, que é uma técnica de pintura em que pequenas manchas, círculos ou pontos são introduzidos na imagem.

Tendo como base os dois códigos disponibilizados no site do professor, canny.cpp e pontilhismo.cpp, o código foi implementado mesclando as funcionalidades de cada um deles. Foi criada uma função para a implementação do pontilhismo, seguindo a mesma ideia do código referência, onde a função circle, disponibilizada pelo OpenCV, traça círculos de raios especificados pelo usuário. Vale lembrar que antes de tudo, o filtro de Canny é aplicado utilizando a função que o próprio OpenCV fornece, Canny, que recebe a imagem de interesse, a matriz onde o resultado do filtro será escrito e os limiares. Por último, vale destacar que pelo filtro retornar uma imagem com bordas, em um fundo escuro, fez-se necessário que cada pixel fosse diminuído por 255, fazendo com que a imagem ficasse clara. O código bem como as imagens original e processadas. Vale lembrar que para maiores informações das funções utilizadas aqui, basta dar uma olhada no site do professor

int main(int argc, char const *argv[]) {
#include "opencv2/imgproc/imgproc_c.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <ctime>
#include <cstdlib>

using namespace std;
using namespace cv;

#define STEP 5
#define JITTER 3
#define RAIO 2

Mat image, border, points;
int width, height, gray;


void pontilhismo(void);
int main(int argc, char** argv){

	  image= imread(argv[1], IMREAD_COLOR/*IMREAD_GRAYSCALE*/);
	  namedWindow("canny",1);
    /*----Filtro de Canny----*/
	  Canny(image, border, 5/*top_slider*/, 3*5/*3*top_slider*/);

	   width=border.size().width;
	   height=border.size().height;

	  //Para que a imagem deixe de ser preta
	  for(int i=0; i < height;i++){
	      for(int j=0; j< width;j++){
		  border.at<uchar>(i,j) = 255 - border.at<uchar>(i,j);
	    }
	  }

	  pontilhismo();

	  return 0;
	}
/*-------Função que aplica o pontilhismo-------*/
void pontilhismo(void){
  vector<int> yrange;
  vector<int> xrange;
  int x, y;

  width=border.size().width;
  height=border.size().height;

  xrange.resize(height/STEP);
  yrange.resize(width/STEP);

  iota(xrange.begin(), xrange.end(), 0);
  iota(yrange.begin(), yrange.end(), 0);

  for(uint i=0; i<xrange.size(); i++){
    xrange[i]= xrange[i]*STEP+STEP/2;
  }

  for(uint i=0; i<yrange.size(); i++){
    yrange[i]= yrange[i]*STEP+STEP/2;
  }

  points = Mat(height, width, CV_8U, Scalar(255));

  random_shuffle(xrange.begin(), xrange.end());

  for(auto i : xrange){
    random_shuffle(yrange.begin(), yrange.end());
    for(auto j : yrange){
      x = i+rand()%(2*JITTER)-JITTER+1;
      y = j+rand()%(2*JITTER)-JITTER+1;
      gray = border.at<uchar>(x,y);

      /*----Aplicação dos círculos nos pixels----*/
      circle(points,
             cv::Point(y,x),
             RAIO,
             CV_RGB(gray,gray,gray),
             -1,
             CV_AA);
    }
   }
    imwrite("cannypoints.jpg", points);
}

Imagem original

Imagem original

Imagem processada

Imagem processada