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.
#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;
}
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.
#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;
}
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.
#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;
}
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.
#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;
}
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
#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;
}
é 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])\$
#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;
}
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.
#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;
}
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.
#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;
}
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.
#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;
}
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:
#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;
}
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
#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;
}
Bibliografia
-
http://docs.opencv.org/ - Principal portal de documentação OpenCV.
-
http://agostinhobritojr.github.io/tutoriais/pdi/. - Site de referência para as tarefas em OpenCV.