funmachine

Kinect NITE & Flash

Deixe um comentário

A semana passada recebi um kinect e tive finalmente a oportunidade de testar esta tecnologia de que tanto se escreve! Para mim o Kinect é daquelas tecnologias que revolucionam a forma que interagimos com as máquinas. Um pouco como aconteceu com os dispositivos touch e multitouch, o kinect traz uma nova vaga de oportunidades na área do design de interacção, onde a criatividade de quem cria interfaces é o limite para trazer experiências cada vez mais imersivas para o utilizador. A minha experiência com o kinect passou por desenvolver uma simples demonstração de como podemos usar NUIs para interagir com aplicação de conteúdos, particularmente aplicações web.

Segue o vídeo para verem já o resultado:

Como podem ver a aplicação é muito simples, basicamente uma galeria de imagens e a interacção é fazer um “swipe right” para passar à próxima imagem e um “swipe left” para mostrar a imagem anterior. A galeria é implementada em as2.

Para conseguir interligar o kinect com o flash precisei de desenvolver 3 aplicações, um cliente flash, que não é mais do que uma galeria de imagens em flash; uma aplicação kinect que detecta movimentos “swipe” (arrastar a mão num sentido qualquer e parar) e um servidor sockets, que recebe os eventos “swipe” do kinect e passa-os para a aplicação flash. Conceptualmente é fácil, a dificuldade é apenas ter que lidar com diversas tecnologias (flash, c++, javascript) e colocar estes módulos todos uns a falar com os outros.

A galeria Flash

Vamos começar pelo mais simples, a galeria flash. Eu não sou com certeza a melhor pessoa para desenvolver uma galeria de imagens em flash toda xpto, por isso começei por googlar e descobrir a Polaroid Gallery, que me interessou porque além do aspecto interessante, é opensource:

Polaroid Gallery

Polaroid Gallery

O único problema que encontrei com esta aplicação é que não tem documentação, e toda a nomeação de variáveis, funções e objectos foi feita Norueguês, o que não é o meu forte 😉

Para este cliente apenas precisamos adicinar uma ligação socket ao servidor (este post explica melhor a ligação socket em flash), e ao receber dados do servidor (o gesto do kinect) inferir se é um swipe left ou swipe right e de seguida invocar a função da galeria flash que passa para a imagem anterior e próxima respectivamente.

Para fazer debug, utilizei a caixa de ajuda da galeria, e faço prints para a text area da ajuda;).Sei que não é boa prática, mas é rápido e eficaz neste caso. O código é o seguinte:

mySocket = new XMLSocket();
mySocket.onConnect = function(success) {
	if (success) {
		help.msgArea.htmlText += "<b>Server connection established!</b>";
	} else {
		help.msgArea.htmlText += "<b>Server connection failed!</b>";
	}
};
mySocket.onClose = function() {
	help.msgArea.htmlText += "<b>Server connection lost</b>";
};
XMLSocket.prototype.onData = function(msg) {
	help.msgArea.htmlText += msg;
	if(msg == "0") // swipe left
		visNeste(); // imagem anterior, em Norueguês
	else
	if(msg == "1") // swipe right
		visForrige(); // próxima imagem
};

mySocket.connect("127.0.0.1", 8124);

O servidor de sockets (a cola;) )

O script que vai correr no servidor e criar as comunicações entre kinect e flash é desenvolvido em Node.js. Se derem uma olhadela neste post e neste vão ter uma noção maior de como funciona o Node.js, aqui, eu vou partir do princípio que já o sabem.

O código é muito simples, vamos criar uma servidor TCP no porto 8124 e receber ligações sockets; quando recebermos dados, simplesmente fazemos broadcast aos restantes clientes. Ou seja, quando recebermos mensagens do kinect, enviamos as mensagens para o cliente flash.
O código está bem documentado e devem perceber bem o que faz:

net = require('net');
var clients = [];

// Iniciar o servidor TCP
net.createServer(function (socket) {
  // Identificar o cliente
  socket.name = socket.remoteAddress + ":" + socket.remotePort
  // colocar o cliente na lista de clientes
  clients.push(socket);

  // Trata mensagem que chegam dos clientes
  socket.on('data', function (data) {
	broadcast(data, socket);
  });

  // Remove o cliente da lista quando se desconexa
  socket.on('end', function () {
    clients.splice(clients.indexOf(socket), 1);
  });

  // Difunde uma mensagem aos outros clientes
  function broadcast(message, sender) {
    clients.forEach(function (client) {
      // Não enviar a quem enviou a mensagem
      if (client === sender) return;
      	client.write(message);
    });
    // Log
    process.stdout.write(message)
  }

}).listen(8124);
// Mensagem para o terminal
console.log("Socket server running at port 8124\n");

