1. Objetivo
Esse projeto deverá nos permitir nos aprofundar nos conceitos abordados em projetos anteriores, assim como aplicá-los em um tipo de filtro que simula um tipo muito caro de lente: a lente Tilt Shift.
2. Tilt Shift
O pricípio do Tilt Shift baseia-se em dar uma pequena inclinação à lente da câmera; em vez da lente ser paralela ao plano de foco da câmera, há uma pequena inclinação entre os dois eixos. Isso faz com que a o foco da câmera fique somente em uma das regiões da imagem, deixando as outras partes com distorção ou borramento.
Esse feito dá a aparência de miniatura às imagens, conforme será mostrado na saída da imagem, em seguida. Para simularmos digitalmente o efeito da imagem, somaremos a imagem original com uma cópia sua borrada, poderadas pela seguinte função
\$\alpha(x) = \frac{1}{2}*(tanh(\frac{x-L1}{d}) - tanh(\frac{x-L2}{d}))\$
Isso dará o efeito de degradê à saída da função. Ajustaremos os parâmetros L1 e L2 (pontos onde a função retorna aproximadamente 0.5) e o valor de d, que é o decaimento (inclinação do decaimento da função).
3. O Código
O código é mostrado abaixo, e é explicado logo em seguida.
#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace cv;
using namespace std;
double d;
int L1_slider = 0;
int L1_slider_max;
int L2_slider = 0;
int L2_slider_max;
int d_slider = 0;
int d_slider_max = 100;
int contrast_slider = 0;
int contrast_slider_max = 100;
int previous_contrast = 0;
Mat im,aux,result;
char TrackbarName[50];
double alpha(double x,double l1,double l2,double d1){
float retorno;
float k11 = (x-l1)/d1;
float k22 = (x-l2)/d1;
retorno = 0.5*(tanh(k11) - tanh(k22));
return retorno;
}
void juntar(Mat& src1 , Mat& src2){
for(int i=2;i<src1.rows;i++){
double alfa = alpha(i,L1_slider,L2_slider,d);
addWeighted(src1.row(i),alfa, src2.row(i),1-alfa,0.0,result.row(i));
}
}
void on_trackbar_d(int, void*){
d = (double) d_slider;
juntar(im,aux);
result.convertTo(result, CV_8UC3);
imshow("TiltShift",result);
}
void on_trackbar_L1(int, void*){
juntar(im,aux);
result.convertTo(result, CV_8UC3);
imshow("TiltShift",result);
}
void on_trackbar_L2(int, void*){
juntar(im,aux);
result.convertTo(result, CV_8UC3);
imshow("TiltShift",result);
}
float media[] = {1,1,1,
1,1,1,
1,1,1};
Mat mask = Mat(3,3,CV_32F,media),mask1;
int main(int argvc, char** argv){
im = imread(argv[1],CV_LOAD_IMAGE_COLOR);
namedWindow("TiltShift", WINDOW_NORMAL);
cout<<"Melhorar cores?";
bool escolha;
cin>>escolha;
if(escolha){
cvtColor(im,im,CV_BGR2HSV);
vector<Mat> planes;
split(im,planes);
//equalizeHist(planes[2],planes[2]);
planes[1].convertTo(planes[1], CV_8UC1,1,30);
merge(planes,im);
cvtColor(im,im,CV_HSV2BGR);
}
aux = im.clone();
cout<<"width: "<<im.cols<<endl;
cout<<"height: "<<im.rows<<endl;
scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
mask = mask1;
for(int i=0;i<25;i++){
filter2D(aux, aux, im.depth(), mask, Point(1,1), 0);
}
L1_slider_max = im.rows;
L2_slider_max = im.rows;
im.convertTo(im,CV_32FC3);
aux.convertTo(aux,CV_32FC3);
result = im.clone();
sprintf( TrackbarName, "decaimento x %d", d_slider_max );
createTrackbar( TrackbarName, "TiltShift", &d_slider, d_slider_max, on_trackbar_d );
on_trackbar_d(d_slider, 0 );
sprintf( TrackbarName, "Linha Superior x %d", L1_slider_max );
createTrackbar( TrackbarName, "TiltShift", &L1_slider, L1_slider_max, on_trackbar_L1 );
on_trackbar_L1(L1_slider, 0 );
sprintf( TrackbarName, "Linha Inferior x %d", L2_slider_max );
createTrackbar( TrackbarName, "TiltShift", &L2_slider, L2_slider_max, on_trackbar_L2 );
on_trackbar_L2(L2_slider, 0 );
waitKey(0);
return 0;
}
3.1. Criando Trackbars
A criação de trackbars é bem explicada em uma página de referência do OpenCV. Criamos 3 Trackbars:
-
Ajuste do limite superior (L1)
-
Ajuste do limite inferior (L2)
-
Ajuste do Decaimento (d)
a criação é feita a partir das seguintes linhas:
sprintf( TrackbarName, "decaimento x %d", d_slider_max );
createTrackbar( TrackbarName, "TiltShift", &d_slider, d_slider_max, on_trackbar_d );
on_trackbar_d(d_slider, 0 );
sprintf( TrackbarName, "Linha Superior x %d", L1_slider_max );
createTrackbar( TrackbarName, "TiltShift", &L1_slider, L1_slider_max, on_trackbar_L1 );
on_trackbar_L1(L1_slider, 0 );
sprintf( TrackbarName, "Linha Inferior x %d", L2_slider_max );
createTrackbar( TrackbarName, "TiltShift", &L2_slider, L2_slider_max, on_trackbar_L2 );
on_trackbar_L2(L2_slider, 0 );
Juntamente com as respectivas funções que são chamadas quando as barras são deslocadas:
void on_trackbar_d(int, void*){
d = (double) d_slider;
juntar(im,aux);
imshow("TiltShift",result);
result.convertTo(result, CV_8UC3);
}
void on_trackbar_L1(int, void*){
juntar(im,aux);
imshow("TiltShift",result);
result.convertTo(result, CV_8UC3);
}
void on_trackbar_L2(int, void*){
juntar(im,aux);
imshow("TiltShift",result);
result.convertTo(result, CV_8UC3);
}
Os valores máximos são iguais ao número de linhas da imagen, começando a partir do zero (padrão do OpenCV). Desse modo, podemos nos deslocar livremente pela imagem.
L1_slider_max = im.rows;
L2_slider_max = im.rows;
3.2. Borrando a Imagem
Para fazer o borramento da imagem, criamos uma imagem auxiliar (cv::Mat aux;
) além da imagem que já havíamos criado (cv::Mat im
), cujo valor é o mesmo da imagem lida. Essa imagem auxiliar é, primeiramente, uma cópia da imagem original, e a ela aplicamos o filtro da média (cerca de 20-30 vezes) para que ela fique borrada.
float media[] = {1,1,1,
1,1,1,
1,1,1};
Mat mask = Mat(3,3,CV_32F,media),mask1;
.
.
.
aux = im.clone();
scaleAdd(mask, 1/9.0, Mat::zeros(3,3,CV_32F), mask1);
mask = mask1;
for(int i=0;i<30;i++){
filter2D(aux, aux, im.depth(), mask, Point(1,1), 0);
}
3.3. Aplicando o Ponderamento às Imagens
Aplicamos o poderamento utilizando a função void juntar(Mat &,Mat &)
, mostrada abaixo.
void juntar(Mat& src1 , Mat& src2){
for(int i=2;i<src1.rows;i++){
double alfa = alpha(i,L1_slider,L2_slider,d);
addWeighted(src1.row(i),alfa, src2.row(i),1-alfa,0.0,result.row(i));
}
}
Essa função realiza o ponderamento das imagens, linha a linha (utilizando a função cv::Mat.row()
), com os valores retornados pela função double alpha(double x,double L1, double L2, double d)
, como mostrado a seguir:
float alpha(double x,double l1,double l2,double d1){
float retorno;
float k11 = (x-l1)/d1;
float k22 = (x-l2)/d1;
retorno = 0.5*(tanh(k11) - tanh(k22));
return retorno;
}
Os valores utilizados por essa função são processados pela função cv::addWeighted()
, da seguinte forma:
\$\text{result.row(x)} = \alpha*\text{src1.row(x)} + (1-\alpha)*\text{src2.row(x)}\$
Para utilizarmos a função tanh() , ou seja, a tangente hiperbólica de um determinado valor, utilizamos a biblioteca <cmath>
|
3.4. Melhorando as Cores
Podemos aumentar a saturação das cores e seu contraste para dar um efeito de miniaturas. Brinquedos, em geral, tem cores muito saturadas. Isso pode ser feito utilizando a função "cvtColor()", modificando a componente "Saturation" da matriz (formato HSV), como mostrado a seguir:
cout<<"Melhorar cores?";
bool escolha;
cin>>escolha;
if(escolha){
cvtColor(im,im,CV_BGR2HSV);
vector<Mat> planes;
split(im,planes);
//equalizeHist(planes[2],planes[2]);
planes[1].convertTo(planes[1], CV_8UC1,1,30);
merge(planes,im);
cvtColor(im,im,CV_HSV2BGR);
}
Essa função só é executada no início do programa e pode ser melhorada para que o ganho de saturação seja também definido pelo usuário, com uma trackbar. O algoritmo foi sugeido por um usuário do StackOverflow.
3.5. A Interface do Usuário
O usuário tem acesso às 3 trackbars, como definidas anteriormente, assim como é capaz de redimensionar a janela, devido à inclusão do parâmetro WINDOW_NORMAL
na função namedWindow()
(criação da janela).
namedWindow("TiltShift",WINDOW_NORMAL);
O redimensionamento da janela é útil quando trabalhamos com imagens muito grandes. A interface é mostrada a seguir:

4. A Saída do Programa
Escolhemos uma imagem qualquer pesquisando-a no Google. A seguir mostramos a saída do nosso programa. O mais interessante dessa aplicação é o efeito de miniatura que a imagem ganha.






5. Referências
Tomamos como base, para a execução do nosso programa, a listagem feita pelo professor Agostinho, disponível em seu GitHub: