Archive for the ‘Tecnologia’ Category

2D Transformation Demo

Opa! Fala galera! Voltando aqui depois de um bom tempo! Mas volto mostrando uma pequena aplicação desenvolvida por mim com a intenção de demonstrar como funcionam as transformações 2D.

Em primeiro lugar, falemos do que se tratam as transformações: Uma imagem 2D é composta formada por um agrupamento de pontos. Uma transformação consiste na multiplicação de cada um desses pontos por uma matriz específica. As transformações mais comuns são aquelas implementadas na maioria dos editores de imagem como rotação, translação e redimensionamento.

Na aplicação desenvolvida, foi utilizado Javascript juntamente com o elemento Canvas do HTML. A intenção foi demonstrar como funciona cada transformação, incluindo a matriz correspondente a cada transformação e a pilha de transformações, que funciona do seguinte modo: se queremos rotacionar um elemento e, em seguida, redimensioná-lo, multiplicamos ambas as matrizes referentes a cada operação para, só então, multiplicarmos por cada ponto da imagem. Isso diminui o processamento necessário para se realizar todo a transformação.

Okay… vamos à explicação do funcionamento da aplicação (Não postarei código ainda… vou implementar mais algumas coisas, dar uma melhorada e posto o código completo pra vocês): tenho um polígono desenhado basicamente com métodos lineTo() do contexto 2D do canvas. Cada um dos vértices desse polígono passa pela multiplicação com a matriz resultante da multiplicação de todas as matrizes da pilha. As operações envolvidas também são percebidas através do esquema de cores utilizado. Desse modo, para uma rotação da imagem sobre ela mesma, deslocamos o polígono até a origem das retas, executamos a rotação e deslocamos de volta ao ponto inicial. Assim, ao requisitar uma rotação desse tipo, na verdade, requisitamos três transformações cujas matrizes são empilhadas.

Bom… sem mais a dizer (por enquanto), segue o link pra aplicação:
2D Transformations Demo

Conforme vou lançando novas versões, vou atualizando o post com novas matrizes/funcionalidades!
Até a próxima. 😉

Anúncios

Implementando um Detector de Pitch com Processing

Já faz um tempo que havia prometido algum trabalho com áudio mas estava sem tempo/’motivação’ xD. Então vamos lá: Em todo som, existe uma frequencia que possui uma amplitude maior que as outras, ou seja, que se sobressai. É a chamada frequencia fundamental ou Pitch. O objetivo da implementação aqui descrita é detectar que frequencia é essa. Vamos lá:

Primeiro criamos uma classe que contem as notas e um método que verifica se uma frequencia passada é uma das notas ou não.

Ok. Feito isso, vamos à implementação dos outros métodos:

Imports/Variáveis:

import ddf.minim.*;
import ddf.minim.analysis.*;

Minim minim;
AudioInput in;
FFT fft;
SoundIdentify si = new SoundIdentify();

Método drawLines():

void drawLines() {

for (int i = 0; i < 12; i ++) {
line( 50, 50 + 50 * i, 350, 50 + 50 * i);
fill(0, 102, 153);
switch(i) {

case 0:
text("Lá", 370, 50 + 50 * i);
break;
case 1:
text("Lá #", 370, 50 + 50 * i);
break;
case 2:
text("Si", 370, 50 + 50 * i);
break;
case 3:
text("Dó", 370, 50 + 50 * i);
break;
case 4:
text("Dó #", 370, 50 + 50 * i);
break;
case 5:
text("Ré", 370, 50 + 50 * i);
break;
case 6:
text("Ré #", 370, 50 + 50 * i);
break;
case 7:
text("Mi", 370, 50 + 50 * i);
break;
case 8:
text("Fá", 370, 50 + 50 * i);
break;
case 9:
text("Fá #", 370, 50 + 50 * i);
break;
case 10:
text("Sol", 370, 50 + 50 * i);
break;
case 11:
text("Sol #", 370, 50 + 50 * i);
break;

}
}
}

Método drawCircle():

void drawCircle(int index) {

if (index != -1) {
ellipse(200, 50 + 50 * index, 30, 30);
}
}

setup():

void setup(){

size(500, 650, P2D);
minim = new Minim(this);
minim.debugOn();
in = minim.getLineIn(Minim.STEREO, 1024);
fft = new FFT(in.bufferSize(), in.sampleRate());
background(255);
stroke(0);

line(50, height/2, width-50, height/2);
int i = 1;

frameRate(60);

}

stop():

void stop() {

in.close();
minim.stop();
super.stop();

}

e, por fim, o método draw():

void draw() {

background(255);
drawLines();
fft.forward(in.mix);
float maiorFreq = 0;
float maior = 0;
int index = 0;

for (int i = 0; i maior) {
maior = maiorFreq;
index = i;
}

}

