Vitor Ramos <ramos.vitor89@gmail.com>

Manipulando pixels em uma imagem

Regiões

Implementar de um programa que deverá solicitar ao usuário as coordenadas de dois pontos \$P_1\$ e \$P_2\$ localizados dentro dos limites do tamanho da imagem que lhe for fornecida e exibir. Entretanto, a região definida pelo retângulo de vértices opostos definidos pelos pontos \$P_1\$ e \$P_2\$ será exibida com o negativo da imagem na região correspondente.

O codigo
#include <iostream>
#include <cv.h>
#include <highgui.h>

using namespace cv;
using namespace std;

int main(int, char**)
{
    Mat image;
    Vec3b val;
    image= imread("biel.png",CV_LOAD_IMAGE_GRAYSCALE); // carrega a imagem biel.png
    if(!image.data)
        cout << "nao abriu biel.png" << endl;
    namedWindow("janela",WINDOW_AUTOSIZE);
    Point p1, p2;
    do
    {
        cout << "Digite p1 : ";
        cin >> p1.x >> p1.y;
        cout << "Digite p2 : ";
        cin >> p2.x >> p2.y;
    }while(p1.x > image.size().width || p2.x > image.size().width  ||
          p2.y > image.size().height || p2.y > image.size().height ||
          p1.x > p2.x || p1.y > p2.y || p1.x < 0 || p1.y < 0 || p2.x < 0 || p2.y < 0);
    // fica no while enquato os pontos nao sao validos
    for(int i=p1.x; i<p2.x; i++)
        for(int j=p1.y; j<p2.y; j++)
            image.at<uchar>(j, i)= 255-image.at<uchar>(j, i);//inverte o valor do pixel dentro da regiao
    imshow("janela", image);
    imwrite("biel2.png", image); // salva a imagem
    waitKey();
    return 0;
}
Imagem Original

biel

Resultado

resultado

Troca regiões

Implementar um programa que deverar trocar aleatoriamente regiões da imagem, formando uma espécie de quebra-cabeças. Explore o uso da classe Mat e seus construtores para criar as regiões que serão trocadas. O efeito é ilustrado na Figura Troca de regiões.

O codigo
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <time.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

int main(int, char**)
{
    srand(time(NULL));
    Mat image;
    Vec3b val;
    int n; // raiz do numero de regioes

    image= imread("abc.jpg",CV_LOAD_IMAGE_COLOR);
    if(!image.data)
        cout << "nao abriu abc.jpg" << endl;

    namedWindow("janela",WINDOW_AUTOSIZE);

    cout << "Digite o Numero de Regioes : ";
    cin >> n; // numero de divisoes em uma linha
    int wr= image.size().width/n, hr= image.size().height/n;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
        {
            // A ideia e pegar cada regiao e trocar com uma regiao aleatoria na imagem
            int rx= rand()%n, ry= rand()%n; // duas posicoes aleatorias de regioes
            Mat randpos= image(Rect(wr*rx,hr*ry,wr,hr)); // a regiao escolhida aleatoriamente
            Mat aux= image(Rect(wr*i,hr*j,wr, hr)).clone(); // a regiao atual
            randpos.copyTo(image(Rect(wr*i,hr*j,wr, hr))); // copia a regiao aleatoria na regiao atual
            aux.copyTo(image(Rect(wr*rx,hr*ry,wr,hr))); // copia a regial atual na regiao aleatoria
        }
    }
    imshow("janela", image);
    imwrite("abc2.jpg", image);
    waitKey();
    return 0;
}
Imagem Original

abc

Resultado 1

resultado

Resultado 2

resultado2

Preenchendo regiões

Contando objetos problema

Observando-se o programa labeling.cpp como exemplo, é possível verificar que caso existam mais de 255 objetos na cena, o processo de rotulação poderá ficar comprometido. Identifique a situação em que isso ocorre e proponha uma solução para este problema.

