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

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: