Skip navigation

câmaras: adquirir vídeo em tempo real por câmaras, delays e slitscans de vídeo, texturas, análise de blobs. também noções iniciantes de usarmos texto dentro do processing mais no fim.

 

getting started capture

para adquirir video em tempo-real nos computadores temos de ter uma câmara instalada, drivers para comunicar com o quicktime, e depois o processing pede ao digitalizador do quicktime para enviar as imagens que vão chegando à câmara.

o sketch que vem como exemplo, gettingstartedcapure, só vai funcionar se tivermos as drivers correctamente instaladas. nos pc’s precisamos também da versão 101 do WinVDIG, e nos mac’s, para aceder a câmaras usb, convém instalar o macam. se virem erros do género cant find vdig, tem a ver com esta layer de software não estar bem instalada.

para que a captura de video se proceda, linkamos a biblioteca de video do processing, que é responsável pela leitura e gravação de filmes quicktime, bem como aceder a digitalizadores, câmaras. isso é feito com a linha import processing.video.*; logo a seguir podemos declarar objectos do tipo Capture, e construí-los na função setup.

vimos como listar as câmaras todas disponíveis, e como construir determinada câmara em função dos dispositivos disponíveis. depois de bem construída a câmara na variável do tipo Capture, podemos configurá-la através da função settings() que abre a janela de configuração quicktime que permite configurar todas as opções configuráveis dessa câmara específica. 

tendo a câmara sido iniciada, podemos agora pedir-lhe as frames através da função cam.read(); apenas se a câmara estiver disponível para as emitir (cam.available();). tal como nos Movies, a variável cam representa uma PImage, e podemos representá-la no sketch com a função image(PImage, locx, locy, width, height);

gettingstarted_capture

 

video delays

após este primeiro passo, onde aprendemos a adquirir e representar imagens da câmara, fomos construir um delay de video, inserindo uma array de PImages no sketch da câmara, com determinado tamanho, e criar depois duas variáveis inteiras, onde uma representa a posição de escrita na array da imagem que entra, e outra representa a posição de leitura dessa array, a uma distância variável da posição que escreve, mantendo um atraso entre a imagem de tempo-real e a imagem que é apresentada na janela da direita. as variáveis que usamos:

import processing.video.*;

PImage frames[];

Capture cam;

int time = 60*30;//1min@30pfs frames

int timehead, timeoffset, timeread;

 

a função setup constrói a array de PImages e a câmara:

  frames = new PImage[time];

  for(int i=0;i<frames.length;i++){

    frames[i] = new PImage(width/2,height); 

  }

  cam = new Capture(this,width/2,height,30);

 

e na função draw escrevemos e lemos dessa array posições distintas. o primeiro passo é ler a imagem para a variável cam, depois copiá-la para a posição de escrita na array, depois calcular a posição de leitura face a determinado offset, e finalmente ler as imagens actuais e atrasadas do timehead e do timeread respectivamente:

 

  if(cam.available()){

    cam.read(); 

    timehead = (timehead + 1) % frames.length;

    frames[timehead].copy(cam,0,0,320,240,0,0,320,240);

    timeoffset = (int)map(mouseX,0,width,time,0);

    timeread = (timehead + timeoffset) % frames.length;

 

    image(frames[timehead],0,0,320,240);

    image(frames[timeread],320,0,320,240);

  }

 

delay-00140

delay-002011

os sketchs de delays posteriores jogam com estas noções, inserindo mais variáveis de leitura em tempos distintos e representando as imagens de outros tempos no tempo actual, primeiro com 4 ecrans, depois 6 ecrans.

delay2-00888

delay2-00833
delay2-06490

delay2a-00602

 

delay2a-01050

 

slitscan 

o slitscan é a par do delay outra técnica muito usada na video arte, onde samplamos apenas uma tira da imagem de origem e a vamos acumulando num movimento horizontal ou vertical numa única frame de dimensões superiores, acumulando momentos temporais de tempos distintos na imagem.