Solucao : Isso ocorre porque para cada objeto é atribuido um valor de cinza, se a imagem tive mais de 255 objetos o programa vai atribuir valores invalidos de cinza pois a imagem só possui 8 bits, a solução é fixar o valor de cinza dos objetos encontrados assim o programa podera classificar quantos objetos tiver na imagem.

Codigo Modificado
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv){
  Mat image, mask;
  int width, height;
  int nobjects;

  CvPoint p;
  image = imread("abc.png",CV_LOAD_IMAGE_GRAYSCALE);

  if(!image.data){
    std::cout << "imagem nao carregou corretamente\n";
    return(-1);
  }
  width=image.size().width;
  height=image.size().height;

  p.x=0;
  p.y=0;

  // busca objetos com buracos presentes
  nobjects=0;
  for(int i=0; i<height; i++){
    for(int j=0; j<width; j++){
      if(image.at<uchar>(i,j) == 255){
                // achou um objeto
                nobjects++;
                p.x=j;
                p.y=i;
                floodFill(image,p,50);
                // foi trocado floodFill(image,p,nobjects);
          }
        }
  }
  cout  << "Numero de objetos : "<< nobjects << endl;
  imshow("image", image);
  imwrite("labeling.png", image);
  waitKey();
  return 0;
}
Imagem Original

abc

Resultado

resultado

Contando objetos com buracos

Aprimoração do algoritmo de contagem apresentado para identificar regiões com ou sem buracos internos. Assumindo que objetos com mais de um buraco podem existir. Incluindo suporte no seu algoritmo para não contar bolhas que tocam as bordas da imagem.

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    Mat image, mask;
    int width, height;
    int nobjects, nobjectshole;

    CvPoint p;
    image = imread("abc.png",CV_LOAD_IMAGE_GRAYSCALE);

    if(!image.data)
    {
        std::cout << "imagem nao carregou corretamente\n";
        return(-1);
    }
    width=image.size().width;
    height=image.size().height;

    p.x=0;
    p.y=0;

    imshow("Original", image);
    waitKey();
    // busca objetos que tocam na borda e preenchem eles com cor de fundo 0
    for(int i=0; i<width; i++)
    {
        if(image.at<uchar>(0,i) == 255)
            floodFill(image, cvPoint(i,0), 0);
        if(image.at<uchar>(height-1, i) == 255)
            floodFill(image, cvPoint(i, height-1), 0);
    }
    for(int i=0; i<height; i++)
    {
        if(image.at<uchar>(i,0) == 255)
            floodFill(image, cvPoint(0,i), 0);
        if(image.at<uchar>(i,width-1) == 255)
            floodFill(image, cvPoint(width-1,i), 0);
    }
    // busca todos os objetos
    nobjects=0;
    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width; j++)
        {
            if(image.at<uchar>(i,j) == 255)
            {
                nobjects++;
                p.x=j;
                p.y=i;
                floodFill(image,p,100);
            }
        }
    }
    // preenche o fundo com outra cor para encontrar os buracos
    floodFill(image,cvPoint(0,0),10);
    // acha objetos com buracos
    nobjectshole=0;
    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width; j++)
        {
            if(image.at<uchar>(i,j) == 0)
            {
                p.x=j;
                p.y=i;
                floodFill(image,p, 10); // preenche o buraco
                // evita contar objeto com mais de um buraco
                if(image.at<uchar>(i, j-1) == 100)
                {
                    nobjectshole++;
                    // preenche objeto com buraco de outra cor
                    floodFill(image,cvPoint(p.x-1,p.y), 70);
                }
            }
        }
    }
    cout << "Numero de objetos : " << nobjects << endl
        <<  "Numero de objetos com buraco : "<< nobjectshole << endl
        << "Numero de objetos sem buraco : " << nobjects-nobjectshole << endl;
    imshow("final", image);
    imwrite("labeling.png", image);
    waitKey();
    return 0;
}
Resultado

resultado Para testar o codigo foram criados objetos com mais de um buraco e o programa contou ele apenas uma vez como esperado

Manipulação de histogramas

Equalizador histograma

