Esta página é destinada ao relatório dos exercícios realizados para a Disciplina de Processamento Digital de Imagens - DCA/UFRN.
Utilizando o programa pixels.cpp como referência, implemente um programa regions.cpp. Esse programa deverá solicitar ao usuário as coordenadas de dois pontos P1 e P2 localizados dentro dos limites do tamanho da imagem e exibir que lhe for fornecida. Entretanto, a região definida pelo retângulo de vértices opostos definidos pelos pontos P1 e P2 será exibida com o negativo da imagem na região correspondente.
Para o desenvolvimento da aplicação, foi utilizado o programa pixels.cpp como referência. Ao executar o código no terminal do linux utilizando do arquivo Makefile, é solicitado que o usuário dê entrada para dois pontos, P1 e P2, com as coordenadas x e y. O resultado pode ser visto a seguir:
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int, char**){
Mat image, image2;
image = imread("../imagens/biel.png", CV_LOAD_IMAGE_GRAYSCALE);
image2 = image.clone();
if(!image.data)
cout << "Não foi possível abrir ou encontrar a imagem." << endl;
Point p1, p2;
cout << "Informe as coordenadas de dois pontos entre (0, 0) e (255, 255):" << endl;
do{
cout << "P1.x: ";
cin >> p1.x;
cout << "P1.y: ";
cin >> p1.y;
cout << "P2.x: ";
cin >> p2.x;
cout << "P2.y: ";
cin >> p2.y;
}while(p1.x > 255 || p1.x < 0 || p1.y > 255 || p1.y < 0 ||
p2.x > 255 || p2.x < 0 || p2.y > 255 || p2.y < 0 ||
p1.x > p2.x || p1.y > p2.y);
cout << "Pontos selecionados: "<< "P1("<< p1.x <<", "<< p1.y<<") e P2("<< p2.x<<", "<< p2.y<<")"<< endl;
for(int i=p1.x;i < p2.x;i++){
for(int j=p1.y;j < p2.y;j++){
image2.at< uchar >(i,j) = 255 - image2.at< uchar >(i,j);
}
}
namedWindow("Imagem Original",WINDOW_AUTOSIZE);
imshow("Imagem Original", image);
namedWindow("Imagem Negativo",WINDOW_AUTOSIZE);
imshow("Imagem Negativo", image2);
waitKey(0);
return 0;
}
Utilizando o programa pixels.cpp como referência, implemente um programa trocaregioes.cpp
. Seu programa deverá trocar os quadrantes em diagonal na imagem. Explore o uso da classe Mat
e seus construtores para criar as regiões que serão trocadas.
Para o desenvolvimento da aplicação, foram criados quatro regiões de interesse, de forma que podemos manipulá-las para obtermos o efeito desejado. A seguir segue o código desenvolvido:
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int, char **)
{
Mat image, aux, aux2;
image = imread("../imagens/biel.png", CV_LOAD_IMAGE_GRAYSCALE);
if (!image.data)
{
cout << "Não foi possível abrir ou encontrar a imagem." << endl;
return 0;
}
aux = image(Rect(0, 0, 128, 128)).clone(); //Rect(x0, y0, x, y ) x e y é o quanto eu quero pegar
aux2 = image(Rect(128, 128, 128, 128)).clone();
//TROCA DE REGIÕES
aux.copyTo(image(Rect(128, 128, 128, 128)));
aux2.copyTo(image(Rect(0, 0, 128, 128)));
aux = image(Rect(0, 128, 128, 128)).clone(); //Rect(x0, y0, x, y ) x e y é o quanto eu quero pegar
aux2 = image(Rect(128, 0, 128, 128)).clone();
//TROCA DE REGIÕES
aux.copyTo(image(Rect(128, 0, 128, 128)));
aux2.copyTo(image(Rect(0, 128, 128, 128)));
namedWindow("Imagem", WINDOW_AUTOSIZE);
imshow("Imagem", image);
waitKey(0);
return 0;
}
Para facilitar na programação, foi utilizado o método Rect()
, que permite fazer uma cópia dos pixels de uma determinada área da imagem que é especificada como parâmetro. Executando o código, obtemos o seguinte efeito:
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.
Como a imagem é composta por pixels de 8 bits, por ser tom de cinza a imagem poderá ter 256 diferentes tons. Retirando a cor de fundo (preta), temos 255 tons de cinza. Logo, se tivermos mais de 255 objetos não será possível rotulá-los na imagem. O interessante seria especificar um tom de cinza diferente de 0 e 255 e rotular todos os objetos com o mesmo tom. Com isso, não importa a quantidade de objetos.
Aprimore o algoritmo de contagem apresentado para identificar regiões com ou sem buracos internos que existam na cena. Assuma que objetos com mais de um buraco podem existir. Inclua suporte no seu algoritmo para não contar bolhas que tocam as bordas da imagem. Não se pode presumir, a priori, que elas tenham buracos ou não.
Uma forma de contar o número de buracos é através de rótulos, ou seja, como os buracos possuem o mesmo tom de cinza do fundo, podemos alterar a cor de fundo e buscar os buracos das bolhas, que terão seu tom de cinza igual ao do fundo original (0). Com o auxílio do método floodFill()
podemos encontrar essas regiões e fazer sua contagem.
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char **argv)
{
Mat image, mask;
int width, height;
int nobjects, noburacos;
CvPoint p;
image = imread("../imagens/bolhas.png", CV_LOAD_IMAGE_GRAYSCALE);
if (!image.data) {
cout << "imagem nao carregou corretamente\n";
return (-1);
}
width = image.size().width; //colunas
height = image.size().height; //linhas
p.x = 0;
p.y = 0;
// Elimina os objetos que tocam as laterais da imagem, rotulando-os com a cor de fundo;
for (int k = 0; k < height; k++) {
floodFill(image, CvPoint(k,0), 0);
floodFill(image, CvPoint(k,255), 0);
floodFill(image, CvPoint(0,k), 0);
floodFill(image, CvPoint(255,k), 0);
}
// Contagem de objetos
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;
//Todos os objetos são rotulados no tom de cinza 200;
floodFill(image, p, 200);
}
}
}
// Contagem de objetos com buracos
//Uma forma de contar o número de buracos é através de rótulos, ou seja, como os buracos
//possuem o mesmo tom de cinza do fundo, podemos alterar a cor de fundo e buscar os objetos,
//que terão seu tom de cinza igual ao do fundo. Depois podemos bunscar na imagem o número de
//regiões de tom de cinza 0, que equivale ao buraco.
floodFill(image,cvPoint(0,0), 50); // utilizando o floodFill(), mudaremos a cor de fundo
noburacos = 0;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (image.at < uchar > (i, j) == 0) { //busca objetos da cor de fundo original
p.x = j;
p.y = i;
//Todos os buracos são rotulados no tom de cinza 50;
floodFill(image, p, 50);
if(image.at< uchar>(i, j-1) == 200)
{
noburacos++;
// preenche objeto com buraco com cor diferente, de forma que se acharmos um novo buraco e
//um pixel anterior for de cor diferente de 200, ele irá saber que a bolha possui outros
//buracos e não contabilizará novos buracos para aquela bolha.
floodFill(image,cvPoint(p.x-1,p.y), 150);
}
}
}
}
cout << "Número de objetos: " << nobjects << endl;
cout << "Número de buracos: " << noburacos << endl;
cout << "Número de objetos sem buracos: " << nobjects - noburacos << endl;
imshow("image", image);
imwrite("labeling.png", image);
waitKey();
return 0;
}
Para testar o código, utilizaremos a Figura 4, que é formada por um fundo preto (0) e bolhas em tom branco (255).
Utilizando o programa 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. Teste sua implementação apontando a câmera para ambientes com iluminações variadas e observando o efeito gerado. Assuma que as imagens processadas serão em tons de cinza.
A equalização de histograma é um método que permite mudar a distribuição dos valores de ocorrência em um histograma permitindo uma redução das diferenças acentuadas de tons e assim, particularmente em imagens, acentuando detalhes não visíveis anteriormente.
Para a realização da aplicação, foi utilizado o exemplo de referência. Dessa forma, temos o seguinte código:
#include <iostream>
#include <cv.h>
#include <highgui.h>
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.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, histG, histB;
int nbins = 64;
float range[] = {0, 256};
const float *histrange = { range };
bool uniform = true;
bool acummulate = false;
cap.open(0); //seleciona a camera
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));
Mat histImgG(histh, histw, CV_8UC3, Scalar(0,0,0));
Mat histImgB(histh, histw, CV_8UC3, Scalar(0,0,0));
while(1){
cap >> image;
//Redimensionar a captura
resize(image, image, Size(640, 360));
///////////////////////////////////////////////////////////////////////////////////////////////////
// EQUALIZAÇÃO
///////////////////////////////////////////////////////////////////////////////////////////////////
//Separa a imagem capturada em três canais que são armazenados em "planes"
split (image, planes);
//Equalização das capturas
equalizeHist(planes[0], planes[0]);
equalizeHist(planes[1], planes[1]);
equalizeHist(planes[2], planes[2]);
//Utilizamos a função merge() para unir os planos ou canais equalizados em image.
merge(planes, image);
///////////////////////////////////////////////////////////////////////////////////////////////////
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, histImgG.rows, NORM_MINMAX, -1, Mat());
normalize(histB, histB, 0, histImgB.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)));
imshow("image", image);
if(waitKey(30) >= 0) break;
}
return 0;
}
O cálculo do histograma será realizado para cada uma das componentes de cor de forma independente. Logo, a separação das componentes em matrizes independentes será feita no vetor de matrizes planes
. Assim, planes[0]
, planes[1]
e planes[2]
armazenarão as componentes de cor Vermelho, Verde e Azul, respectivamente. É utilizado o método split()
para a separação dos componentes de cor. Para fazer a equalização, é utilizado o método equalizeHist()
, que é do OpenCV, ele é utilizado para todas as componentes da imagem em captura. Para finalizar, é utilizado o método merge()
para unir os planos e formar a nova imagem equalizada.
A seguir, a Figura 7 mostra a captura sem equalização e a Figura 8 mostra o resultado da equalização.
Podemos perceber no histograma que que o da figura 7 possui mais picos elevados do que o da Figura 8, isso ocorre devido a equalização.
Utilizando o programa 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 Motion Detection ou detector de movimento, pode ser utilizado para diferentes aplicações, sendo a mais comum em serviços de segurança e monitoramento. Para Detectarmos movimento, foi utilizado o histograma da captura, sendo nescessário apenas de uma componente. Nesse caso, no início da captura foi armazenado um histograma, de forma que nas capturas seguintes elas são comparadas com a primeira captura, que está armazenada durante a execução da aplicação.
Há um valor que é utilizado como limiar para sabermos se na variação do histograma houve um movimento grande movimento perceptível. A seguir, código desenvolvido.
#include <iostream>
#include <cv.h>
#include <highgui.h>
#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 hg_old, hg_new; // histograma verde antigo e novo
Mat histR, histG, histB;
int nbins = 64;
float range[] = {0, 256};
const float *histrange = { range };
bool uniform = true;
bool acummulate = 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));
Mat histImgG(histh, histw, CV_8UC3, Scalar(0,0,0));
Mat histImgB(histh, histw, CV_8UC3, Scalar(0,0,0));
//As análises serão feitas utilizando o primeiro histograma, ou seja, da primeira imagem tirada do ambiente.
//Dessa forma, qualquer alteração dentro do limite x informará movimentação.
cap >> image;
split (image, planes);
calcHist(&planes[1], 1, 0, Mat(), hg_old, 1, &nbins, &histrange, uniform, acummulate);
double x = 0;
while(1){
cap >> image;
resize(image, image, Size(640,360));
///////////////////////////////////////////////////////////////////////////////////////////////////
//DETECÇÃO DE MOVIMENTO
split (image, planes);
calcHist(&planes[1], 1, 0, Mat(), hg_new, 1, &nbins, &histrange, uniform, acummulate);
x = compareHist(hg_new, hg_old, CV_COMP_CORREL);
cout << "x = "<< x<< endl;
if(x<-0.25){
putText(image, "ALARME", cvPoint(250, 100), FONT_HERSHEY_COMPLEX_SMALL, 1, Scalar(255, 0, 0), 2);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
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, histImgG.rows, NORM_MINMAX, -1, Mat());
normalize(histB, histB, 0, histImgB.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)));
imshow("image", image);
if(waitKey(30) >= 0) break;
}
return 0;
}
Utilizando o programa filtroespacial.cpp como referência, implemente 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. Compare o resultado desse filtro com a simples aplicação do filtro laplaciano.
Utilizando o código de referência, obtemos alguns resultados. Aqui temos o código desenvolvido como a adição do filtro "Laplaciano do Gussiano":
#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"
"x - 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
};
// Aplicação da máscara "Laplaciano do Gaussiano", comum na literatura
float x[]= {0, 0, -1, 0, 0,
0, -1, -2, -1, 0,
-1, -2, 16, -2, -1,
0, -1, -2, -1, 0,
0, 0, -1, 0, 0
};
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
menu();
for (;;) {
video >> cap;
resize(cap, cap, Size(640,360));
cvtColor(cap, frame, CV_BGR2GRAY);
flip(frame, frame, 1);
imshow("original", frame);
frame.convertTo(frame32f, CV_32F);
filter2D(frame32f, frameFiltered, frame32f.depth(), mask, Point(1, 1), 0);
if (absolut) {
frameFiltered = abs(frameFiltered);
}
frameFiltered.convertTo(result, CV_8U);
imshow("filtroespacial", result);
key = (char) waitKey(10);
if (key == 27)
break; // esc pressed!
switch (key) {
case 'a':
menu();
absolut = !absolut;
break;
case 'm':
menu();
mask = Mat(3, 3, CV_32F, media);
scaleAdd(mask, 1 / 9.0, Mat::zeros(3, 3, CV_32F), mask1);
mask = mask1;
printmask(mask);
break;
case 'g':
menu();
mask = Mat(3, 3, CV_32F, gauss);
scaleAdd(mask, 1 / 16.0, Mat::zeros(3, 3, CV_32F), mask1);
mask = mask1;
printmask(mask);
break;
case 'h':
menu();
mask = Mat(3, 3, CV_32F, horizontal);
printmask(mask);
break;
case 'v':
menu();
mask = Mat(3, 3, CV_32F, vertical);
printmask(mask);
break;
case 'l':
menu();
mask = Mat(3, 3, CV_32F, laplacian);
printmask(mask);
break;
//Implementação do case para o "laplaciano do Gaussiano"
case 'x':
menu();
mask = Mat(5, 5, CV_32F, x);
printmask(mask);
break;
default:
break;
}
}
return 0;
}
Analizando os resultados, podemos verificar que o "Laplaciano do Gaussiano" deixa as bordas ainda mais aparente, em comparação ao filtro Laplaciano, entretanto, percebe-se mais ruidos.
Utilizando o programa addweighted.cpp como referência, implemente um programa tiltshift.cpp
. Três ajustes deverão ser providos na tela da interface:
Independente de como o resultado é obtido (seja da maneira tradicional, através do uso de lentes especiais, ou por pós-produção digital), a aparência é a mesma graças ao desfoque local em algumas áreas específicas. O olho estranha esse desfoque, que não é natural para objetos e edifícios tão grandes e o cérebro acaba percebendo aquilo como uma miniatura.
Nessa Atividade foi feito um programa que realize o efeito tilt-shift, que consiste em simular miniaturas de imagens desfocando determinadas partes da imagem escolhida. Para implementar tal efeito, deve ser feita uma filtragem de borramento em uma determinada faixa escolhida, além de poder configurar a sua intensidade. É utilizado o createTrackbar()
para criar a barra que definirá o efeito do borramento em uma determinada área. Também é feito o cálculo do alfa que é uma relação entre tangentes hiperbólicas envolvendo duas coordenadas definidas.
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int height;
int MAX = 100;
double d = 6;
int d_slider = 0;
int t_slider = 0;
int t = 0;
int h = 20;
int h_slider = 0;
Mat image1, image2, alpha, beta;
char TrackbarName[50];
void tiltShift(){
Mat mWeighted1, mWeighted2, tiltshift;
int l1 = -h/2;
int l2 = -l1;
alpha = Mat::zeros(image1.rows, image1.cols, CV_32F);
beta = Mat::zeros(image1.rows, image1.cols, CV_32F);
int i, j;
for (i = 0; i < alpha.rows; i++) {
int x = i - (t + h/2);
float alfa = 0.5f * (tanh((x - l1)/d) - tanh((x - l2)/d));
for (j = 0; j < alpha.cols; j++) {
alpha.at< float>(i, j) = alfa;
beta.at< float>(i, j) = 1 - alfa;
}
}
Mat auxA[] = {alpha, alpha, alpha};
Mat auxB[] = {beta, beta, beta};
merge(auxA, 3, alpha);
merge(auxB, 3, beta);
image1.convertTo(mWeighted1, CV_32FC3);
image2.convertTo(mWeighted2, CV_32FC3);
multiply(mWeighted1, alpha, mWeighted1);
multiply(mWeighted2, beta, mWeighted2);
add(mWeighted1, mWeighted2, tiltshift);
tiltshift.convertTo(tiltshift, CV_8UC3);
imshow("tiltshift", tiltshift);
imshow("imagem", alpha);
imshow("borramento", beta);
}
void on_trackbar_d(int, void*){
d = (double) d_slider;
if (d < 1) {
d = 1;
}
tiltShift();
}
void on_trackbar_t(int, void *){
t = t_slider*height/MAX;
tiltShift();
}
void on_trackbar_h(int, void*) {
h = h_slider*height/MAX;
if (h == 0) {
h = 1;
}
if (h > height) {
h = height;
}
tiltShift();
}
int main(int argvc, char** argv){
image1 = imread("../imagens/trem.jpg");
height = image1.size().height;
image2 = image1.clone();
Mat aux, mask, mask1;
float media[] = {1,1,1,
1,1,1,
1,1,1};
mask = Mat(3, 3, CV_32F, media);
scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
mask = mask1;
image2.convertTo(aux, CV_32F);
for (int i = 0; i < 10; i++) {
filter2D(aux, aux, aux.depth(), mask, Point(1, 1), 0);
}
aux = abs(aux);
aux.convertTo(image2, CV_8UC3);
namedWindow("tiltshift", 1);
sprintf( TrackbarName, "Altura");
createTrackbar( TrackbarName, "tiltshift",
&h_slider,
MAX,
on_trackbar_h);
sprintf( TrackbarName, "Decaimento");
createTrackbar( TrackbarName, "tiltshift",
&d_slider,
MAX,
on_trackbar_d);
sprintf( TrackbarName, "Vertical");
createTrackbar( TrackbarName, "tiltshift",
&t_slider,
MAX,
on_trackbar_t );
tiltShift();
waitKey(0);
return 0;
}
Utilizando o programa dft.cpp como referência, implemente 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.
Através do modelo de iluminação-reflectância, é possível desenvolver um procedimento no domínio da frequência para melhorar a aparência de uma imagem efetuando simultaneamente a compressão da faixa de intensidade e o realce de contraste. Uma imagem pode ser expressa por $f(x,y) = i(x,y)r(x,y)$ , mas ela não pode ser utilizada diretamente para atuar sobre os componentes de frequência de iluminação e reflectância, pois a Transformada de Fourier de um produto não é o produto das transformadas. Para que isso seja possível, aplicaremos o logaritmo na expressão, pois o logaritmo de um produto é a soma dos logaritmos.
$$z(x,y) = ln(f(x,y)) = ln(i(x,y)) + ln(r(x,y))$$
Podemos seguir os passos abaixo para obtenção da imagem filtrada:
$$f(x,y) \longrightarrow ln \longrightarrow DFT \longrightarrow H(u,v) \longrightarrow (DFT)^{-1} \longrightarrow exp \longrightarrow g(x,y)$$
O cálculo do filtro é dado por: $$H(u,v) = (\gamma_{H} - \gamma_{L})[1-e^{-c[D^2(u,v)/D_0^2]}] + \gamma_{L} $$ onde $c$ é a constante que controla a inclinação da função à medida que ela realiza a transição entre $\gamma_{L}$ e $\gamma_{H}$.
A seguir, código desenvolvido a partir desses conceitos:
#include <iostream>
#include <highgui.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
Mat complexImage, padded, filter;
Mat image, imagegray, tmp;
Mat_< float > realInput, zeros;
vector < Mat > planos;
int dft_M, dft_N;
int gammaL=0, gammaL_max=100;
int gammaH=0, gammaH_max=100;
int D0=0, D0_max=100;
int C0=0, C0_max= 100;
char key; // Guarda valor para ESC
void callFilter(int, void*)
{
int dft_M= tmp.size().height;
int dft_N= tmp.size().width;
// Normalização dos parâmetros
double gH = gammaH/10.0;
double gL = gammaL/1000.0;
double d0 = D0/100.0;
double c0 = C0/100.0;
// Calcula o filtro pixel a pixel
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;
}
}
// cria a matriz com as componentes do filtro e junta
// ambas em uma matriz multicanal complexa
Mat comps[]= {tmp, tmp};
merge(comps, 2, filter);
imshow("Filtro", tmp);
}
// 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 copias 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("Filtro Homomórfico", CV_WINDOW_AUTOSIZE);
namedWindow("Filtro", CV_WINDOW_AUTOSIZE);
createTrackbar("GammaH", "Filtro Homomórfico", &gammaH, gammaH_max, callFilter);
createTrackbar("GammaL", "Filtro Homomórfico", &gammaL, gammaL_max, callFilter);
createTrackbar("C", "Filtro Homomórfico", &C0, C0_max, callFilter);
createTrackbar("D0", "Filtro Homomórfico", &D0, D0_max, callFilter);
// carrega imagem
image = imread("noite.jpg", CV_LOAD_IMAGE_GRAYSCALE);
if(!image.data){
cout<<"Não foi possível abrir a image.!\n";
return -1;
}
resize(image, image, Size(640,480));
//imwrite("original.png", 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 funcaoo de transferenncia (filtro frequencial) deve ter o
// mesmo tamanho e tipo da matriz complexa
filter = complexImage.clone();
tmp = Mat(dft_M, dft_N, CV_32F);
// O programa permanecerá em loop para receber as manipulações do Trackbar
for(;;)
{
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("Filtro Homomórfico", planos[0]);
key = (char) waitKey(10);
if( key == 27 )
break; // esc pressed
}
Mat res;
planos[0].convertTo(res, CV_8UC1, 255.0);
// salva a imagem resultante
imwrite("filtrada.png", res);
vector < Mat > pFiltro;
split(filter,pFiltro);
normalize(pFiltro[0], pFiltro[0], 0, 1, CV_MINMAX);
pFiltro[0].convertTo(res, CV_8UC1, 255.0);
// salva uma imagem do filtro
imwrite("filtro.png", res);
waitKey(0);
return 0;
}
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:
Esta atividade teve como intuito o desenvolvimento do filtro de Canny e gerar uma imagem com pontilhismo. Primeiramente foi aplicado na imagem o processo do pontilhismo, em seguida, a imagem bruta passou por algumas iterações do filtro de Canny, sendo possível delimitar suas bordas. Em cada uma das iterações o limiar do filtro é alterado com a intenção de obter uma imagem com menos bordas. Para as bordas ficarem mais destacadas foi necessário percorrer a imagem filtrada e desenhar um círculo em cada pixel que possuísse um tom de cinza maior que zero.
#include <iostream>
#include <highgui.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <fstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <ctime>
#include <cstdlib>
using namespace std;
using namespace cv;
//Definição de constantes
#define STEP 5
#define RAIO 3
#define JITTER 3
int main(int argc, char** argv){
vector< int> yrange;
vector< int> xrange;
Mat image, borderImage;
Mat pontilhismo;
int width, height, gray, x, y;
image= imread("ponte.jpg",CV_LOAD_IMAGE_GRAYSCALE);
srand(time(0));
if(!image.data){
cout << "Impossível ler a imagem."<< endl;
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(int i=0; i < xrange.size(); i++){
xrange[i] = xrange[i]*STEP+STEP/2;
}
for(int j=0; j < yrange.size(); j++){
yrange[j] = yrange[j]*STEP+STEP/2;
}
image.copyTo(pontilhismo);
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< uchar>(x,y);
circle(pontilhismo,
cv::Point(y,x),
RAIO,
CV_RGB(gray,gray,gray),
-1,
CV_AA);
}
}
imshow("pontilhismo", pontilhismo);
imwrite("pontilhismo.png", pontilhismo);
for(int n = 0; n < 5; n++){
Canny(image, borderImage, 10 * n, 50 * n);
imshow("cannyborderImage", borderImage);
imwrite("cannyborderImage.png", borderImage);
int raio = 5 - n;
for(int i = 0; i < height; i++){
for(int j = 0; j < width; j++){
if(borderImage.at< uchar>(i,j)>0){
gray = image.at< uchar>(i,j);
circle(pontilhismo,
cv::Point(j,i),
raio,
CV_RGB(gray,gray,gray),
-1,
CV_AA);
}
}
}
}
imshow("cannyPoints", pontilhismo);
imwrite("cannyPoints.png", pontilhismo);
waitKey();
return 0;
}
Projeto Final