Programas/atividades desenvolvidas para a disciplina DIM0141 - VISÃO COMPUTACIONAL, da Universidade Federal do Rio grande do Norte UFRN

Prefácio

Todos os programas, neste documento, foram desenvolvidas em C++ utilizando-se da biblioteca OpenCV e em ambiente Linux. Para compilar qualquer programa presente neste documento, pode-se fazer uso deste Makefile, coloca o Makefile na mesma pasta do código fonte, extensão .cpp, e execute via terminal o comando make <nome_do_programa>. Todos os códigos encontram-se no Repositório do github.

1. Projetos e Tarefas da Primeira Unidade

1.1. Tarefa 1

  1. Descreva a distribuição de células fotorreceptoras na retina e seu impacto na percepção visual

    Possuimos dois tipos de células fotorreceptoras, são elas os cones e os bastonetes. Resumidamente os cones são responsaveis pela percepção das cores e os bastonetes pela intensidade luminosa, os cones predominantemente se localizam em um ponto na retina, chamado de fovea, já os bastonetes ficam distribuidos na periferia dessa região, essa distribuição faz com que os seres humanos possuam um ponto focal no centro do seu campo de visão, essa região central é a que possui maior nitidez, e devido a distribuição dos bastonetes, e sua sensibilidade a luminosidade, eles são responsaveis pela visão noturna e nossa visão periférica.

  2. Qual a diferença entre os cones S, M e L? Quais deles são mais ativados quando uma luz amarela é projetada na retina?

    A imagem a seguir resume bem o papel de cada tipo de cone na nossa perceção de cor, o tipo S é responsavel pela perceção da faixa mais proxima ao azul, M reage principalmente as frequencias proximas do verde e o L ao vermelho. A luz amarela possui comprimento de onda entre 565 e 590 nm, portando podemos observar pelo gráfico que é uma combinação, entre a reação do cone M com o cone L, provoca a reação que nos faz perceber a cor amarela.

Cones SMJ2 E
Figura 1. Simplified human cone response curves

  • Escreva um programa para

    • abrir uma imagem e exibir na tela os 3 canais separadamente

Compilando e Executando.

$ make q3a
$ ./q3a <caminho_para_a_imagem>

Exemplo de funcionamento

entrada q3a
Figura 2. Entrada do programa
canal R
Figura 3. Apenas o Canal R
canal G
Figura 4. Apenas o Canal G
canal B
Figura 5. Apenas o Canal B

Download do código completo: q3a.cpp

Clique aqui pra ver o código completo

//Programa que separa os três canais (RGB) e exibi uma imagem de cada canal
#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char**argv)
{
  Mat img;
  Mat ch[3];

  img = imread(argv[1],CV_LOAD_IMAGE_COLOR);

  if(!img.data){
    std::cout << "imagem nao carregou corretamente\n";
    return(-1);
  }
  imshow("Original", img);

  ch[0] = Mat(img.size(), CV_8UC3);
  ch[1] = Mat(img.size(), CV_8UC3);
  ch[2] = Mat(img.size(), CV_8UC3);

  for(int y = 0; y < img.rows; y++)
  for(int x = 0; x < img.cols; x++)
  {
    ch[0].at<Vec3b>(y,x)[0] = img.at<Vec3b>(y,x)[0];//B
    ch[1].at<Vec3b>(y,x)[1] = img.at<Vec3b>(y,x)[1];//G
    ch[2].at<Vec3b>(y,x)[2] = img.at<Vec3b>(y,x)[2];//R
  }

  imshow("Canal B", ch[0]);
  imshow("Canal G", ch[1]);
  imshow("Canal R", ch[2]);

  imwrite("canal_B.png", ch[0]);
  imwrite("canal_G.png", ch[1]);
  imwrite("canal_R.png", ch[2]);

  imwrite("entrada_q3a.png", img);

  waitKey(0);
  return 0;
}

  • abrir uma imagem e exibir na tela a imagem invertida horizontalmente

Compilando e Executando.

$ make q3b
$ ./q3b <caminho_para_a_imagem>

Exemplo de funcionamento

entrada q3b
Figura 6. Entrada do programa
flipHorizontal
Figura 7. Imagem invertida horizontalmente

Download do código completo: q3b.cpp

Clique aqui pra ver o código completo