Utilizando o programa exemplos/histogram.cpp como referência. Implemente um programa equalize.cpp. Este deverá, para cada imagem capturada, realizar a equalização do histogram antes de exibir a imagem.

o programa a seguir equaliza os 3 canais de cores RGB individualmente

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void MostraHist(Mat &image, bool equaliza= false)
{
    vector<Mat> planes;
    Mat histR, histG, histB;
    int nbins = 256;
    float range[] = {0, 256};
    const float *histrange = { range };
    bool uniform = true;
    bool acummulate = false;

    int histw = nbins, histh = nbins/2;
    Mat histImgR(histh, histw, CV_8UC3, Scalar(0,0,0));
    Mat histImgG(histh, histw, CV_8UC3, Scalar(0,0,0));
    Mat histImgB(histh, histw, CV_8UC3, Scalar(0,0,0));

    split (image, planes);
    if(equaliza) // equaliza os 3 planos R G B
    {
        equalizeHist(planes[0], planes[0]);
        equalizeHist(planes[1], planes[1]);
        equalizeHist(planes[2], planes[2]);
    }
    merge(planes, image); // junta os planos novamente na imagem
    calcHist(&planes[0], 1, 0, Mat(), histR, 1,
             &nbins, &histrange,
             uniform, acummulate);
    calcHist(&planes[1], 1, 0, Mat(), histG, 1,
             &nbins, &histrange,
             uniform, acummulate);

    calcHist(&planes[2], 1, 0, Mat(), histB, 1,
             &nbins, &histrange,
             uniform, acummulate);

    normalize(histR, histR, 0, histImgR.rows, NORM_MINMAX, -1, Mat());
    normalize(histG, histG, 0, histImgR.rows, NORM_MINMAX, -1, Mat());
    normalize(histB, histB, 0, histImgR.rows, NORM_MINMAX, -1, Mat());

    histImgR.setTo(Scalar(0));
    histImgG.setTo(Scalar(0));
    histImgB.setTo(Scalar(0));

    for(int i=0; i<nbins; i++)
    {
        line(histImgR, Point(i, histh),
             Point(i, histh-cvRound(histR.at<float>(i))),
             Scalar(0, 0, 255), 1, 8, 0);
        line(histImgG, Point(i, histh),
             Point(i, histh-cvRound(histG.at<float>(i))),
             Scalar(0, 255, 0), 1, 8, 0);
        line(histImgB, Point(i, histh),
             Point(i, histh-cvRound(histB.at<float>(i))),
             Scalar(255, 0, 0), 1, 8, 0);
    }
    histImgR.copyTo(image(Rect(0, 0,nbins, histh)));
    histImgG.copyTo(image(Rect(0, histh,nbins, histh)));
    histImgB.copyTo(image(Rect(0, 2*histh,nbins, histh)));
}


int main(int argc, char** argv)
{
    Mat image;
    int width, height;
    VideoCapture cap;
    cap.open(0);
    if(!cap.isOpened())
    {
        cout << "cameras indisponiveis";
        return -1;
    }

    width  = cap.get(CV_CAP_PROP_FRAME_WIDTH);
    height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);

    cout << "largura = " << width << endl;
    cout << "altura  = " << height << endl;

    while(1)
    {
        cap >> image;
        resize(image, image, Size(640,480));
        MostraHist(image);
        imshow("Original", image);
        MostraHist(image, 1);
        imshow("image", image);
        if(waitKey(30) >= 0) break;
    }
    return 0;
}
Resultado utilizando 64 beans

resultado64beans

Resultados utilizando 256 beans

resultado256beans resultado256 2beans

é possivel obervar a homogeneização do histograma e as cores mais vivas

Detecção de movimento

Utilizando o programa exemplos/histogram.cpp como referência, implemente um programa motiondetector.cpp. Este deverá continuamente calcular o histograma da imagem (apenas uma componente de cor é suficiente) e compará-lo com o último histograma calculado. Quando a diferença entre estes ultrapassar um limiar pré-estabelecido, ative um alarme. Utilize uma função de comparação que julgar conveniente.