Para correr este código basta ter o Node.js instalado e correr num terminal:

$ sudo node socket_broadcast.js

]
O servidor fica a correr no porto 8124 e está pronto para receber e distribuir mensagens de clientes.

A aplicação Kinect

Resta programar a aplicação kinect que vai receber eventos “swipe right” e “swipe left” e enviar para o servidor socket, que por sua vez envia ao cliente flash. Existem várias opções por aí para brincar com o kinect enquanto a microsoft não lança as drivers. Eu escolhi a framework da OpenNI junto com os drivers e API kinect da PrimeSense, pela única razão de eles serem os criadores do Kinect. No entanto, depois de fazer esta demo, pesquisei mais e parece-me que a OpenKinect tem mais documentação, exemplos e uma maior comunidade, além de vários wrappers para outras linguagens, como python, javascript e actionscript. Sim, eu podia ter usado directamente o wrapper actionscript, e deixava de ter que programar o servidor e a aplicação kinect C++, mas a minha ideia era mesmo explorar o processo e não colocar uma demo a funcionar “à força”.

Além disso, queria mesmo perceber como funciona a framework NITE  da PrimeSense, e é uma agradável surpresa! Fornecem, além da documentação da API que é muito muito útil, um documento da arquitectura da framework, que vale mesmo a pena ler para perceber como funciona aquele sistema. A documentação é fornecida junto com o código fonte. Para instalar a SDK openNI, NITE e drivers Kinect podem seguir este tutorial. Depois de instalar podem testar com as demos fornecidas. Uma explicação sobre cada uma das demos pode ser encontrada aqui.

Depois de ler esse documento, navegar no código dos exemplos e dar uma vista de olhos na API, facilmente se perceber como detectar facilmente uma série de gestos predefinidos (swipe, push, wave, etc). Além disso, e talvez o mais interessante, a framework permite acesso ao esqueleto do utilizador, e a partir daí, a imaginação é o limite para criar formas de interações, mas nesta demo ficamos pelos gestos predefinidos.

Para a nossa demo vamos usar um dos exemplos fornecidos com a NITE, o SimpleControl. A ideia é alteramos o código para:

  • Detectar gestos swipe.
  • Programar um cliente socket para enviar os gestos swipe para o servidor.

Para detectar swipe, muito genéricamente a ideia é a seguinte, iniciamos um objecto detector na sessão e associamos uma callback que vai ser invocada quando o objecto detectar um determinado gesto. Dentro da callback vamos simplesmente criar uma ligação ao servidor e enviar o gesto detectado. (swipe: left, right, down, top, etc…)
O código que declara a callback é o seguinte:

void XN_CALLBACK_TYPE GeneralSwipeCB(XnVDirection eDir, XnFloat fVelocity, XnFloat fAngle, void *pUserCxt)
{
	// Alocar memória para a string a enviar por socket.
	int length = strlen("1");
	char * data = (char*) malloc((length + 1) * sizeof(char));

	sprintf (data, "%d",eDir);
	printf("%s:%s","Swip", data);

    n = write(sockfd,data,length + 1);
    if (n < 0)
         printf("ERRO ao escrever na socket");

	free(data);
}

A seguir, na função main, vamos definir um objecto detector de gestos swipe, associar à callback e por fim associar à sessão:

        // Iniciar e registar o detector swipe
	XnVSwipeDetector swipeDetector;
	swipeDetector.RegisterSwipe(NULL, GeneralSwipeCB);
        ...
        // Associar o detector à sessão
        pSessionGenerator-&gt;AddListener(&amp;swipeDetector);

Por fim basta criar a ligação socket com o servidor, no início da função main. Para uma boa introdução a sockets em C, vejam este post.

    int portno;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    portno = 8124;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        printf("ERRO ao abrir a socket");
    server = gethostbyname("localhost");
    if (server == NULL) {
        printf("ERRO, host não encontrado\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr,server->h_length);
    serv_addr.sin_port = htons(portno);

    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
        printf("ERRO de conexão");

Voltem a compilar os exemplos e se não ocorrer erros temos o código kinect pronto a detectar eventos swipe e enviar para o servidor.
Agora podemos colocar tudo a correr e controlar a nossa galeria flash via kinect!!
Os passos são os seguintes:

– Correr o servidor sockets Node.js
– Abrir a galeria flash.
– Correr o exemplo kinect.

Anúncios

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