Exibindo dados do kinect dentro do XNA usando a biblioteca OpenNI Este artigo demonstra como usar a biblioteca OpenNI para exibir informações capturadas pelo kinect na tela de um jogo em XNA. Recentemente adquiri o sensor do Kinect, ja tinha ssistido muitos vídeos sobre projetos bem interessantes que estava sendo desenvolvidos com o Kinect, alguns bem próximos do filme "Minority Report". Enfim, achei interessante e comecei estudar um pouco mais sobre o assunto, me deparei com duas bibliotecas bem interessantes: a OpenNI criada pela PrimeSense, empresa que participou junto com a Microsoft do projeto do Kinect(o principal chip foi criado por eles) e a biblioteca NUIPlataform da CodeLaboratories. Ambas possuem suporte ao .Net, então estou testando as duas mas com foco no OpenNI. Depois de brincar um pouco com o sensor, rodando diversos exemplos que foram criado ao redor do mundo, resolvi rodar o código dentro do XNA para ver como ficava, o resultado são dois projetos: o XnaSimpleRead e o XnaSimpleViewer. O primerio apenas exibe na tela um texto com as informações que estão sendo capturadaspelo sensor, já o segundo exibe a imagem capturada pelo sensor infravermelho Primeiramente vou mostrar o exemplo mais simples de leitura dos dados,fiquem a vontade para indicar melhorias, segue abaixo o artigo:
=========================================================================== Este tutorial mostra os primeiros passos na configuração da biblioteca do OpenNI dentro do XNA(Plataforma de desenvolvimento de games da Microsoft) e a leitura dos dados do Kinect. Exemplo 1: Leitura dos dados do KinectEste exemplo demonstra como fazer a leitura das informações proveniente do sensor do Kinect e exibir na tela do XNA. Considerando que você ja possui o XNA Game Studio 4.0 instalado e já sabe como criar um projeto do XNA WindowsGame?(4.0), vamos ao código: 1 Passo - Fazer referência a dll do OpenNI
No "Solution Explorer" do seu projeto no Visual Studio, clique com o botão direito em "References" > "Add Reference". Na tela que abrir clique na aba "Browse" e procure pela dll do OpenNI.net (OpenNI.net.dll) e clique em "Ok". Veja imagem abaixo: 
2 Passo - Declarar o namespace
Para poder acessar os métodos do OpenNI.net declare o using xn. Veja: using Microsoft.Xna.Framework.Media; using xn; using System.Text; 3 Passo - Crie uma classe que representará as linhas na tela public class LinhaTela { private string texto; public string Texto { get { return texto; } set { texto = value; } }
private Vector2 posicao; public Vector2 Posicao { get { return posicao; } set { posicao = value; } }
public LinhaTela(string _texto,Vector2 _posicao) { this.texto = _texto; this.Posicao = _posicao; } }
4 Passo - Declara as variáveis SpriteFont fonte; //fonte para poder escrever na tela Context context; //variáveis de controle do OpenNI DepthGenerator depth; MapOutputMode mapMode; DepthMetaData depthMD; List<LinhaTela> linhas; //lista de linhas que serão exibidas float posY; string result; 5 Passo - Escrever o código de inicialização dos objetos protected override void Initialize() { // TODO: Add your initialization logic here //Kinect string SAMPLE_XML_FILE = "C:/SamplesConfig.xml"; context = new Context(SAMPLE_XML_FILE);
depth = context.FindExistingNode(NodeType.Depth) as DepthGenerator; if (depth == null) { Console.WriteLine("Sample must have a depth generator!"); return; }
posY = 125; //Posição inicial na tela onde a primeira linha com a leitura das informações irá aparecer.
mapMode = depth.GetMapOutputMode(); depthMD = new DepthMetaData();
linhas = new List<LinhaTela>(); //Criamos a lista base.Initialize(); } Inicialmente carregamos o arquivo de configuração do OpenNI o SamplesConfig?.xml. Instanciamos o objeto context, passando como parâmetro o xml de configuração. 6 Passo - Carregar a fonte no método LoadContent? protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here fonte = Content.Load<SpriteFont>(@"Arial"); } Obs: este arquivo esta presente na pasta junto com o projeto do XNA na área de downloads. 7 Passo - Adicionar o código abaixo no método Update O código abaixo captura as informações provenientes do sensor do Kinect(depth.GetMetaData?) e preenche a lista com as informações. // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();
// TODO: Add your update logic here context.WaitNoneUpdateAll(); depth.GetMetaData(depthMD);
if (linhas.Count >= 6) { linhas.RemoveAt(0); }
StringBuilder linhaDados = new StringBuilder(); linhaDados.Append("Frame "); linhaDados.Append(depthMD.FrameID.ToString()); linhaDados.Append(" - Middle point is: "); linhaDados.Append(depthMD[(int)mapMode.nXRes / 2, (int)mapMode.nYRes / 2]);
linhas.Add(new LinhaTela(linhaDados.ToString(),new Vector2(10,posY)));
foreach(LinhaTela lin in linhas) { lin.Posicao = new Vector2(lin.Posicao.X, lin.Posicao.Y - 20); } base.Update(gameTime); 8 Passo - Escrever na tela a lista com as informações capturadas. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here spriteBatch.Begin(); for (int i = 0; i < linhas.Count;i++) { spriteBatch.DrawString(fonte, "[" + i + "]: " + linhas[i].Texto,linhas[i].Posicao, Color.White); //escrevendo na tela } spriteBatch.End(); base.Draw(gameTime); } Observem que pouquíssimas alterações foram necessárias para fazer o OpenNI funcionar no XNA, já era de se esperar ja que o código é em c#. A única coisa que precisou ser criada foi a lista com os dados para serem exibidos.
Uma alteração que foi feita em relação ao sample em C# que vem com o código do OpenNI foi a chamada do método context.WaitNoneUpdateAll?() que antes era context.WaitOneUpdateAll?(depth). O motivo é que pela documentação do OpenNI, este último método espera um determinado nó receber novos dados para atualizar todos os outros, e isto estava causando uma lentidão quando rodei o projeto em XNA. Leiam aqui um trecho da documentação: Documentação do OpenNI explicando os métodos. "Reading Data Generators usually constantly receive new data. However, the application might still use the old (previous) data. For this reason, any generator should keep the new data internally until explicitly asked to update its data to the new one. This request is called Update Data. OpenNI gives the application the option to wait for new data to be available, and then update it using xnWaitAndUpdateData() (xn::Generator::WaitAndUpdateData?()). In some cases, the application holds more than one node, and it usually wants all the nodes to be updated. OpenNI supplies several functions to do so, depending on what the application wants to wait for before updating takes place: xnWaitAnyUpdateAll() (xn::Context::WaitAnyUpdateAll?()) - Waits for any node to have new data. Once new data is available from any node, all nodes are updated. xnWaitOneUpdateAll() (xn::Context::WaitOneUpdateAll?()) - Waits for a specific node to have new data. Once new data is available from this node, all nodes are updated. This is especially useful when having several nodes producing data, but only one determines the progress of the application. xnWaitNoneUpdateAll() (xn::Context::WaitNoneUpdateAll?()) - Does not wait for anything. All nodes are updated. xnWaitAndUpdateAll() (xn::Context::WaitAndUpdateAll?()) - Waits for all nodes to have new data available, and then updates them. Unless needing to update only a specific node, it is highly advised to use one of the UpdateAll? functions. In addition for updating all the nodes they have some more benefits: If nodes are dependend in one another, it promises the needed node will be updated before the needing node. If playing a recording, it reads data from the recording until the condition is met. If a recorder exists, it records the data from all nodes added to it (without the need to call xnRecord())." Dificuldades Encontradas Problemas para rodar o projeto do XNA 4.0 no meu notebook Um dos problemas que tive for quando tentei rodar meu projeto do XnaSimpleRead? pela primeira vez no notebook, me deparei com o seguinte erro: "No suitable graphics card found Could not find a Direct3D device that supports the XNA Framework HiDef? profile.." para contornar este problema cliquei com o botão direito do mouse no meu projeto no "Solution Explorer" > Properties(Propriedades) > XNA Game Studio. Na seção Game Profile marquei a opção: "Use Reach to acess..." ao invés de "Use HiDef?". |