O programa utiliza apenas o canal R para calcular a diferença do histograma, e a diferença e calculada pela função chi-quadrado \$\sum_{k=1}^{\n} (histR[k]-prevHistR[k])^2/(histR[k])\$

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
    Mat image;
    int width, height;
    VideoCapture cap;
    vector<Mat> planes;
    Mat histR;
    int nbins = 256;
    float range[] = {0, 256};
    const float *histrange = { range };
    bool uniform = true;
    bool acummulate = false;
    bool movimento= false;

    cap.open(0);

    if(!cap.isOpened())
    {
        cout << "cameras indisponiveis";
        return -1;
    }

    width  = cap.get(CV_CAP_PROP_FRAME_WIDTH);
    height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);

    cout << "largura = " << width << endl;
    cout << "altura  = " << height << endl;

    int histw = nbins, histh = nbins/2;
    Mat histImgR(histh, histw, CV_8UC3, Scalar(0,0,0));

    vector<Mat> prevHist; // histogramas anteriores
    prevHist.resize(1);
    double contFrame= 0;
    while(1)
    {
        cap >> image;
        resize(image, image, Size(640,480));
        split (image, planes);
        calcHist(&planes[0], 1, 0, Mat(), histR, 1,
                 &nbins, &histrange,
                 uniform, acummulate);
        normalize(histR, histR, 0, histImgR.rows, NORM_MINMAX, -1, Mat());
        histImgR.setTo(Scalar(0));
        for(int i=0; i<nbins; i++)
        {
            line(histImgR, Point(i, histh),
                 Point(i, histh-cvRound(histR.at<float>(i))),
                 Scalar(0, 0, 255), 1, 8, 0);
        }
        histImgR.copyTo(image(Rect(0, 0,nbins, histh)));
        if(prevHist[0].data) // verifica se ja existe histograma anterior
        {
            movimento= false; // movimento inicialmente falso
            // verifica se existe uma diferenca muito grande
            if(compareHist(histR, prevHist[0], CV_COMP_CHISQR)>100)
                movimento= true;
        }
        if(movimento) // se tiver movimento escreve na tela
        {
            putText(image, "Movimento Detectado.", cvPoint(1,150),
                FONT_HERSHEY_COMPLEX_SMALL, 0.8, cvScalar(0,0,250), 1, CV_AA);
        }
        if(contFrame>15) // atualiza histograma de comparacao a cada 15 frames
        {
            contFrame= 0;
            prevHist[0]= histR.clone(); // copia o histograma
        }
        contFrame++;
        imshow("image", image);
        if(waitKey(30) >= 0) break;
    }
    return 0;
}
Resultado

resultado

Filtragem no domínio espacial I

Laplace com gauss

Utilizando o programa exemplos/filtroespacial.cpp como referência, Implementar um programa laplgauss.cpp. O programa deverá acrescentar mais uma funcionalidade ao exemplo fornecido, permitindo que seja calculado o laplaciano do gaussiano das imagens capturadas.

O Codigo
#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"
         "j - laplaciano do gaussiano\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
                       };

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

    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);
    scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
    swap(mask, mask1);
    absolut=1; // calcs abs of the image
    bool lapgauss= false;
    menu();
    for(;;)
    {
        video >> cap;
        resize(cap, cap, Size(640, 480));
        cvtColor(cap, frame, CV_BGR2GRAY);
        flip(frame, frame, 1);
        imshow("original", frame);
        frame.convertTo(frame32f, CV_32F);
        if(lapgauss)
        {
            Mat aux;
            filter2D(frame32f, frameFiltered, frame32f.depth(), Mat(3, 3, CV_32F, gauss)/16, Point(1,1), 0);
            filter2D(frameFiltered, aux, frameFiltered.depth(), Mat(3, 3, CV_32F, laplacian), Point(1,1), 0);
            if(absolut)
                aux=abs(aux);
            aux.convertTo(result, CV_8U);
        }
        else
        {
            filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1,1), 0);
            if(absolut)
                frameFiltered=abs(frameFiltered);
            frameFiltered.convertTo(result, CV_8U);
        }
        imshow("filtroespacial", result);
        key = (char) waitKey(10);
        if( key == 27 ) break; // esc pressed!
        if(key != -1) lapgauss= false;
        switch(key)
        {
        case 'a':
            menu();
            absolut=!absolut;
            break;
        case 'm':
            menu();
            mask = Mat(3, 3, CV_32F, media).clone();
            mask/=9;
            printmask(mask);
            break;
        case 'g':
            menu();
            mask = Mat(3, 3, CV_32F, gauss).clone();
            mask/=16;
            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 'j':
            menu();
            lapgauss= true;
        break;
        default:
            break;
        }
    }
    return 0;
}
Resultado utilizando filtro gaussiano