//Este programa recebe uma imagem como entrada e inverte a mesma, espelha na vertical,
//exibi e salva o resultado
#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char**argv)
{
  Mat img;
  Mat imgFlip;

  img = imread(argv[1],CV_LOAD_IMAGE_COLOR);

  if(!img.data){
    std::cout << "imagem nao carregou corretamente\n";
    return(-1);
  }
  imshow("Original", img);

  imgFlip = img.clone();

  for(int y = 0; y < img.rows; y++)
  for(int x = 0; x < img.cols; x++)
  {
    imgFlip.at<Vec3b>(y,img.cols - 1 - x) = img.at<Vec3b>(y,x);
  }

  imshow("Imagem Invertida Horizontalmente", imgFlip);
  imwrite("entrada_q3b.png", img);
  imwrite("flipHorizontal.png", imgFlip);

  waitKey(0);
  return 0;
}

  • abrir duas imagens (a e b) de mesmo tamanho e exibir na tela uma nova imagem (c) com o blending entre ambas, usando uma combinação linear entre elas

Compilando e Executando.

$ make q3c
$ ./q3c <caminho_para_a_imagem A> <caminho_para_a_imagem B>

Exemplo de funcionamento

Demonstração Blending

Download do código completo: q3c.cpp

Clique aqui pra ver o código completo

#include <opencv2/opencv.hpp>
using namespace cv;

const char* mainWindow = "Blending";

Mat imgA, imgB;
Mat imgC;

void blending(int, void*);

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

  if(argc != 3)
  {
    std::cout << "Voce deve informar o caminho de duas imagens de mesmo tamanho\n";
    return(-1);
  }

  imgA = imread(argv[1],CV_LOAD_IMAGE_COLOR);
  imgB = imread(argv[2],CV_LOAD_IMAGE_COLOR);

  //Teste se as imagens foram carregadas corretamente
  if(!imgA.data){
    std::cout << "imagem "<< argv[1] <<"nao carregou corretamente\n";
    return(-1);
  }
  if(!imgB.data){
    std::cout << "imagem "<< argv[2] <<"nao carregou corretamente\n";
    return(-1);
  }
  //Testa se as imagens possuem o mesmo tamanho
  if(imgA.size() != imgB.size())
  {
    std::cout << "As imagens devem possuir o mesmo tamanho\n";
    return(-1);
  }

  imgC = imgB.clone();

  //Cria o slider, para ajustar a variavel alpha do calculo do blending
  namedWindow(mainWindow, WINDOW_AUTOSIZE);
  createTrackbar( "Alpha [0-100%]",  mainWindow,
                                     NULL,
                                     100,
                                     blending);
  int key;
  while(true)
  {
    imshow(mainWindow, imgC);
    key = waitKey(10);
    if(key == 27)//esq
      break;
  }
  return 0;
}

void blending(int pos, void*)
{
  float alpha = pos/100.0;
  for(int y = 0; y < imgA.rows; y++)
  for(int x = 0; x < imgA.cols; x++)
  {
      imgC.at<Vec3b>(y,x) = imgA.at<Vec3b>(y,x)*alpha + imgB.at<Vec3b>(y,x)*(1.0 - alpha);
  }
}

  • salvar uma nova imagem com o seguinte gradiente vertical

Compilando e Executando.

$ make q3d
$ ./q3d  <caminho_para_a_imagem>

Exemplo de funcionamento

entrada q3d
Figura 8. Entrada do programa
gradiente
Figura 9. Gradiente utilizado na imagem
imagem com gradiente
Figura 10. Imagem de entrada x Gradiente

Download do código completo: q3d.cpp

Clique aqui pra ver o código completo