fill(255, 0, 0);
drawCircle(si.checkNotas(fft.indexToFreq(index)));

}

O resultado:

Brincando de 3D com imagens

Ok… vamos ao post!

A ideia aqui é fazer meio que um ‘efeito 3D’ com algumas imagens em sequencia. O que procurei demonstrar aqui é um daqueles ‘efeitos de Matrix’, quando o personagem principal (Trinity, segundo a cena que me lembro) pára no ar e a câmera gira em torno dela. Aqui, temos nossa ‘Trinity’ e o Tux (conhecido de vocês já, não? xD). Bom… o Tux não é o Tux… aqui ele é o Ryu (Apesar de um amigo meu dizer que tá mais pra Menudo. xD). Mas ok. Mantive a nossa ‘Trinity’ como centro da imagem e desloquei a câmera cerca de 5 centímetros para a direita. Ao todo, tirei 9 fotos seguindo esse processo. Aqui estão as imagens:

Pronto! Agora basta juntar tudo! Eu preferi usar o processing, para ter maior controle sobre as imagens. Mas qualquer programa para fazer gif’s funciona. Segue meu código:


PImage[] imgArray = new PImage[9];
int frame = 0;
int sentido = +1;
void setup() {

for (int i = 0; i 0) {
frame = frame + 1;
image(imgArray[frame], 0, 0);
if (frame == 8) {
sentido = -1;
}
}
else {
frame = frame - 1;
image(imgArray[frame], 0, 0);
if (frame == 0) {
sentido = 1;
}
}
}

Basicamente, criamos uma array de imagens e exibimos na ordem normal e inversa. Easy Mode, hein?

Como com o processing não pude demonstrar o resultado, criei um .gif com o programa
GiftedMotion. Ficou meio falho em alguns pontos mas eis o resultado:

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

Introdução ao Processing

Processing é uma linguagem de programação voltada principalmente para o multimidia. Você pode fazer o download em. No meu caso, fiz o download da versão para linux, mas está disponível para Linux, Mac OS e Windows. Vem em um pacote .tgz. Basta extraí-lo e executar o script ‘processing’ (pode ser necessário dar permissões de execução ao arquivo) e… pronto. Se tudo correu bem até agora, você verá o ambiente de desenvolvimento do Processing. Uma dica importante é a do menu ‘Help’, em que você encontra o item ‘Reference’, que é a documentação offline do processing. Ok… mas vamos ao desenvolvimento:

Processing

Tela inicial do Processing

Acima temos a tela inicial do processing…
Na minha implementação, preferi fazer uso de constantes para melhor demonstrar:

//COR DE FUNDO DA JANELA
int BACKGROUND_COLOR = 255;
//ALTURA DA JANELA
int WINDOW_HEIGHT = 600;
//LARGURA DA JANELA
int WINDOW_WIDTH = 600;
//LINHA
int LINE_COLOR = 0;
int LINE_WEIGHT = 10;
//ELIPSE
int ELLIPSE_COLOR = 0;
int ELLIPSE_WEIGHT = 10;
//CÍRCULO
int CIRCLE_COLOR = 0;
int CIRCLE_WEIGHT = 10;

Então, criamos um novo método:

void setup() {
size (WINDOW_WIDTH, WINDOW_HEIGHT);
background(BACKGROUND_COLOR);
}

Nele, definimos o tamanho da janela (com o método size()) e a cor de fundo da janela (background()). Nesse caso, a cor é passada em RGB, variando de 0 (preto) a 255 (branco). Para mais detalhes, veja a documentação do processing clicando. O método setup() prepara a janela que conterá os elementos que serão definidos no próximo passo.

Execução do método setup()

O próximo método é o draw():

void draw() {
stroke(LINE_COLOR);
strokeWeight(LINE_WEIGHT);
line(0, 0, 70, 90);
stroke(CIRCLE_COLOR);
strokeWeight(CIRCLE_WEIGHT);
ellipse(200, 200, 100, 100);
stroke(ELLIPSE_COLOR);
strokeWeight(ELLIPSE_WEIGHT);
ellipse(500, 500, 100, 40);
}

A chamada de alguns métodos se repete. O método stroke() define uma cor para a forma desenhada. O método strokeWeight() define a grossura do contorno. Mas vamos às formas:

line() – desenha uma linha em que os dois primeiros parametros correspondem às coordenadas (x, y) do começo da linha e os dois últimos correspondem às coordenadas do fim da linha.

ellipse() – desenha uma elipse em que os dois primeiros parametros são a coordenadas do encontro do maior e do menor diametro da elipse. Os dois parametros seguintes correspondem à altura e à largura da elipse. No código acima, foram desenhadas duas elipses: uma que, de fato, é uma elipse e um círculo (que nada mais é do que uma elipse com as duas diagonais iguais). Segue o resultado final:

Resultado final do código

Resultado final do código