gauss

Resultado utilizando filtro laplaciano

laplace

Resultado utilizando filtro laplaciano do gaussiano

lagauss

foi verificado que ao utilizar o filtro gaussiano antes do laplaciano faz muito diferença pois o filtro gaussiano elimina pequenos ruidos que existem na imagem e o laplaciano consegue distinguir melhor as bordas

Filtragem no domínio espacial II

TiltShift

Utilizando o programa exemplos/addweighted.cpp como referência, implemente um programa tiltshift.cpp. Três ajustes deverão ser providos na tela da interface:

  • um ajuste para regular a altura da região central que entrará em foco

  • um ajuste para regular a força de decaimento da região borrada

  • um ajuste para regular a posição vertical do centro da região que entrará em foco. Finalizado o programa, a imagem produzida deverá ser salva em arquivo.

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>

using namespace cv;
using namespace std;

// nome das barras deslizantes
char TrackbarH[50];
char TrackbarL[50];
// h - barra deslizante da altura
// l - barra deslizante da tamanho da janela de foco
// d - forca do decaimento da janela
int h_slider, h_max;
int l_slider, l_max;
int d_slider, d_max= 200;
double l1, l2;

double alphaPeso(int x, double l1, double l2, double d)
{
    return 0.5*(tanh((x-l1)/d)-tanh((x-l2)/d));
}

void on_trackbar_d(int, void* )
{

}

void on_trackbar_h(int, void* )
{
    // calcula l1 e l2 da funcao
    l1= h_slider-l_slider;
    l2= h_slider+l_slider;
}

void on_trackbar_l(int, void* )
{
    // calcula l1 e l2 da funcao
    l1= h_slider-l_slider;
    l2= h_slider+l_slider;
}

int main(int argvc, char** argv)
{
    Mat img= imread("pessoas.jpg");
    resize(img,img,Size(640,480));
    Mat borrada= img.clone();
    Mat result= Mat::zeros(img.size, CV_8UC3);

    // bora a imagem passando 10 vezes o filtro gaussiano com kernel 9x9
    for(int i=0; i<10; i++)
        GaussianBlur(borrada, borrada, Size(9,9),0,0);

    namedWindow("imr", 1);
    h_max= img.size().height; // altura maxima regiao central
    sprintf(TrackbarH, "Altura    %d", h_max);
    createTrackbar(TrackbarH, "imr", &h_slider, h_max, on_trackbar_h);
    on_trackbar_h(h_slider, 0);

    l_max= img.size().height; // largura regiao central maxima
    sprintf(TrackbarL, "Largura %d", l_max);
    createTrackbar(TrackbarL, "imr", &l_slider, l_max, on_trackbar_l);
    on_trackbar_l(l_slider, 0);

    createTrackbar("Forca", "imr", &d_slider, d_max, on_trackbar_d);
    on_trackbar_d(d_slider, 0);

    while(1)
    {
        // calcula a imagem resultante aplicando a funcao de transicao a cada linha da imagem
        for(int i=0; i<img.size().height; i++)
            addWeighted(img.row(i),alphaPeso(i,l1,l2,d_slider),borrada.row(i),1-alphaPeso(i,l1,l2,d_slider),0,result.row(i));
        imshow("imr",result);
        if(waitKey(30) == 27) break;
    }
    imwrite("resultado.jpg",result); // salva a imagem
    return 0;
}
Resultado