#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char**argv)
{
  Mat img;
  Mat result;
  Mat gradient;

  img = imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);

  if(!img.data){
    std::cout << "imagem nao carregou corretamente\n";
    return(-1);
  }
  imshow("Original", img);

  result   = Mat(img.size(), CV_32FC1);
  gradient = img.clone();

  for(int y = 0; y < img.rows; y++)
  for(int x = 0; x < img.cols; x++)
  {
    result.at<float>(y,x) = (img.at<uint8_t>(y,x)/255.0)*y*(1.0/img.rows);
    gradient.at<uint8_t>(y,x) = y*(255.0/img.rows);
  }
  //converte para um formato que eh possivel salvar, neste caso equivalente ao grayscale
  //https://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#void%20Mat::convertTo(OutputArray%20m,%20int%20rtype,%20double%20alpha,%20double%20beta)%20const
  result.convertTo(result, CV_8UC1, 255);

  imshow("Resultado", result);
  imshow("Gradiente", gradient);

  imwrite("gradiente.png", gradient);
  imwrite("imagem_com_gradiente.png", result);
  imwrite("entrada_q3d.png", img);

  waitKey(0);
  return 0;
}


  1. Considere o formato de imagem NetPBM

    Qual a diferença entre os números mágicos P1, P2, P3, P4, P5 e P6?

    P1: BitMap, extenção .pbm, valores: 0-1 (Preto e Branco), ASCII

    P2: GrayMap, extenção .pgm, valores:0-255, ASCII
    P3: PixMap, extenção .ppm, valores: 3 canais(RGB) 0-255 cada canal, ASCII
    P4: BitMap, extenção .pbm, valores: 0-1 (Preto e Branco),  Binário
    P5: GrayMap, extenção .pgm, valores:0-255, Binário
    P6: PixMap, extenção .ppm, valores: 3 canais(RGB) 0-255 cada canal, Binário
    Converta uma imagem jpg para PBM (ASCII) utilizando convert e display para exibir
$ convert -compress none cafe.jpg cafe_ascii.pbm
$ display  cafe_ascii.pbm
cafe
Figura 11. Imagem Original
cafe ascii
Figura 12. Imagem convertida para pbm

Imagem convertida: cafe.pbm

Converta a mesma imagem para PBM (binário) e para PPM (binário). Compare o tamanho dos 4 arquivos de imagem

Arquivo

Tamanho

cafe.jpg

98.9Kb

cafe_ascii.pbm

600.5Kb

cafe_binary.pbm

37.5Kb

cafe_ascii.ppm

3.2Mb

cafe_binary.ppm

900Kb

Por que o formato binário ocupa menos espaço que o formato ASCII?

Cada caracter ASCII ocupa 8 bits, já no binário cada bit representa um valor de pixel, no PBM já no PPM, cada conjunto de 3 caracteres asciis represantam um pixel(RGB).

Por que o formato PPM binário ocupa mais espaço que o formato PBM binário?

O formato PPM é um PixMap já o PBM é um BitMap, ou seja, cada unidade do PPM é é formado por um conjunto de 3 bytes, para representar as componentes RGB do pixel, já no padrão PBM, cada unidade representa um unico valor, 0 ou 1, ou seja 1 bit por unidade (pixel de 1 bit).

Quais desses formatos são vetoriais e quais são bitmaps? BMP, SVG, JPG, EPS, PNG
Formatos bitmaps

BPM, JPEG, PNG.

Formatos vetorais

SVG, EPS.

Imagens de algumas aplicações possuem um nível de ruído considerável, principalmente aquelas que captam em níveis baixos de iluminação, como na captura de imagens astronômicas. Uma das formas de atenuar esse tipo de ruído é através da média de inúmeras imagens. Utilizando as 9 imagens disponibilizadas, crie um programa que gere uma nova imagem com o ruído atenuado.

Compilando e Executando.

$ make q6
$ ./q6

Exemplo de funcionamento

Media
Figura 13. Imagem Media, Imagem filtrada

Download do código completo: q6.cpp

Entrada utilizada: imagensComRuido

Clique aqui pra ver o código completo

//Programa que tira a media aritmetica de N imagens
//Este programa carrega todas as imagens de um arquivo, cujo caminho (path)
//esta indicado pela variavel path, e calcula a media de todas essas imagens
#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char**argv)
{
  String path("./imagensComRuido/*.jpg");
  std::vector<String> fn;
  std::vector<Mat> imagens;
  Mat media;

  glob(path, fn, true);
  //carrega as N imagens
  for(size_t i = 0; i < fn.size(); i++)
  {
    Mat im = imread(fn[i], CV_LOAD_IMAGE_GRAYSCALE);
    if(!im.data)continue;//caso falhe na leitura, vai para o proximo arquivo
    imagens.push_back(im);
  }

  if(imagens.empty())
  {
    std::cerr << "Erro ao carrega as imagens de " << path << "\n";
    return -1;
  }

  media = Mat::zeros(imagens[0].size(), CV_32FC1);
  //calcula da media das N imagens
  for(size_t i = 0; i < imagens.size(); i++)
  {
    for(int y = 0; y < imagens[0].rows; y++)
    for(int x = 0; x < imagens[0].cols; x++)
    {
      media.at<float>(y,x) += imagens[i].at<uint8_t>(y,x)/(float)imagens.size();
    }
  }
  media.convertTo(media, CV_8UC1, 1.0, 0);

  imshow("Media", media);
  imwrite("Media.png",media);

  waitKey(0);
  return 0;
}

