Update 05/06/2012 –

A nova versão do Kinect SDK foi lançada a pouco tempo e deixou todo esse conteúdo desatualizado. O novo conteúdo pode ser encontrado aqui: http://www.100loop.com/destaque/kinect-sdk-1-5-parte-1-camera-rgb/

 

Fala filhotes! Como vocês devem saber a Microsoft lançou em Junho a versão beta do Kinect SDK, um conjunto de ferramentas necessárias para que você possa criar aplicativos que utilizam o Kinect. E agora no final de Julho eles lançaram uma atualização deixando a SDK ainda mais estável. Por isso já passou da hora de termos um artigo sobre isso, então a partir de hoje nós vamos começar a utilizar essa SDK para algumas coisas.

Esse primeiro tutorial será bem básico sobre como criar sua primeira aplicação usando o Kinect SDK, então vamos aos fundamentos primeiro:

Sobre o SDK

O SDK o Kinect é bastante leve (+- 20MB ) e vem com drivers, APIs, documentação e até um “joguinho” de exemplo. Os requerimentos para rodar o SDK são: Directx 9.0C, Framework 4.0 e Windows 7 (x86 e x64) e de acordo com a documentação, não é possível executar uma aplicação dentro de maquinas virtuais. Com o SDK você pode criar aplicações usando C#, VB.NET ou C++. Como sempre, você pode utilizar o Visual Studio 2010 Express.
Dito isso, só falta baixar e instalar! Basta entrar no site oficial: http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/

Capacidades do Kinect

Kinect é um aparelho com câmeras e microfones e pode ser conectado ao PC usando um pequeno adaptador com saída USB. E graças ao SDK você poderá utilizar os seguintes recursos:

  • Mapeamento dos esqueletos de uma ou duas pessoas que estão na área de visualização do Kinect
  • Acesso a câmera padrão além da câmera que retorna a posição e a distancia (XYZ) de um objeto
  • Acesso aos recursos do microfone como supressão do ruído acústico e cancelamento de ecos.

Nosso objetivo

Hoje nós vamos aprender como usar o mapeamento de esqueletos para mostrar todas as juntas do usuário na tela. Então vamos explicar o básico sobre os esqueletos e explicar o passo a passo do nosso tutorial:

Esqueleto

O Kinect tem a capacidade de detectar os esqueletos automaticamente, basta ficarem com o corpo todo na frente do Kinect.

Cada esqueleto conta com 20 juntas:

image

Classes do Esqueleto

Quando um esqueleto é detectado, ele chama o método SkeletonFrameReadyArgs enviando um SkeletonFrame como parâmetro, e dentro dele temos uma coleção de seis esqueletos (SkeletonData), onde apenas dois deles vão estar mapeados.

image

Retirado da documentação oficial

Em cada SkeletonData há a coleção de juntas (Joint).
Cada Joint conta com: o seu ID (um enum com o nome da junta), um vector como position aonde :

  • O X e o Y são um numero entre –1 e 1
  • O Z é a distancia entre a junta e o Kinect em metros
  • E o W que de acordo com a documentação é a distancia em metros do Kinect para o chão, apesar de nem sempre o chão ser visível.

E  também contêm um TrackingState que pode ser:

  • Tracked – Foi mapeado normalmente.
  • Inferred – Acontece algumas vezes, significa que o Kinect não tem certeza que a junta esta lá. Por exemplo, quando você põe o cotovelo na frente do seu corpo, o Kinect não consegue ver o seu cotovelo, então ele tem um algoritmo que pega a posição do seu cotovelo baseado na posição da sua mão e do seu ombro.
  • Not Tracked – Parece que é bem raro isso acontecer. Mas é bom checar mesmo assim.

Posição do Esqueleto

A posição do esqueleto é representada em metros e usando um sistema de coordenadas destra, ou seja o eixo Y positivo fica para cima e o eixo positivo X fica para a esquerda.

image

Retirado da documentação oficial

Então precisamos sempre fazer uma conversão para podermos mostrar o esqueleto na tela. Apesar de já existir varias bibliotecas que fazem essa conversão automaticamente, nesse post iremos fazer do “jeito VIKING” ou seja, convertendo “manualmente”.

 

Criando e preparando o projeto

Depois de instalar o SDK vamos criar o nosso projeto no Visual Studio. Nesse exemplo vou criar um WPF (Windows Presentation Forms) para ficar mais simples entender o funcionamento, mas se você quiser, pode usar qualquer outro projeto. Depois de criar o projeto ( File >New > Project > WPFApplication ) basta adicionar a referência a biblioteca do Kinect e podemos começar!

O primeiro passo é sempre adicionar a referencia a biblioteca que nós vamos usar:

image

Botão direito em References >> Add Reference >> Microsoft.Research.Kinect

Agora em MainWindow vamos adicionar um Canvas:

<Window x:Class="WPFKinectTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="600" Width="800" Loaded="Window_Loaded" Closed="Window_Closed">
    <Grid>
        <Canvas Name="cnArea">

        </Canvas>
    </Grid>
</Window>

E no CodeBehind vamos usar a Microsoft.Research.Kinect:

using Microsoft.Research.Kinect.Nui;

e criar uma variável do tipo Runtime:

 //Kinect Runtime
 Runtime kinectRuntime = new Runtime();

É essa variável que irá conversar diretamente com o Kinect. Agora temos que inicializa-la, então no evento Loaded do Window :

private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Inicializa o runtime do Kinect passando que eu quero somente o tracking dos esqueletos
            // Se quiser usar mais de uma opção (camera normal por exemplo) basta usar o "|" entre cada uma
            kinectRuntime.Initialize(RuntimeOptions.UseSkeletalTracking);

 

E agora adicionamos um EventHandler passando o método que será executado sempre que detectar um esqueleto.

// Adicionamos o evento que irá executar sempre que detectar um esqueleto
kinectRuntime.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);

 

E o nosso método:

        /// <summary>
        /// Toda hora que detectar um esqueleto ele passa aqui
        /// </summary>
        void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            // Pega todos os 6 esqueletos
            SkeletonFrame allSkeletons = e.SkeletonFrame;            

            // Pegamos apenas o primeiro com o status de "Tracked"
            SkeletonData skeleton = (from s in allSkeletons.Skeletons
                                     where s.TrackingState == SkeletonTrackingState.Tracked
                                     select s).FirstOrDefault();                       

            // Limpa todos os items do canvas
            cnArea.Children.Clear();

            // Cria um loop com todas as juntas do esqueleto
            foreach (Joint item in skeleton.Joints)
            {
                    // Cria uma
                    Ellipse objEllipse = new Ellipse();
                    // Adiciona uma cor
                    objEllipse.Fill = new SolidColorBrush(Color.FromArgb(150, 55, 255, 0));
                    // Tamanho
                    objEllipse.Width = 20;
                    objEllipse.Height = 20;
                    // Adiciona ao Canvas
                    cnArea.Children.Add(objEllipse);
                    // Converte a posição da junta e altera a posição da ellipse
                    setDisplayPosition(ref objEllipse, item);
            }
        }

E o SetDisplayPosition:


         /// <summary>
        /// Converte a posição do Esqueleto e altera a posição da ellipse
        /// </summary>
        public void setDisplayPosition(ref Ellipse ellipse, Joint joint)
        {
            int colourX, colourY;

            float depthX, depthY;
            // Converte a posição de Skeleton para DepthImage que gera dois floats entre 0.0 e 1.0
            this.kinectRuntime.SkeletonEngine.SkeletonToDepthImage(joint.Position, out depthX, out depthY);

            // Converte para 320 / 240 que é o que o GetColorPixelCoordinatesFromDepthPixel suporta
            depthX = Math.Max(0, Math.Min(depthX * 320, 320));
            depthY = Math.Max(0, Math.Min(depthY * 240, 240));

            ImageViewArea imageView = new ImageViewArea();

            // Converte os valores de DepthPixel para ColorPixel, que agora sim é o que precisamos
            this.kinectRuntime.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480,
                imageView, (int)depthX, (int)depthY, 0, out colourX, out colourY);

            // Agora você ja tem um valor mais exato das coordenadas
            Canvas.SetLeft(ellipse, colourX);
            Canvas.SetTop(ellipse, colourY);           

            // Você tem a opção de escalar ela para o tamanho do seu esqueleto também
           // Canvas.SetLeft(ellipse, cnArea.Width * colourX / 640 - 20);
            // Canvas.SetTop(ellipse, cnArea.Height * colourY / 480 - 20);           

        }

E no final, vamos eliminar os recursos:

 /// <summary>
 ///  Executa quando fecha a janela
 /// </summary>
 private void Window_Closed(object sender, EventArgs e)
    {
      // Desliga o Kinect
      kinectRuntime.Uninitialize();
     }

Resultado

 

Agora executamos! O resultado deve ser algo assim:

kinect_teste

Com isso desenhamos uma bola em cada junta que o Kinect mapeia. Se quiser baixar o código fonte basta clicar aqui.

Bom filhotes, sei que não teve muita coisa hoje, mas o objetivo foi isso mesmo, deixar as coisas simples.

Qualquer coisa estamos a disposição! Sugestões e criticas são sempre bem vindas!

E vamos que vamos, espero começar a escrever bastante sobre o Kinect ainda. Ate mais!

Referências

http://blogs.microsoft.co.il/blogs/shair/archive/2011/06/17/kinect-getting-started-become-the-incredible-hulk.aspx

http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/docs/ProgrammingGuide_KinectSDK.pdf