para este sketch necessitamos de uma PImage que será a imagem de fundo, para onde vamos acumular as tiras da imagem video em tempo real, uma posição x que vamos variar para onde copiamos a posição videoloc da câmara em real-time.

 

Capture video;

PImage frame;

int px;

int videoloc;

definimos uma tela maior que o normal para acumularmos mais tirinhas de video, e construímos a câmara e a PImage e finalmente, inicializamos a videoloc a metade da largura da imagem do video. na função draw vamos copiar a tira para a posição px, usando a função copy que já usámos anteriormente no delay de video.

 // img.copy(srcImg, sx, sy, swidth,  sheight, dx, dy, dwidth, dheight);

   frame.copy(video,   videoloc, 0, 1, height, 

                                       px, 0, 1, height);

esta linha define a cópia de uma coluna (largura de 1 pixel) da posição central do video para uma posição x variável, que logo a seguir actualizamos com:

      px = px + 1;

      if(px >= width)

        px = 0;

 

e finalmente desenhamos o slitscan:

     image(frame,0,0,width,height);   

 

slit2-07897

slit2-10508

a seguir introduzimos velocidade variável no slitscan, onde fomos introduzir uma variável de speed que é controlada pelo rato, e as funções de slit agora não copiam uma coluna de 1 pixel, mas copiam colunas com dimensões variáveis, dependendo da velocidade:

     frame.copy(video,   videoloc, 0, ceil(speed), height, 

                        (int)px, 0, ceil(speed), height);

      px = px + speed;

      if(px >= width)

        px = 0;

 

slit2a-11390

finalmente, trocámos os eixos e fomos fazer um slitscsan ao longo do eixo dos y’s em vez do eixo x, mais uma vez, as funções relevantes estão na maneira de copiarmos os pixeis da frame de origem para a frame de destino:

    frame.copy(video,   0, videoloc, width,  ceil(speed), 

                        0, (int)py, width, ceil(speed));

      py = py + speed;

      if(py >= height)

        py = 0;

 

slit2av-04631

 

timerain (texturas opengl + delays) 

 

nos sketches de timerain fomos rever algumas noções fundamentais de 3d e aplicar imagens em tempo real para um buffer e colocar n planos em cena, cada um com uma textura desse buffer das imagens capturadas em tempo real.

o mecanismo é muito semelhante ao delay que já vimos anteriormente, criamos um buffer de imagens que vamos preenchendo com imagens provenientes da câmara. depois inserimos uma classe de planos, que, na função draw principal, após termos actualizado o buffer de imagens vindas da câmara, vamos mandar renderizar.

a classe dos planos será constituída por posições x, y e z, velocidade, que em vez de ser aplicada ao eixo dos z, como nos sketches starfield da sessão 6, vai ser aplicada no eixo dos y’s, e se a altura de cada plano for superior a 700, ou seja, se já tiver caído bastante, vamos mandar re-iniciar as variáveis. vai ter ainda variáveis para rotações nos eixos x e y, dimensões x e y, e finalmente, uma PImage que apontará para uma imagem do buffer que vai sendo preenchido.

quando o plano é construído coloca variáveis aleatórias nos eixos x e z e no de y também, mas no de y é mais para cima, para que os planos vão caíndo continuamente, e depois surjam novamente em cima. coloca ainda velocidades, dimensões e uma imagem do buffer de imagens dentro da img que lhe corresponde.

a função que trata de tudo é a render(), que é chamada a cada draw, para alterar as posições dos planos, e desenhar a textura correspondente que lhe está associada.

  void render(){

    //update

    y += s;

    if(y>700){

      reset();

    }

    //draw

    pushMatrix();

    translate(x,y,z);

    rotateX(rx);

    rotateY(ry);

    beginShape();

    texture(img);

    vertex(-dimx,-dimy,0,0);

    vertex( dimx,-dimy,img.width,0);

    vertex( dimx, dimy,img.width,img.height);

    vertex(-dimx, dimy,0,img.height);

    endShape(CLOSE);

    popMatrix();     

  }

 