1.2. Tarefa 2

1.2.1. Aumentar brilho, pintar faixas

Compilando e executando.

$ make efeito
$ ./efeito <caminho_para_a_imagem>
entradaEfeito
Figura 14. Entrada do programa
resultadoEfeito
Figura 15. Resultado

Download do código completo: efeito.cpp

Clique aqui pra ver o código completo

#include <cmath>
#include <opencv2/opencv.hpp>
using namespace cv;

#define LINE_WIDTH 5//px

void gammaCorrection(const Mat &img, Mat &out,double gamma)
{
  static Mat lookupTable(1, 256, CV_8U);
  static bool first = true;

  out = img.clone();

  for(int i = 0; i < 256 && first; i++)
    lookupTable.ptr()[i] = saturate_cast<uint8_t>(pow(i/255.0, gamma)*255);
  first = false;

  cv::LUT(img, lookupTable, out);
}

int main(int argc, char** argv){
  Mat img;
  Mat result;

  img = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
  if(!img.data)
  {
    std::cerr << "Erro ao carregar imagem!\n";
    return -1;
  }

  gammaCorrection(img, result, 0.05);

  imshow("Saida", img);
  waitKey(0);

  imshow("Saida", result);
  waitKey(0);

  //pinta faixas pretas com largura de LINE_WIDTH pixels
  //espaçadas de LINE_WIDTH
  for(int y = 0; y < img.rows; y++)
  for(int x = 0; x < img.cols; x++)
  {
    result.at<uint8_t>(y,x) = 0;
    if((x+1)%LINE_WIDTH == 0)x += LINE_WIDTH;
  }

  imshow("Saida", result);
  waitKey(0);

  imwrite("entradaEfeito.png", img);
  imwrite("resultadoEfeito.png", result);
  return 0;
}

1.2.2. Suavização da imagem anterior

Este programa recebe como parametro o caminho para uma imagem e aplica um filtro de suavização convolucional (filtro de borramento). O programa aplica o filtro toda vez que o usuário pressiona qualquer tecla(com excesão do ESQ), aplicando assim uma suavização em cascata, o programa se encerra ao ser pressionado a tecla ESQ.

Neste exemplo o programa foi utilizado para suavizar a imagem resultando do programa anterior.

Compilando e executando.

$ make suavizacao
$ ./suavizacao <caminho_para_a_imagem>
entradaSuavizacao
Figura 16. Entrada do programa
resultadoSuavizacao
Figura 17. Resultado
animation
Figura 18. Resultados intermediarios

Download do código completo: suavizacao.cpp

Clique aqui pra ver o código completo

#include <string>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
  Mat img;
  Mat res;
  Mat mask;
  float kernel_media[] = {1, 1, 1,
                          1, 1, 1,
                          1, 1, 1};

  img = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
  if(!img.data)
  {
    std::cerr << "Erro ao carregar imagem\n";
    return -1;
  }

  mask = Mat(3,3, CV_32F, kernel_media)/9.0;
  //Aplica filtro de suavização, filtro de Media
  filter2D(img, res, -1, mask);

  imshow("Entrada", img);
  int key;
  string fileName;
  int count = 0;
  while(true)
  {
    //Salva cada imagem resultante para gerar o gif, com o comando convert -delay 150 tmp/* animation.gif
    fileName = string("tmp/a") + to_string(count++) + ".png";
    imwrite(fileName, res);

    filter2D(res, res, -1, mask);
    imshow("Saida", res);
    key = waitKey(0);
    if(key == 27)
      break;
  }
  imwrite("entradaSuavizacao.png",img);
  imwrite("resultadoSuavizacao.png",res);
  return 0;
}

1.2.3. Filtro de suavização seletiva

Compilando e executando.

$ make suavizacao2
$ ./suavizacao2 <caminho_para_a_imagem>
entradaSuavizacao2
Figura 19. Entrada do programa
contorno
Figura 20. Imagem com contorno realçado
borrada
Figura 21. Imagem Borrada
saidaSuavizacao2
Figura 22. Resultado

Download do código completo: suavizacao2.cpp

Clique aqui pra ver o código completo

#include <string>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

