Archive for março \15\UTC 2012

Somando imagens pixel a pixel

Ok… nos posts anteriores demonstrei algumas coisas sobre processamento digital de imagens. Vimos introdução ao processingo, convolução e detecção de pontos brilhantes… mas vamos fazer algo diferente: e se somássemos duas imagens, pixel a pixel, isolando os canais? Bom…ok… para fazer isso, vamos nos utilizar de dois pontos de iluminação de lados opostos da imagem (não é um pré-requisito, mas a visualização do trabalho fica melhor). Primeiro, tiramos uma foto com as duas luzes ligadas, como na imagem a seguir:

sim, eu tenho minha irmã tem um pinguim de pelúcia, o Tux…

ok… depois alternamos as luzes e tiramos fotos:

hm… aos detalhes: é importante que a câmera fique parada! E, como mostrarei daqui a pouco, o objeto também…Outra coisa muito importante também é o ajuste manual da câmera… como tempo de exposição e etc. O ajuste manual facilita muito a verificação mas caso não possua uma câmera que permita isso, a imagem deve ser corrigida no braço!

Vamos ao algoritmo:

Por fim, temos um resultado:

Como podem ver, meu pinguim emagreceu um pouco… acidentalmente esbarrei na base dele quando ia desligar um dos leds…mas é assim mesmo…xD

Vamos fazer uma experiência?

E se isolássemos um dos canais? Como o vermelho do lado esquerdo e o azul do lado direito, por exemplo?
Teríamos o seguinte:

Aí está! A impressão que é passada é que havia um holofote azul à esquerda e um vermelho à direita durante a foto…
mas isso foi feito no pós-processamento. Legal, né? xD

Mas foi isso… mais um post sobre processamento de sinais. Em breve, teremos alguns com áudio. Até lá!

Processing e convolução

Ok… vamos à mais um post sobre Processing e suas magias!

Quando se carrega uma imagem no processing, através do método loadImage("nome_da_imagem.jpg");, esta vem na forma de um Array. Quando se trata de imagens, no entanto, é mais fácil de se trabalhar com Matrizes (É possível se aplicar a operação aqui descrita em arrays também…). Mas vamos lá: Convolução é a antiga arte Jedi uma operação efetuada sobre a imagem que permite que criemos efeitos. Consiste no seguinte:

1 – Tendo a seguinte matriz (imagine como a imagem original):

1    3    2
9    2    1
2    5    0

temos o elemento a[0][0] = 1, a[1][0] = 3, a[0][1] = 9 etc.

2 – Temos também, o seguinte kernel de convolução (é a matriz que define o efeito a ser utilizado):

-1    0    1
-2    0    2
-1    0    1

A convolução se dá através do somatório do produto dos elementos adjacentes ao elemento central, considerando como 0 os elementos que extrapolarem o limite da matriz. Assim, o elemento da matriz resultante

b[x][y] = a[x-1][y-1] * k[0][0] + a[x][y-1] * k[1][0] + … + a[x+1][y+1] * k[2][2];

Quando x-1 ou y-1 forem menores que zero, considera-se o valor do elemento como zero.

Mas vamos à demonstrações práticas:

Para o primeiro exemplo, usaremos o operador de Sobel. Ele é utilizado para a detecção de bordas e consiste em gerar uma matriz com o Sobel Horizontal e uma com o Sobel Vertical. A detecção de bordas é dada pela combinação de ambas as matrizes.

Definimos as matrizes:

Criamos um método convolution(); que retorna a matriz com convolução já aplicada e recebe como parâmetros o kernel e a imagem.

Por fim, definimos o método setup();

E o resultado final:

o Código completo:

int[][] kernelY = {
{ -1, -2, -1 },
{ 0, 0, 0 },
{ 1, 2, 1 }
};

int[][] kernelX = {
{ -1, 0, 1 },
{ -2, 0, 2 },
{ -1, 0, 1 }
};

float[][] convolution(int[][] kernel, PImage img) {

float[][] retorno = new float[img.width][img.height];
float[][] imgMatrix = new float[img.width][img.height];
float soma = 0;

for (int y = 0; y < img.height; y++) {
for (int x = 0; x < img.width; x++) {
imgMatrix[x][y] = red(img.pixels[y * img.width + x]);
}
}

for (int y = 0; y < img.height; y++) {
for (int x = 0; x < img.width; x++) {
for (int j = -1; j <= +1; j++) {
for (int i = -1; i <= +1; i++) {
try {
soma = kernel[i+1][j+1] * imgMatrix[x+i][y+j] + soma;
}
catch (Exception e) {
// print(e);
}
}
}
retorno[x][y] = soma;
soma = 0;
}
}
return retorno;
}

void setup() {

PImage img = loadImage("valve.png");
size (2 * img.width + 10, img.height);
PImage im = img;
image(img, 0, 0);

img.loadPixels();

float[][] Gx = convolution(kernelX, img);
float[][] Gy = convolution(kernelY, img);

for (int j = 0; j < img.height; j++) {
for (int i = 0; i < img.width; i++) {
img.pixels[j * img.width + i] = color(abs(Gy[i][j]) + abs(Gx[i][j]));
}
}
img.updatePixels();
image(img, img.width + 10, 0);
}

Por curiosidade, teste utilizar o kernel (apenas ele e sem valor absoluto):

-1, -1, -1
-1, 8, -1
-1, -1, -1

com isso, você será capaz de detectar pontos mais brilhantes cercados por pontos mais escuros, como na imagem a seguir:

O link para a imagem original:

http://upload.wikimedia.org/wikipedia/commons/f/f0/Valve_original_%281%29.PNG