resultado

TiltShift com video

Utilizando o programa exemplos/addweighted.cpp como referência, implemente um programa tiltshiftvideo.cpp. Tal programa deverá ser capaz de processar um arquivo de vídeo, produzir o efeito de tilt-shift nos quadros presentes e escrever o resultado em outro arquivo de vídeo. A ideia é criar um efeito de miniaturização de cenas. Descarte quadros em uma taxa que julgar conveniente para evidenciar o efeito de stop motion, comum em vídeos desse tipo.

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>

using namespace cv;
using namespace std;

char TrackbarH[50];
char TrackbarL[50];

int h_slider, h_max;
int l_slider, l_max;
int d_slider, d_max= 200;
double l1, l2;

double alphaPeso(int x, double l1, double l2, double d)
{
    return 0.5*(tanh((x-l1)/d)-tanh((x-l2)/d));
}

void on_trackbar_d(int, void* )
{

}

void on_trackbar_h(int, void* )
{
    l1= h_slider-l_slider;
    l2= h_slider+l_slider;
}

void on_trackbar_l(int, void* )
{
    l1= h_slider-l_slider;
    l2= h_slider+l_slider;
}

int main(int argvc, char** argv)
{
    VideoWriter res;
    // arquivo a se salvo
    res.open("result3.avi", CV_FOURCC('M','J','P','G'), 15, Size(640,480));
    if(!res.isOpened())
        return -1;
    // abre arquivo do computador
    VideoCapture cap("teste3.avi");
    Mat img, hsv; // imagem em RGB e HSV
    cap >> img;
    resize(img,img,Size(640,480));
    Mat borrada= img.clone(); // imagem borrada
    Mat result= Mat::zeros(img.size(), CV_8UC3); // imagem resultante


    // barras deslizantes
    namedWindow("imr", 1);
    h_max= img.size().height;
    sprintf(TrackbarH, "Altura    %d", h_max);
    createTrackbar(TrackbarH, "imr", &h_slider, h_max, on_trackbar_h);
    on_trackbar_h(h_slider, 0);

    l_max= img.size().height;
    sprintf(TrackbarL, "Largura %d", l_max);
    createTrackbar(TrackbarL, "imr", &l_slider, l_max, on_trackbar_l);
    on_trackbar_l(l_slider, 0);

    createTrackbar("Forca", "imr", &d_slider, d_max, on_trackbar_d);
    on_trackbar_d(d_slider, 0);
    // configura posicao do tilt-shift
    while(1)
    {
        if(img.empty())
            break;
        borrada= img.clone();
        for(int i=0; i<10; i++)
            GaussianBlur(borrada, borrada, Size(9,9),0,0);

        for(int i=0; i<img.size().height; i++)
            addWeighted(img.row(i),alphaPeso(i,l1,l2,d_slider),borrada.row(i),1-alphaPeso(i,l1,l2,d_slider),0,result.row(i));
        imshow("imr",result);
        if(waitKey(30) == 27) break;
    }
    // comeca a gravar
    while(1)
    {
        for(int i=0; i<4; i++)
            cap >> img;
        cap >> img;
        resize(img,img, Size(640,480));
        // converte para hsv
        cvtColor(img, hsv, CV_BGR2HSV);
        vector<Mat> planes;
        split(hsv, planes);
        // aumenta a saturacao da imagem
        planes[1]*=2;
        // junta novamente a imgame
        merge(planes, hsv);
        cvtColor(hsv,img,CV_HSV2BGR);
        if(img.empty())
            break;
        borrada= img.clone();
        // Borra a imagem
        for(int i=0; i<10; i++)
            GaussianBlur(borrada, borrada, Size(9,9),0,0);

        // efeito tilt-shift
        for(int i=0; i<img.size().height; i++)
            addWeighted(img.row(i),alphaPeso(i,l1,l2,d_slider),borrada.row(i),1-alphaPeso(i,l1,l2,d_slider),0,result.row(i));
        res << result; // guarda o frame resultante
        imshow("imr",result);
        if(waitKey(30) != -1) break;
    }
    res.release();
    imwrite("resultado.jpg",result);
    return 0;
}
Resultado