#define MEDIA_SIZE 10.0

void convFilter(const Mat &src,Mat &dest ,const Mat &mask);

int main(int argc, char** argv)
{
  Mat img, result, tmp;
  Mat blurImg, contourImg;
  Mat mask;
  float kernel_media[(int)(MEDIA_SIZE*MEDIA_SIZE)];
  float kernel_gaussian[] = {1, 2, 1,
                             2, 4, 2,
                             1, 2, 1};
  float kernel_laplaciano[] = {1, 1, 1,
                               1, -8, 1,
                               1, 1, 1};

  for(uint i = 0; i < (MEDIA_SIZE*MEDIA_SIZE); i++)
    kernel_media[i] = 1.0/(MEDIA_SIZE*MEDIA_SIZE);

  img = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
  if(!img.data)
  {
    std::cerr << "Erro ao carregar imagem\n";
    return -1;
  }

  //suavizacao gaussiana
  mask = Mat(3,3, CV_32F,kernel_gaussian)/16.0;
  convFilter(img, contourImg, mask);

  //realçar contornos
  mask = Mat(3,3, CV_32F,kernel_laplaciano);
  convFilter(contourImg, contourImg, mask);
  contourImg.convertTo(tmp, CV_8U, 255.0/2040.0, 127.0);

  /*Exibir e salvar imagem*/
  imshow("Contornos", tmp);
  imwrite("contorno.png", tmp);

  //Borramento
  mask = Mat(MEDIA_SIZE, MEDIA_SIZE, CV_32F,kernel_media);
  convFilter(img, blurImg, mask);
  blurImg.convertTo(tmp, CV_8U);

  /*Exibir e salvar imagem*/
  imshow("Borrada", tmp);
  imwrite("borrada.png", tmp);

  result = blurImg - contourImg;
  result.convertTo(result, CV_8U);

  //trecho apenas para exibir e salvar as imagens
  imshow("Entrada", img);
  imshow("Saida", result);
  imwrite("entradaSuavizacao2.png", img);
  imwrite("saidaSuavizacao2.png", result);

  waitKey(0);
  return 0;
}

void convFilter(const Mat &src,Mat &dest ,const Mat &mask)
{
  float sum;
  Mat src_f;

  src.convertTo(src_f, CV_32F);
  dest = Mat::zeros(src.size(), CV_32F);

  for(int y = mask.rows/2; y < (src.rows - mask.rows/2); y++)
  for(int x = mask.cols/2; x < (src.cols - mask.cols/2); x++)
  {
      sum = 0;
      for(int v = 0; v < mask.rows; v++)
      for(int u = 0; u < mask.cols; u++)
      {
        sum+= src_f.at<float>(y - mask.rows/2 + v,x - mask.cols/2 + u)*mask.at<float>(v,u);
      }
      dest.at<float>(y,x) = sum;
  }

}

1.3. Tarefa 4

1.3.1. Análise de alguns tipos de ruídos em uma imagem e calculo do PSNR

PSNR: peak signal to noise ratio, é uma forma de medir a qualidade de uma imagem, quanto maior esse valor, melhor a imagem.

Calculo do PSNR
\[PSNR = 10\log_{10}\frac{I_{Max}}{MSE}\\\]
MSE(mean square error)
\[MSE = \frac{1}{MN}\sum_{y = 0}^{M-1}\sum_{x = 0}^{N-1}\Big(f(x,y) - \bar{f}(x,y)\Big)\]

Compilando e executando.

$ make analise_ruido1
$ ./analise_ruido1
imagem0
Figura 23. Imagem 0
histograma0
Figura 24. histograma 0
imagem1
Figura 25. Imagem 1
histograma1
Figura 26. histograma 1
imagem2
Figura 27. Imagem 2
histograma2
Figura 28. histograma 2
imagem3
Figura 29. Imagem 3
histograma3
Figura 30. histograma 3
imagem4
Figura 31. Imagem 4
histograma4
Figura 32. histograma 4
imagem5
Figura 33. Imagem 5
histograma5
Figura 34. histograma 5
imagem6
Figura 35. Imagem 6
histograma6
Figura 36. histograma 6
imagem7
Figura 37. Imagem 7
histograma7
Figura 38. histograma 7

Download do código completo: analise_ruido1.cpp

Clique aqui pra ver o código completo

#include <opencv2/opencv.hpp>
#include <string>
#include <cmath>

using namespace cv;
using namespace std;