e é aqui que começamos a usar texturas em formas gráficas arbitrárias. dentro do beginShape() e endShape(), chamamos texture(img), e logo a seguir, os comandos de vertex, em vez de terem apenas coordenadas geométricas, têem também coordenadas uv, que correspondem as coordenadas das texturas que utilizamos. notem bem a correspondência que existe entre o zero inicial em torno do qual são definidos os pontos vertex, e a correspondência entre as coordenadas espaciais com as coordenadas uv. o canto superior esquerdo do quadrado tem coordenadas uv (0,0) e o canto inferior direito tem coordenadas uv (largura, altura).

timerain-01016

no primeiro timerain usámos imagens de buffer e captura com dimensões 320×240 pixeis, e 50 planos texturados. isto não é muito eficiente para o opengl, vimos como a actividade deste sketch andava em volta de 70-80%. e não é muito eficiente porque o opengl a trabalhar com texturas usa-as com dimensões de base 2, ie, 64, 128, 256, 512, etc. o que acontece é que sempre que usamos a linha texture(img); , transferimos uma PImage para uma textura, e o opengl tem de assegurar que as dimensões dela são de base 2, logo, vai redimensionar cada textura para a próxima power of two size, que é uma operação dispendiosa e que não é muito aconselhável fazê-la a cada frame 50 ou mais vezes. num segundo sketch de timerain alterei as dimensões da captura de câmara e do buffer de imagens para tamanhos nativos de base 2, 128×128 pixeis, e o resultado, sem significante perda de qualidade nas imagens finais, com o triplo do número de planos (150), é que passamos a activity do sketch para 30%. uma optimização brutal, pois as texturas são dos comandos gráficos que mais memória gastam quando mal usados. ainda usamos cerca de 1000 planos para repor os níveis de cpu que tínhamos anteriormente. além da optimização a nível de cpu, optimizámos também a fluidez do próprio programa, pois o opengl já não perde tempo em sítios desnecessários e o sketch agora corre como faca quente em manteiga.

timerain2-01237

timerain2-00829

timerain2del-00332

 

 

análise de blobs na imagem (centróides)

outro ponto essencial do uso de câmaras é utilizarmos as imagens que elas devolvem como instrumentos interactivos, onde determinados movimentos ou posições face à câmara despoletam acções. este campo é muito vasto, está em ebulição onde há muita gente a desenvolver novos mecanismos de análise, e há várias técnicas distintas, dependendo do resultado que se pretende, há umas melhores que outras. de destacar o uso pioneiro de câmaras pelo Myron Krueger, a par da invenção do rato, onde ele construiu vários trabalhos, por exemplo, o videoplace, onde é o corpo dele e de outras pessoas que activam espaços interactivos, no espaço local, ou através de redes, em espaços distintos.

um exemplo relativamente simples que analisámos hoje foi  identificar várias blobs na mesma imagem, ie, regiões contíguas que se destacam do fundo com mais de x pixeis, e que acarretam uma posição xy face às dimensões de análise da imagem. 

flobhello-00339

para analisar blobs, é necessário ter condições luminosas relativamente estáveis e afinar os tresholds a partir dos quais vamos identificar se um pixel se destaca do fundo ou não. a técnica principal consiste em copiar os pixeis actuais para a imagem de fundo, e confrontar em tempo real cada imagem que chega com essa imagem de fundo. se os valores se encontrarem a determinada distância do valor de fundo superior ao treshold, então identificamos esse pixel como diferente. se encontrarmos uma região com muitos pixeis diferentes contíguos, ie, todos brancos seguidos uns aos outros, então essa região é uma blob, e tem um centro e dimensões que podemos usar como alvo de centros de sistemas de partículas e por aí. 

flobhello-00422

flobhello-00576