result3 1

Filtragem no domínio da frequência

Filtro Homomorfico

Problema: Utilizando o programa exemplos_dft.cpp como referência, Implementar o filtro homomórfico para melhorar imagens com iluminação irregular. Crie uma cena mal iluminada e ajuste os parâmetros do filtro homomórfico para corrigir a iluminação da melhor forma possível. Assuma que a imagem fornecida é em tons de cinza.

Teoria: Aplicar logaritimo na imagem para atuar separadamente nas componentes reflectancia e iluminação, pois a iluminação apresenta um perfil de baixas frequencias e a reflectancia altas frequencias, assim ao aplicar log na imagem e passar o filtro passa alta modificado teremos um efeito maior na componente de iluminação, assim foi feito o esquema a seguir:

diagrama

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

Mat complexImage;
Mat padded, filter;
Mat image, imagegray, tmp;
Mat_<float> realInput, zeros;
vector<Mat> planos;

char key;
int dft_M, dft_N;
int gammaL, gammaLmax=100;
int gammaH, gammaHmax=100;
int D0, D0max=100;
int C0, C0max= 100;

void CalculaFiltro(int, void*)
{
    int dft_M= tmp.size().height;
    int dft_N= tmp.size().width;
    // calculo dos parametros
    // variacao de 0-10
    double gH= gammaH/10.0;
    double gL= gammaL/10.0;
    double d0= D0/10.0;
    // variacao 0-0.1
    double c0= C0/1000.0;
    // calcula componentes do filtro
    for(int i=0; i<dft_M; i++)
    {
        for(int j=0; j<dft_N; j++)
        {
            double D= pow(i-dft_M/2,2)+pow(j-dft_N/2, 2);
            tmp.at<float> (i,j) = (gH-gL)*(1-exp(-c0*D/pow(d0,2)))+gL;
        }
    }
    // junta os planos imaginario e real
    Mat comps[]= {tmp, tmp};
    merge(comps, 2, filter);
}

// 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**)
{
    // cria Janelas e sliders
    namedWindow("filtrada", CV_WINDOW_AUTOSIZE);
    createTrackbar("GammaH", "filtrada", &gammaH, gammaHmax, CalculaFiltro);
    createTrackbar("GammaL", "filtrada", &gammaL, gammaLmax, CalculaFiltro);
    createTrackbar("D0", "filtrada", &D0, D0max, CalculaFiltro);
    createTrackbar("C0", "filtrada", &C0, C0max, CalculaFiltro);

    // carrega imagem
    image= imread("pessoas3.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    resize(image, image, Size(640,480));
    imwrite("original.png", image);

    // tamanho para algoritimo da dft
    dft_M = getOptimalDFTSize(image.rows);
    dft_N = getOptimalDFTSize(image.cols);

    // padding
    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();
    tmp = Mat(dft_M, dft_N, CV_32F);

    while(1)
    {
        imagegray= image.clone();

        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);

        // soma com 1 para evitar problemas de log(0) o erro e minimo
        realInput += Scalar::all(1);
        // calcula o log da imagem
        log(realInput, realInput);

        // 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);

        // calcula o dft
        dft(complexImage, complexImage);

        // realiza a troca de quadrantes
        deslocaDFT(complexImage);

        // aplica o filtro frequencial
        mulSpectrums(complexImage,filter,complexImage,0);

        // troca novamente os quadrantes
        deslocaDFT(complexImage);

        // calcula a DFT inversa
        idft(complexImage, complexImage, DFT_SCALE);

        // limpa o array de planos
        planos.clear();

        // separa as partes real e imaginaria da
        // imagem filtrada
        split(complexImage, planos);
        // calcular expodencial
        exp(planos[0], planos[0]);

        // normaliza a parte real para exibicao
        normalize(planos[0], planos[0], 0, 1, CV_MINMAX);
        imshow("filtrada", planos[0]);

        key = (char) waitKey(10);
        if( key == 27 )
            break; // esc pressed
    }
    Mat res;
    // converte para uchar para salvar
    planos[0].convertTo(res, CV_8UC1, 255.0);
    // salva
    imwrite("filtrada.png", res);
    // mostra parte real do filtro
    vector<Mat> pFiltro;
    split(filter,pFiltro);
    normalize(pFiltro[0], pFiltro[0], 0, 1, CV_MINMAX);
    // converte para uchar para salvar
    pFiltro[0].convertTo(res, CV_8UC1, 255.0);
    imwrite("filtro.png", res);
    waitKey(0);

    return 0;
}
Filtro