#define I_MAX 255

float calcPSNR(const Mat &ref, const Mat &img);
void calcHistograma(const Mat &src, uint32_t histograma[]);
//Esta função cria uma imagem, contendo o plot do histograma
//com a dimensão de 256x200 px
void drawHistograma(const uint32_t histograma[], Mat &imgH);

int main(int argc, char** argv){
  Mat orig, imgH;
  String path("./primeiroConjunto/*.jpg");
  std::vector<String> fn;
  std::vector<Mat> imagens;
  uint32_t histograma[I_MAX+1];

  glob(path, fn, true);
  orig = imread(fn[0], CV_LOAD_IMAGE_GRAYSCALE);
  if(!orig.data)
  {
    std::cerr << "Erro ao carregar a imagem original" << '\n';
    return-1;
  }

  // carrega as N imagens
  for(size_t i = 0; i < fn.size(); i++)
  {
    Mat im = imread(fn[i], CV_LOAD_IMAGE_GRAYSCALE);
    if(!im.data)continue;//caso falhe na leitura, vai para o proximo arquivo
    imagens.push_back(im);

    calcHistograma(im, histograma);
    drawHistograma(histograma, imgH);
    imshow("imagem", im);
    imshow("Histograma", imgH);
    imwrite(string("imagem") + to_string(i) + string(".png"), im);
    imwrite(string("histograma") + to_string(i) + string(".png"), imgH);

    std::cout << "PSNR:"<< calcPSNR(imagens[0], im) << '\n';

    waitKey(0);
  }

  if(imagens.empty())
  {
    std::cerr << "Erro ao carrega as imagens de " << path << "\n";
    return -1;
  }

  return 0;
}

void calcHistograma(const Mat &src, uint32_t histograma[])
{
  memset(histograma, 0, (I_MAX+1)*sizeof(uint32_t) ); //zera todas as posições do vetor de freq.

  for(int y = 0; y < src.rows; y++)
    for(int x = 0; x < src.cols; x++)
      histograma[src.at<uint8_t>(y,x)]++;
}

uint32_t maxValue(const uint32_t v[], size_t size)
{
  uint32_t max = 0;
  for(size_t i = 0; i < size; i++)
    max = (v[i] > max)?v[i]:max;
  return max;
}

void drawHistograma(const uint32_t histograma[], Mat &imgH)
{
  imgH = Mat::zeros(200, 256, CV_8U);
  uint32_t freqMax = maxValue(histograma, I_MAX+1);

  for(uint32_t i = 0; i < I_MAX+1; i++)
  {
    line(imgH, Point(i, 200), Point(i, 200-histograma[i]*(200.0/freqMax)), 255);
  }
}

float calcPSNR(const Mat &ref, const Mat &img)
{
  float mse = 0;
  for(int y = 0; y < ref.rows; y++)
  for(int x = 0; x < ref.cols; x++)
  {
    mse += pow(img.at<uint8_t>(y,x) - ref.at<uint8_t>(y,x), 2.0);
  }
  mse = mse/(ref.cols*ref.rows);
  return 10.0*log10((float)(I_MAX*I_MAX)/mse);
}

Tabela 1. Análises
Nome PSNR Análise

Image 0

inf

Imagem original(sem ruído). Como a imagem 0 foi utilizada como imagem de referencia, por essa razão o PSNR deu inf.

Image 1

34.5399

Olhando apenas para a imagem, não conseguimos notar muita diferença, mas se olharmos para o histograma podemos observar um padrão no ruído do tipo gaussiano.

Image 2

15.5066

Conseguimos observar, facilmente pela imagem, que o ruído é do tipo sal e pimenta, mas observando também o histograma dessa imagem, podemos notar que o comportamento do ruído se aproxima de uma distribuição normal.

Image 3

31.4324

A maior diferença nesta imagem é o histograma, nele podemos notar que o ruído possui uma distribução aproximadamente exponencial.

Image 4

18.2371

Na imagem 4 nota-se facilmente o padrão típico de um ruído tipo sal.

Image 5

9.82257

Podemos notar na imagem 5 o padrão de ruído do tipo sal e pimenta.

Image 6

18.5184

Ruído do tipo pimenta.

Image 7

37.3253

Aqui só conseguimos notar a diferença no histograma, por meio dele podemos observar um ruído com comoprtamento gaussiano novamente.

1.3.2. Filtros para suavização de ruídos

2. Bibliografia