1. Objetivo
Por meio deste trabalho devemos ser capazes de entender como funciona um dos filtros mais comuns em câmeras digitais atuais e programas de edição de imagens - o negativo - e nos aprofundarmos um pouco mais no assunto da manipulação de imagens.
2. Obtendo o Negativo de Imagens
Primeiramente, para podermos manipular adequadamente uma imagem, devemos saber quantos bits essa imagem possui. O mais comum é que imagens em escala de cinza possuam 8 bits. Isso quer dizer que elas possuem \$2^8 = 256\$ tons de cinza possíveis. Desse modo, temos que o valor máximo que podemos atribuir a um pixel é 255, pois a contagem começa no 0.
O próximo passo é sabermos qual é a cor complementar de uma cor. A cor complementar é aquela que quando somada a uma cor temos o valor máximo, que para o nosso caso é 255. desse modo:
\$\text{cor_complementar} = 255 - {cor_do_pixel}\$
E assim definimos a cor negativa de uma cor como sendo a sua complementar. Veremos, a partir da saída do programa, que essa definição é correta e corresponde ao negativo que estamos acostumados a ver usualmente.
É importante também lembrarmos que, por definição:
3. O Código
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[]){
Mat im = imread(argv[1],CV_LOAD_IMAGE_COLOR);
if(!im.data) cout<<"Nao abriu a imagem!\n";
CvPoint cp1, cp2;
namedWindow("janela",WINDOW_AUTOSIZE);
cout<<"Tamanho da imagem: "<<im.cols<<'x'<<im.rows<<endl;
cout<<"Diga dois pontos: \nX1: ";
cin>>cp1.y; cout<<"Y1: "; cin>>cp1.x;
cout<<"X2: "; cin>>cp2.y; cout<<"Y2: "; cin>>cp2.x;
int maiorx,menorx,maiory,menory;
if(cp1.x>=cp2.x){
maiorx = cp1.x;
menorx = cp2.x;
}
else{
maiorx = cp2.x;
menorx = cp1.x;
}
if(cp1.y>=cp2.y){
maiory = cp1.y;
menory = cp2.y;
}
else{
maiory = cp2.y;
menory = cp1.y;
}
for(int i=menorx;i<maiorx;i++){
for(int j=menory;j<maiory;j++){
im.at<Vec3b>(i,j)[0] = 255 - im.at<Vec3b>(i,j)[0];
im.at<Vec3b>(i,j)[1] = 255 - im.at<Vec3b>(i,j)[1];
im.at<Vec3b>(i,j)[2] = 255 - im.at<Vec3b>(i,j)[2];
}
}
imshow("janela",im);
imwrite("saida.jpg",im);
waitKey();
}
4. Explicando o Funcionamento do Código
Aqui é importante lembrarmos que grande parte das funções aqui utilizadas foram apresentadas e explicadas no Primeiro Projeto. Iremos detalhar somente aqueles que forem cuja utilização é novidade.
4.1. Pedindo ao Usuário os Pontos
O código tem a função de pedir ao usuário pontos de início e fim, para caso não se deseje negativar a imagem completa mas somente uma parte dela. Como esses pontos serão necessários adiante, fazemos testes para que eles sejam colocados em ordem crescente: \$x2>=x1\$ e \$y2>=y1\$, ou, na linguagem do programa, menorx<maiorx e menory<maiory.. O programa também mostra a dimensão da imagem, para que sirva de referência para o usuário, utilizando os parâmetros cv::Mat.rows
e cv::Mat.cols
, da classe Mat
, para linhas e colunas, respectivamente.
cout<<"Tamanho da imagem: "<<im.cols<<'x'<<im.rows<<endl;
cout<<"Diga dois pontos: \nX1: ";
cin>>cp1.y; cout<<"Y1: "; cin>>cp1.x;
cout<<"X2: "; cin>>cp2.y; cout<<"Y2: "; cin>>cp2.x;
int maiorx,menorx,maiory,menory;
if(cp1.x>=cp2.x){
maiorx = cp1.x;
menorx = cp2.x;
}
else{
maiorx = cp2.x;
menorx = cp1.x;
}
if(cp1.y>=cp2.y){
maiory = cp1.y;
menory = cp2.y;
}
else{
maiory = cp2.y;
menory = cp1.y;
}
As entradas dos x’s e y’s estão trocadas na função std::cin>> pois as coordenadas utilizadas nas classes cv::CvPoint e cv::Mat também estão trocadas. Isso corrige o erro.
|
4.2. Efetuando a Negativação da Imagem
Esse processo é feito utilizando a equação mostrada na seção 2. Como estamos agora trabalhando com uma imagem RGB, devemos executar a operação em cada uma das componentes. Para uma imagem RGB, cada componente também é de 8 bits, e assim a equação apresentada anteriormente não muda. Desse modo:
for(int i=menorx;i<maiorx;i++){
for(int j=menory;j<maiory;j++){
im.at<Vec3b>(i,j)[0] = 255 - im.at<Vec3b>(i,j)[0];
im.at<Vec3b>(i,j)[1] = 255 - im.at<Vec3b>(i,j)[1];
im.at<Vec3b>(i,j)[2] = 255 - im.at<Vec3b>(i,j)[2];
}
}
As submatrizes da hipermatriz im
são as componentes RGB da matriz, porém não nessa ordem. As matrizes estão na ordem B,R e G, algo feito por definição na elaboração das funções do opencv. Temos também o Vec3b
, que é um typedef definido por: typedef Vec<uchar, 3> Vec3b
. O Vec3b é utilizado template da função at()
, e indica que temos um vetor de unsigned char de 3 posições (por isso podemos acessar cada uma das submatrizes utilizando o operador []).
4.3. Execução e Saída
Para executarmos o programa no terminal, devemos rodar o arquivo Makefile
no terminal, e em seguida passarmos uma imagem como parâmetro:
$ make negative
$ ./negative bplathi.jpg

Durante a execução do programa, é essa a interface a que temos acesso:
Tamanho da imagem: 292x292
Diga dois pontos:
X1: 0
Y1: 0
X2: 150
Y2: 150
Aqui passamos como parâmetros os pontos \$(0,0)\$ e \$(150,150)\$, e obtivemos a seguinte saída:

A imagem foi gravada utilizando a função Mat::imwrite(string nome, Mat imagem); .
|