filtro

Resultado 1 Comparação

juntas

Resultado 2

homo2

Resultado 3

homo3

Resultado 4

homo4

Canny

Canny e a arte com pontilhismo

Problema: Utilizando os programas exemplos/canny.cpp e exemplos/pontilhismo.cpp como referência, implemente um programa cannypoints.cpp. A idéia é usar as bordas produzidas pelo algoritmo de Canny para melhorar a qualidade da imagem pontilhista gerada. A forma como a informação de borda será usada é livre. Entretanto, são apresentadas algumas sugestões de técnicas que poderiam ser utilizadas:

  • Desenhar pontos grandes na imagem pontilhista básica.

  • Usar a posição dos pixels de borda encontrados pelo algoritmo de Canny para desenhar pontos nos respectivos locais na imagem gerada.

  • Experimente ir aumentando os limiares do algoritmo de Canny e, para cada novo par de limiares, desenhar círculos cada vez menores nas posições encontradas. A Figura Pontilhismo aplicado à imagem Lena foi desenvolvida usando essa técnica.

Metodo: Ao aplicar o filtro de canny em cada ponto da borda desenhar um circulo de raio menor

O Codigo
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <algorithm>
#include <time.h>

using namespace std;
using namespace cv;

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

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

    vector<int> yrange;
    vector<int> xrange;

    Mat image, points;

    int width, height;
    int x, y;

    Vec3b gray;

    image= imread("abc.jpg",CV_LOAD_IMAGE_COLOR);

    srand(time(NULL));

    if(!image.data)
    {
        cout << "nao abriu" << argv[1] << endl;
        cout << argv[0] << " imagem.jpg";
        exit(0);
    }

    width=image.size().width;
    height=image.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_8UC3, Scalar(255,255,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 = image.at<Vec3b>(x,y);
            circle(points, Point(y,x), RAIO, CV_RGB(gray[2],gray[1],gray[0]), -1, CV_AA);
        }
    }
    imwrite("Antes.jpg", points);
    imshow("Antes", points);
    Mat grayImg, borrada, bordas;
    cvtColor(image, grayImg, CV_BGR2GRAY);
    GaussianBlur(grayImg, borrada, Size(3,3), 25, 25);
    Canny(borrada, bordas, 150, 50);
    for(int i=JITTER; i<bordas.size().width-JITTER; i++)
    {
        for(int j=JITTER; j<bordas.size().height-JITTER; j++)
        {
            if(bordas.at<uchar>(j,i) == 255)
            {
                x = i+rand()%(2*JITTER)-JITTER+1;
                y = j+rand()%(2*JITTER)-JITTER+1;
                gray = image.at<Vec3b>(y,x);
                circle(points, Point(x,y), 1, CV_RGB(gray[2],gray[1],gray[0]), -1, CV_AA);
            }
        }
    }
    imshow("Depois", points);
    imwrite("Depois.jpg", points);
    imshow("Canny", bordas);
    imwrite("Canny.jpg", bordas);
    waitKey(0);
    return 0;
}
Imagem original

abc

Imagem pontilhismo sem canny

Antes

Imagem canny

Canny

Imagem pontilhismo com canny

Depois

Bibliografia