para acelarar as coisas fomos usar uma biblioteca para o processing que eu desenvolvi que se chama flob e que detecta blobs na imagem e devolve os centroides e dimensões. estivemos a tentar calibrar o espaço de análise nas dificeis condições da aula, e depois de calibrado, usámos um skecth onde cada blob ia desenhando uma ellipse para a tela. o tamanho da elipse é controlado com as dimensões da blob analisada, e fica uma espécie de máquina de desenhar multi-toque a partir de câmaras. ainda vamos analisar alguns exemplos mais elaborados de análise de imagem, por hoje ficamos pela introdução e pelo exemplo simples. 

videodraw-000463

videodraw-006320

videodraw-007737

videodraw-008248

 

 

texto em processing (alice in wonderland)

texto em processing é muito fácil de se usar. declaramos uma PFont, construimos a fonte ao carregar uma fonte realizada com o createFont das tools do processing, usamos o comando textFont(font,tamanho);, e estamos prontos a representar Strings na tela do processing, com o comando text, que recebe uma String e duas posições, uma x e outra y:

PFont font;

void setup(){

  size (420,150);

  font = createFont(“Arial”,16);

//  font = loadFont(“ArialMT-47.vlw”);

  textFont(font,47);

  text(“Hello World!”, width/5,height/2);

}

com base neste mecanismo, fomos ler um livro inteiro para a memória do computador (alice no país das maravilhas), separar cada uma das linhas de texto, depois dentro de cada linha as palavras que as compõem, e finalmente transferimos as palavras para um tipo de dados que criámos dentro da class Wonderland, que associa cada palavra a uma posição x, y na tela.

as funções base que permitem isto são a loadStrings que lê um ficheiro de texto para uma array de Strings, e a splitTokens que separa cada palavra da linha de texto. com isto já conseguimos passar para uma array de Strings cada palavra individualmente. 

depois, associamos a cada uma destas palavras uma posição xy:

    for(int i = 0; i < texto.length; i++) {

        float xpos = i * 200.0f;

        float ypos = random(-((i)%500),(i)%height) + random(-10,10);

        palavras[i] = new word(texto[i], xpos, ypos);            

    }

finalmente, a wonderland vai apenas desenhar palavras que sejam activas, e para tal, chamamos uma função logic que de x em x tempo verifica se as palavras estão a uma distância x da alice.

a classe da alice é um bitmap com uma posição xy que podemos manipular com as teclas e que navega ao longo de todo o texto do livro. finalmente, colocamos a câmara do processing a ir atrás da posição da alice com um filtro lowpass para suavizar as transições de valores, e ficamos com o esboço de um mini-jogo à base de teclas e de texto.

 

// alice in wonderland, andré sier, o som do pensamento, 2009

 

import processing.opengl.*;

 

Wonderland land; 

Alice alice;

 

PFont font;

float camx;

 

void setup(){

  size(1000,500,OPENGL); 

  frameRate(30);

 

  font = loadFont(“ArialMT-47.vlw”);

  textFont(font,47);

 

  alice = new Alice();

  land = new Wonderland(); 

}

 

void draw(){

  background(255);

 

  float f = 0.1;

  camx = camx*(1.-f) + alice.x*f; //ease

  camera(camx,0,1000,camx,0,-100,0,1,0);

 

  land.draw();

  alice.move();

  alice.draw();

}

 

void keyReleased(){

  if(keyCode==UP||key==’w’)

    alice.up=false;

  if(keyCode==LEFT||key==’a’)

    alice.left=false;

  if(keyCode==RIGHT||key==’d’)

    alice.right=false;

}

alice-in-wonderland-00130

 

alice-in-wonderland-01172

alice-in-wonderland-03539

Anúncios

One Trackback/Pingback

  1. By sessão 17 « O Som do Pensamento on 27 Maio 2009 at 11:55 am

    […] de blobs, frame differencing, detecção de outras features como caras, mãos, … (sessão 7, […]

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: