kinect

21
1. Introdução O Kinect é um aparelho desenvolvido para o videogame Xbox360 da microsoft. Este disponibiliza uma série de características: Sensor de movimentos (possibilitando rastrear e reconhecer movimentos do corpo todo); Rastreamento de esqueleto (reconhece e pode representar o esqueleto); Reconhecimento facial (Podendo a partir da leitura facial fazer o login do usuário); Reconhecimento de voz (microfones estrategicamente posicionados dentro do sensor). Sendo o único videogame de mercado em que é possível jogar sem o uso de controles, o kinect foi um grande sucesso de vendas. Curiosamente seu projeto inicialmente se chamava projeto Natal, em alusão a cidade de Natal no Brasil. A Microsoft explica o porque do nome da empresa: “ Um de nossos pesquisadores, Alex Kipman, é do Brasil e ele escolheu a cidade de Natal como um tributo a seu País. Além disso, ele sabia que Natal também significa nascer, em latim. Considerando o novo público que será atraído ao Xbox 360 pela novidade, o nome encaixou perfeitamente”. O Sensor Kinect conta com uma câmera, um sensor de profundidade, um microfone embutido e o próprio processador e software. Possui paroximadamente 23cm de comprimento horizontal. Apesar do sensor ter sido projetado para Xbox360 existirão futuras aplicações para Windows. Já é possível utilizar o kinect com objetivos de desenvolvimento no windows 7 com o uso do Kinect SDK. Imagem 1: Kinect Sensor [3] 2. Objetivos

Upload: rogeriohenrique

Post on 01-Nov-2014

957 views

Category:

Documents


1 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Kinect

1. Introdução O Kinect é um aparelho desenvolvido para o videogame Xbox360 da microsoft. Este disponibiliza uma série de características: Sensor de movimentos (possibilitando rastrear e reconhecer movimentos do corpo todo); Rastreamento de esqueleto (reconhece e pode representar o esqueleto); Reconhecimento facial (Podendo a partir da leitura facial fazer o login do usuário); Reconhecimento de voz (microfones estrategicamente posicionados dentro do sensor). Sendo o único videogame de mercado em que é possível jogar sem o uso de controles, o kinect foi um grande sucesso de vendas. Curiosamente seu projeto inicialmente se chamava projeto Natal, em alusão a cidade de Natal no Brasil. A Microsoft explica o porque do nome da empresa: “Um de nossos pesquisadores, Alex Kipman, é do Brasil e ele escolheu a cidade de Natal como um tributo a seu País. Além disso, ele sabia que Natal também significa nascer, em latim. Considerando o novo público que será atraído ao Xbox 360 pela novidade, o nome encaixou perfeitamente”. O Sensor Kinect conta com uma câmera, um sensor de profundidade, um microfone embutido e o próprio processador e software. Possui paroximadamente 23cm de comprimento horizontal. Apesar do sensor ter sido projetado para Xbox360 existirão futuras aplicações para Windows. Já é possível utilizar o kinect com objetivos de desenvolvimento no windows 7 com o uso do Kinect SDK.

Imagem 1: Kinect Sensor [3]

2. Objetivos

Page 2: Kinect

Este trabalho tem como objetivo explicar como se utiliza o aparelho Kinect em um computador e as possibilidades para a criação de um desenvolvedor criar seu próprio aplicativo. Será realizado um estudo sobre o Kinect SDK com os seguinte passos: A instalação será realizada seguindo o guia disponibilizado no site oficial do Kinect SDK [4]; Espera-se após a instalação ser possível rodar o Demo da kinect SDK [5]; Serão realizados testes de modificação de código após a compreensão dos tutoriais [6]; Caso todos os passos acima forem executados com sucesso, será feito uma aplicação própria usando as bibliotecas e o aparelho Kinect.

3. Kinect SDK Aqui será apresentado um guia sucinto sobre os requerimentos, instalação, configuração e as principais funções do kinect SDK. É sugerido que o leitor já tenha noções básicas de programação em C++ e seja familiar com o windows e o microsoft visual studio.

3.1 RequerimentosApenas o sistema operacional windows 7 (x86, x64) é compatível. Não é possível rodar o SDK em uma máquina virtual pois o driver do kinect e o sdk devem estar instalados no computador. Os requerimentos de hardware são: computador dual-core, 2,66GHz ou mais; placa de cídeo compátivel com o windows 7 e o directx 9.0; 2GB de memória RAM; sensor Kinect para xbox360. Os requerimentos de software são: Microsoft visual studio 2010; microsoft .NET framework 4 (instalado com o visual studio); Directx SDK.

3.2 Instalação 1. Obter as últimas atualizações para o Windows 7 da Microsoft Update.2. Certifique-se que o dispositivo Kinect não está conectado à porta USB no seu computador.3. Remova todos os outros drivers que você possa ter instalado anteriormente para o dispositivo Kinect.4. Certifique-se de todo o software necessário foi instalado no seu computador, conforme descrito em "Requerimentos ", anteriormente neste guia.5. Fechar o Visual Studio antes de instalar o SDK beta.6. Na página de download do SDK Beta, clique na opção download para o seu computador (x86 ou x64).7. Para iniciar a instalação do SDK beta imediatamente, clique em Executar.8. Siga o assistente para completar a instalação.

Page 3: Kinect

Para configurar o driver do kinect, basta plugar o usb em seu computador e esperar o reconhecimento do driver pelo windows. Lembrando que o kinect deve estar ligado com uma fonte externa de energia. Se instalado corretamente, o kinect apresenta´ra um led verde piscando.

3.3 Protegendo seu Kinect O sensor kinect é protegido contra o aquecimento por uma ventoinha. Caso a temperatura chegue acima de 90° celsius, a câmera desliga automaticamente. Não existe uma interface de software que controle a ventoinha. O controle da câmera é realizado através de API’s, o mecanismo de rotação do aparelho para melhor visualização da câmera não é projetado para uso frequente, caso isso ocorra pode resultar na quebra do aparelho. Posicione o kinect em uma superfície plana e longe de possíveis quedas. Não coloque o sensor perto de soms ou fortes vibrações e mantenha em lugar protegido do sol. Enquanto o kinect está em uso, lembre-se que este funciona apenas para reconhecimento de esqueleto de pessoas em pé e que sua áre de reconhecimento está entre 1.2 e 3.5 metros. Abaixo mais especificações:

Imagem 2: Especificações do Kinect[4]

3.4 NUI API

Page 4: Kinect

O SDK beta fornece uma biblioteca de software sofisticados e ferramentas para ajudar os desenvolvedores a utilizar o kinect. O sensor e a biblioteca NUI interagem para gerar a aplicação, como mostrado abaixo:

Imagem 3: Interação Hardware, Software e aplicação[4]

Abaixo os componentes do SDK:

Imagem 4: Principais componentes[4] 1.Kinect hardwareComponentes de Hardware, incluind o kinect e a porta USB. 2.Microsoft Kinect driversDrivers do kinect no widows 7 que foram instalados durante a instalação do kinect SDK. 3.NUI APIUm conjunto de APIs que recebem dados dos sensores de imagem e controla o kinect. 4.KinectAudio DMOKinect DMO que extende o suporte para microfone no Windows 7 para fazer a funcionalidade

Page 5: Kinect

de reconhecimento de localização através do som. 5.Windows 7 standard APIsAPIs de áudio, voz, e mídia do windows 7.

3.4.1 Inicialização Para implementar sua aplicação são necessários os seguintes passos: C#: 1) Referencie o Microsoft.Research.Kinect.dll.2) Inclua usando diretivas o seguinte:Para o NUI API, inclua: using Microsoft.Research.Kinect.NuiPara o Audio API, inclua: using Microsoft.Research.Kinect.Audio C++: 1) Inclua <windows.h> no código fonte.2) Para usar o NUI API, inclua o MSR_NuiApi.h.Local: Program Files\Microsoft Research KinectSDK\inc3) Para usar o Kinect Audio API, inclua MSRKinectAudio.h.Local: Program Files\Microsoft Research KinectSDK\inc4) Link com o MSRKinectNUI.lib.Local: Program Files\Microsoft Research KinectSDK\lib5) Certifique-se de que as beta SDK DLLs estão no mesmo caminho quando for rodar o projeto.Local: \Program Files\Microsoft Research KinectSDK A tabela a seguir descreve o que incluir em seu projeto:

..\Program Files (x86)\Microsoft Research KinectSDK\inc\

MSR_NuiApi.h

MSR_NuiImageCamera.h

MSR_NuiProps.h

MSR_NuiSkeleton.h

MSRKinectAudio.h

NuiImageBuffer.h

Para usar a API você pode usar as seguintes funções:

Page 6: Kinect

C++:1. NuiInitialize - Inicializa a primeira instancia do sensor kinect no sistema.2. NuiXxx - funções para fazer o stream de imagem , dados do esqueleto e gerenciar a câmera.3. NuiShutdown - Chamar quando o uso do kinect for finalizado. C#:1.Crie um novo objeto Runtime e deixe a lista de parâmetros vazia, como a seguir:nui = new Runtime();2.Chame Runtime.Initialize para inicializar o NUI API.3Chame métodos adicionais na interface para fazer o stream da imagem e administrar os dados de esqueleto e câmeras.4.Chame Runtime.Shutdown quando o uso do sensor Kinect estiver terminado.

3.4.2 Streams de dados de imagens Na inicialização deverá ser especificado quais os subsistemas serão utilizados na aplicação. Esses subsistemas são divididos em cor, profundidade, profundidade e player index, esqueleto.Após a especificação, o NUI retorna do kinect um stream de dados em forma de uma sucessão de imagens. Também é fornecido insofrmações adicionais sobre a stream como resolução, tipo de imagem, buffer e etc. Uma aplicação tem acesso 3 tipos de dados de imagem: Color data - Formatos RGB e YUV Depth data - Retorna uma imagem em que cada pixel representa a distancia Cartesiana(mm) do plano da camera para o objeto mais próximo naquela coordena x e y. Aplicações podem urilizar dos dados de profundidade para suportar funcionalidades personalizadas diversas, tais como rastreamento de movimentos dos usuários ou identificação de objetos de fundo para ignorar durante o funcionamento da aplicação. Player segmentation data - Identifica duas figuras humanas em frente ao sensor e, em seguida, cria um mapa de Segmentação do jogador. Este mapa é um quadro de bits em que o valor do pixel corresponde ao jogador no campo de visão que está mais próximo da câmera. Coletando imagens: Os códigos conseguem coletar o último frame de dados de uma imagem chmando um método de recuperação de frame e passando um buffer. O último frame de dados é copiado no buffer.

Page 7: Kinect

Caso sua aplicação necessite recuperar um frame com mais frequencia do que ele fica pronto, você pode escolher entre esperar pelo próximo frame ou retornar e tentar de novo mais tarde. O NUI API nunca repete a mesma informação de frame mais de uma vez. Existem dois modelos que podem ser usados: Polling Model - Opção mais simples para ler frames. Primeiro a aplicação abre a strem de imagens, então chama por um frame e especifica o tempo máximo de espera. Caso o tempo máximo for colocado como infinito, a aplicação aguardará o quanto for necessário pelo próximo frame. Quando a requisição for retornada com sucesso, o novo frame estará disponível para processamento. Em uma aplicação em C++ chama-se NuiImageStreamOpen para abrir uma stream de cor ou profundidade. NuiImageStreamGetNextFrame é usado para fazer o polling. Event Model - Possibilita recuperar o frame de um esqueleto com mais flexibilidade e precisão. Em C++ passa-se um evento NuiImageStreamOpen. Quando um novo frame de dados de imagem está pronto, o evento é avisado. Qauqluer thread a espera acorda e recebe o quadro de esqueleto dados chamando NuiImageGetNextFrame. Durante este período, o evento é reiniciado pelo NUI API.

3.4.3 Reconhecimento de esqueleto O NUI Skeleton API fornece a localização de até dois jogadores com a informação detalhada de posição e orientação. A informação é dada para a plicação como um conjunto de pontos que compõem o esqueleto. Aplicações que usam informações de esqueleto devem incicializar no NUI Initialization e devem habilitar o skeleton tracking.

Page 8: Kinect

Imagem 5: Conjunto de pontos do esqueleto[4]Recuperando informação de esqueleto: Semelhante a coleta de imagem, recuperar uma informação de esqueleto basta chama um função passando o buffer e o tempo máximo de espera. Podendo ser usado o polling ou o event model.Em C++ para usar o polling model é usado NuiSkeletonGetNextFrame e para usar o event model é usado NuiSkeletonTrackingEnable. Reconhecimento de esqueleto ativo e passivo: O Kinect reconhece apenas o esqueleto 2 jogadores ativamente. Quando um esqueleto é reconhecido ativamente, é fornecido uma informação completa sobre esse esqueleto. O reconhecimento de esqueleto passivo pode reconhecer até 4 jogadores. Por padrão o Kinect escolhe ativamente 2 esqueletos como mostrado abaixo:

Page 9: Kinect

Imagem 6: 2 esqueletos reconhecidos ativamente[4]

A informação de esqueleto é retornada em um skeleton frame que contem um vetor de estrutura de dados, um para cada esqueleto reconhecido. As seguintes informações são fornecidas:Estado de tracking de cada esqueleto; Um ID único para cada esqueleto encontrado; Um posição a partir do centro de massa do jogador.

3.4.4 Transformações Profundidade: Frames da imagem de profundidade são 640x480, 320×240, ou 80x60 pixels. Cada pixel representa a distância cartesiana em mm do plano da câmera até o objeto mais próximo naquela coordenada x e y. Um pixel de valor 0 indica que nenhum objeto foi encontrado naquela posição.

Page 10: Kinect

Imagem 7: esquema de reconhecimento de profundidade de objetos[4] Esqueleto: As posicões do esqueleto são representadas em coordenadas x, y e z e em metros.Caso o sensor seja posicionado em uma superfície não plana, pode ser possível que o eixo y não esteja perpendicular ao chão, podendo resultar em um efeito que pessoas em pé possam aparecer tortas.

Imagem 8: Eixo x, y e z [4]

Determinação do chão: Em um frame de esqueleto podemos encontrar um vetor chamado floor clip plane que determina a localização do chão. A equação geral do plano é Ax + By + Cz + D = 0, onde:A = vFloorClipPlane.x B = vFloorClipPlane.y C = vFloorClipPlane.z D = vFloorClipPlane.w A equação é normalizada para que D seja interpretado como a altura da câmera em relação ao chão, em metros. Caso o chão não esteja visível. o floor clip plane é um vetor com valor

Page 11: Kinect

zero. Tal vetor pode ser encontrado em vFloorClipPlane que é um campo da estrutura NUI_SKELETON_FRAME. Espelhamento de esqueleto: Por padrão o esqueleto espelha o usuário. Dependendo da aplicação pode ser interessante usar uma matriz de transformação para mudar o espelhamento.

4. Exemplo: SkeletalViewer C++:

Imagem 9: SkeletalViewer[4] O aplicativo SkeletalViewer é dividido em 3 arquivos.SkeletalViewer.cpp(Começo da aplicação, janela principal e funções de callback); NUIImpl.cpp e SkeletalViewer.h (implementam a classe CSkeletalViewerApp que captura dados do kinect e transforma para renderização); DrawDevice.cpp e DrawDevice.h (implementam a classe DrawDevice que usa Direct3D para a renderização de imagens). Fluxo do programa: 1. Cria a janela principal em que o programa exibe imagens do sensor Kinect.2. Inicializar o sensor Kinect, abre streams para cada tipo de dados e cria uma thread para processar os dados.3. Processa dados do sensor e renderiza imagens na janela assim que chegam novos frames.4. Limpa e sai quando o usuário fecha a janela. C#: Em C#, o exemplo da Microsoft do SkeletalViewer tem alguns programas básicos:

Page 12: Kinect

● App.xaml, onde declara os níveis da aplicação● App.xaml.cs que contém a codificação por trás d App.xaml● MainWindow.xaml declara o layout da aplicação principal● MainWindow.xaml.cs que contém a codificação por trás da jánela principal, e

também inicialização NUI API● e o SkeletalViewer.ico, definida como icone da aplicação

Imagem 10: SkeletalViewer[4]

O EsqueletalViewer em C# usa a NUI API para capturar dados, cor e movimentos do esqueleto e assim transformar em imagens utilizando o WPF. A aplicação C# SkeletalViewer é instalada com Kinect for Windows Software Development Kit (SDK) Beta que esta no arquivo KINECTSDK_DIR%\Samples\KinectSDKSamples.zipA implementação é feita nos seguintes arquivos:

Page 13: Kinect

Construir e rodar o exemplo1. No Windows Explorer, navegue ate SkeletalViewer\CS directory.2. De um duplo clique no arquivo com extensão .sln (solution) para abrir no Visual Studio.3. Rode a aplicação4. Clique em CTRL+F5 para rodar o código.O arquivo solução atinge x86 platform, pois o beta SDK inclui somente a x86 libraries. Criar janela principal O C# SkeletalViewer usa WPF para criar a janela que mostra a imagem real, em profundidade, e os frames do esqueleto, e faz uma aproximação destes por segundo. è usado o System.Windows para classes elementares e também para adição de características especificas. è de extrema importância incluir a Microsoft.Research.Kinect.Nui para acessar o código de interface do beta SDK. O código abaixo mostra a inicialização do código MainWindow namespace SkeletalViewerpublic partial class MainWindow : Window{ public MainWindow() { InitializeComponent(); } Runtime nui; int totalFrames = 0; int lastFrames = 0; DateTime lastTime = DateTime.MaxValue; const int RED_IDX = 2; const int GREEN_IDX = 1; const int BLUE_IDX = 0; byte[] depthFrame32 = new byte[320 * 240 * 4]; Dictionary<JointID,Brush> jointColors = new Dictionary<JointID,Brush>() { {JointID.HipCenter, new SolidColorBrush(Color.FromRgb(169, 176, 155))}, {JointID.Spine, new SolidColorBrush(Color.FromRgb(169, 176, 155))}, {JointID.ShoulderCenter, new SolidColorBrush(Color.FromRgb(168, 230,29))}, {JointID.Head, new SolidColorBrush(Color.FromRgb(200, 0, 0))} {JointID.ShoulderLeft, new SolidColorBrush(Color.FromRgb(79, 84, 33))}, {JointID.ElbowLeft, new SolidColorBrush(Color.FromRgb(84, 33, 42))}, {JointID.WristLeft, new SolidColorBrush(Color.FromRgb(255, 126, 0))}, {JointID.HandLeft, new SolidColorBrush(Color.FromRgb(215, 86, 0))}, {JointID.ShoulderRight, new SolidColorBrush(Color.FromRgb(33, 79, 84))}, {JointID.ElbowRight, new SolidColorBrush(Color.FromRgb(33, 33, 84))}, {JointID.WristRight, new SolidColorBrush(Color.FromRgb(77, 109, 243))}, {JointID.HandRight, new SolidColorBrush(Color.FromRgb(37, 69, 243))}, {JointID.HipLeft, new SolidColorBrush(Color.FromRgb(77, 109, 243))}, {JointID.KneeLeft, new SolidColorBrush(Color.FromRgb(69, 33, 84))}, {JointID.AnkleLeft, new SolidColorBrush(Color.FromRgb(229, 170, 122))}, {JointID.FootLeft, new SolidColorBrush(Color.FromRgb(255, 126, 0))}, {JointID.HipRight, new SolidColorBrush(Color.FromRgb(181, 165, 213))},

Page 14: Kinect

{JointID.KneeRight, new SolidColorBrush(Color.FromRgb(71, 222, 76))}, {JointID.AnkleRight, new SolidColorBrush(Color.FromRgb(245, 228, 156))}, {JointID.FootRight, new SolidColorBrush(Color.FromRgb(77, 109, 243))} };// . . . SkeletalViewer namespace code continues O metodo Window_Loaded manuseia a Window.Loaded e inicializa a NUI APIO nui_DepthFrameReady, nui_SkeletonFrameReady, e nui_ColorFrameReady são manipuladores de eventos.O método convertDepthFrame converte dados em 16-bit para dados em formato 32-bit.O método getBodySegment desenha partes do esqueleto.O método Window_Closed deriva do evento Window.Closed.A classe MainWindow inicializa a janela e cria variáveis globais: Declara nui como um objeto Runtime, que representa o sensor do Kinect no momento.Inicializa diversas variáveis—totalFrames, lastFrames, e lastTime—que são usadas para calcular o numero de frames por segundoDefine depthFrame32 como um vetor de bytes, suficiente para armazenar o frame de 32 bits por pixel e define o RED_IDX, GREEN_IDX, e BLUE_IDX como constantes para indexar o vetor.Cria jointColors como uma classe System.Collections.Generic.Dictionary que associa cor ao esqueleto Inicialização RunTime

O método Window_Loaded cria o nui objeto runtime, abrindo o vídeo e streams, setando o evento que são chamados quando um vídeo ou frame esta pronto:

private void Window_Loaded(object sender, EventArgs e ){ nui = new Runtime(); try { nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor); } catch (InvalidOperationException) { // Display error message; omitted for space return; } try { nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);

nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex); } catch (InvalidOperationException) {

// Display error message; omitted for spacereturn;

} lastTime = DateTime.Now;

Page 15: Kinect

nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs> (nui_DepthFrameReady); nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs> (nui_SkeletonFrameReady); nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs> (nui_ColorFrameReady);} A aplicação tem que inicializar o sensor do Kinect chamando Runtime.Initialize antes da chamada qualquer outro método do objeto. Runtime.Initialize inicializa uma aplicação interna que recupera dados do sensor e sinais quando um frame esta pronto. O método Initialize possui uma exceção InvalidOperationException senão encontrar o sensor Kinect.O único parâmetro de Runtime.Initialize e opções de bit a bit ou, combinação dos seguintes:

· UseDepthAndPlayerIndex· UseColor· UseSkeletalTracking· UseDepth· O exemplo usa cor, profundidade, índice do jogador e do esqueleto assim

especifica as UseDepthAndPlayerIndex, UseColor, and UseSkeletalTracking.· Na próxima chamada o método Window_Loaded abre o fluxo de vídeo e

profundidade através da nui.VideoStream.Open e nui.DepthStream.Open. VideoStream e DepthStream são propriedade do objeto Runtime.

· O método ImageStream.Open possui os seguintes parâmetros:· O tipo de fluxo pode ser ImageStreamType.Video ou ImageStreamType.Depth.· Possuem 2 buffers, um para desenhar e outro que captura.· A resolução da imagem, ImageResolution.· O tipo da imagem, ImageType.· Se a aplicação especifica um tipo de resolução ou imagem não compatível com

as especificações o ImageStream.Open lança uma exceção.· Para obter imagens coloridas:· A opção deve incluir UseColor.· Resoluções validas são 1280x1024 e 640x480.· Tipos validos de cor: ColorYUV e ColorYUVRaw.SkeletalViewer Walkthrough –

30· Para transmitir profundidade e dados do jogador:· Deve incluir UseDepthAndPlayerIndex.· Resoluções validas de profundidade e dados do índice do jogador são 320x240 e

80x60.· O único tipo de imagem valida e DepthAndPlayerIndex.

Na próxima etapa o Window_Loaded salva o tempo corrente em lastTime para calcular os frames por segundo.Existem dois modos para recuperar frames. Uma aplicação pode usar o ImageStream.GetNextFrame ou SkeletonEngine.GetNextFrame que espera ate que o frame esteja pronto, ou a orientada a eventos. O SkeletalViewer usa o tipo orientado a eventos. Para implementar o modelo orientado a evento, o método Window_Loaded cria eventos: DepthFrameReady, SkeletonFrameReady, e VideoFrameReady para a nui Runtime e associa com manipuladores que são nomeados nui_DepthFrameReady, nui_SkeletonFrameReady, e nui_ColorFrameReady.

Page 16: Kinect

Processando dados do Vídeo Quando o frame do vídeo esta pronto, o runtime sinaliza o evento VideoFrameReady e chama a nui_ColorFrameReady. Abaixo esta o código: void nui_ColorFrameReady(object sender, ImageFrameReadyEventArgs e){ PlanarImage Image = e.ImageFrame.Image; video.Source = BitmapSource.Create( Image.Width, Image.Height, 96, 96, PixelFormats.Bgr32, null, Image.Bits, Image.Width * Image.BytesPerPixel);} O parâmetro e é passado como argumento não função acima pertence à classe ImageFrameReadyEventArgs que possui uma propriedade ImageFrame. O ImageFrame.Image é objeto PlanarImage que representa apropria imagem. A imagem é plana no RGB com 32 bits por pixel.O método nui_ColorFrameReady chama o WPF BitmapSource, que cria um bitmap para a tela, passando os parâmetros:

· Largura e altura da imagem que estão no objeto PlanarImage.· Resolução vertical e horizontal do bitmap—96 pontos por polegadas em cada

direção.· O formato do pixel identificado por PixelFormats.Bgr32 no

System.Windows.Media.· A paleta deve ser nula, pois não é usada na imagem.· O vetor de bits que forma a imagem.

WPF lida com exibição do bitmap resultante.Processando dados de profundidade Quando a imagem da câmera de profundidade esta pronta, o runtime sinaliza o DepthFrameReady e chama nui_DepthFrameReady. Este manipulador de evento recupera a imagem converte no formato desejado e chama o método BitmapSource. O método nui_DepthFrameReady também atualiza os frames-por-segundo. Abaixo a demonstração do código:void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e){ PlanarImage Image = e.ImageFrame.Image; byte[] convertedDepthFrame = convertDepthFrame(Image.Bits); depth.Source = BitmapSource.Create(Image.Width, Image.Height, 96, 96, PixelFormats.Bgr32, null, convertedDepthFrame, Image.Width * 4); ++totalFrames; DateTime cur = DateTime.Now; if (cur.Subtract(lastTime) > TimeSpan.FromSeconds(1)) { int frameDiff = totalFrames - lastFrames; lastFrames = totalFrames;

lastTime = cur;frameRate.Text = frameDiff.ToString() + " fps";

}}Quando uma aplicação tem dados de profundidade então possui um vetor de 16-bit, da

Page 17: Kinect

seguinte forma:● Os 3 primeiros bits contem o ID do esqueleto.● Os bits restantes contem o valor da profundidade em milímetros.Se a aplicação exibir um quadro de profundidade em escala cinza, os usuários acham difícil distinguir os indivíduos na tela, então a amostra converte a 16-bit na escala cinza para 32-bit RGB, com cada pessoa em uma cor diferente. A conversão opera em um vetor de bytes de dados de profundidade. Segue os passos:1. Guarda o numero do jogador a partir dos 3 primeiros bits na variável players. Valores são entre 0-6. Valor igual a 0 significa que nenhum jogador esta presente.2. Recupera 11 bits dos dados de profundidade guarda na variável realDepth.3. Inverte o resultado para gerar um valor de 8 bits de intensidade que é melhor para exibição de imagens, assim objetos mais pertos ficam mais brilhantes e objetos mais distantes parecem mais escuros.4. Zero inicializa os elementos do 32-bit frame.5. Atribuir às cores vermelha verde, e azul para o resultado com base no numero do jogador, usando as constantes RED_IDX, GREEN_IDX, e BLUE_IDX no vetor.O método abaixo refere ao convertDepthFrame: byte[] convertDepthFrame(byte[] depthFrame16){ for (int i16 = 0, i32 = 0) i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4) {

int player = depthFrame16[i16] & 0x07;int realDepth = (depthFrame16[i16+1] << 5) | (depthFrame16[i16] >> 3);byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));

depthFrame32[i32 + RED_IDX] = 0;depthFrame32[i32 + GREEN_IDX] = 0;depthFrame32[i32 + BLUE_IDX] = 0;switch (player){

case 0: depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2); depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2); depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 2); break; case 1: depthFrame32[i32 + RED_IDX] = intensity; break; case 2: depthFrame32[i32 + GREEN_IDX] = intensity; break; case 3: depthFrame32[i32 + RED_IDX] = (byte)(intensity / 4); depthFrame32[i32 + GREEN_IDX] = (byte)(intensity); depthFrame32[i32 + BLUE_IDX] = (byte)(intensity); break; case 4: depthFrame32[i32 + RED_IDX] = (byte)(intensity); depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);

Page 18: Kinect

depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 4); break; case 5: depthFrame32[i32 + RED_IDX] = (byte)(intensity); depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 4); depthFrame32[i32 + BLUE_IDX] = (byte)(intensity); break; case 6: depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);

depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2); depthFrame32[i32 + BLUE_IDX] = (byte)(intensity); break; case 7: depthFrame32[i32 + RED_IDX] = (byte)(255 - intensity); depthFrame32[i32 + GREEN_IDX] = (byte)(255 - intensity); depthFrame32[i32 + BLUE_IDX] = (byte)(255 - intensity); break;

} }return depthFrame32;} Processando dados do Skeleton O método nui_SkeletonFrameReady manipula o evento SkeletonFrameReady. Este método recupera os dados do esqueleto, limpa o display, e em seguida desenha linhas que representa as partes do esqueleto, abaixo o algoritmo: void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e){ SkeletonFrame skeletonFrame = e.SkeletonFrame; int iSkeleton = 0; Brush[] brushes = new Brush[6]; brushes[0] = new SolidColorBrush(Color.FromRgb(255, 0, 0)); brushes[1] = new SolidColorBrush(Color.FromRgb(0, 255, 0)); brushes[2] = new SolidColorBrush(Color.FromRgb(64, 255, 255)); brushes[3] = new SolidColorBrush(Color.FromRgb(255, 255, 64)); brushes[4] = new SolidColorBrush(Color.FromRgb(255, 64, 255)); brushes[5] = new SolidColorBrush(Color.FromRgb(128, 128, 255)); skeleton.Children.Clear(); foreach (SkeletonData data in skeletonFrame.Skeletons) { if (SkeletonTrackingState.Tracked == data.TrackingState)

{ // Draw bones Brush brush = brushes[iSkeleton % brushes.Length]; skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.Spine, JointID.ShoulderCenter, JointID.Head));

Page 19: Kinect

skeleton.Children.Add(getBodySegme nt(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderLeft, JointID.ElbowLeft, JointID.WristLeft, JointID.HandLeft)); skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.ShoulderCenter, JointID.ShoulderRight, JointID.ElbowRight, JointID.WristRight, JointID.HandRight)); skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipLeft, JointID.KneeLeft, JointID.AnkleLeft, JointID.FootLeft)); skeleton.Children.Add(getBodySegment(data.Joints, brush, JointID.HipCenter, JointID.HipRight, JointID.KneeRight, JointID.AnkleRight, JointID.FootRight)); // Draw joints

foreach (Joint joint in data.Joints) { Point jointPos = getDisplayPosition(joint); Line jointLine = new Line(); jointLine.X1 = jointPos.X - 3 ; jointLine.X2 = jointLine.X1 + 6; jointLine.Y1 = jointLine.Y2 = jointPos.Y; jointLine.Stroke = jointColors[joint.ID]; jointLine.StrokeThickness = 6; skeleton.Children.Add(jointLine); }

} iSkeleton++; }} O método nui_SkeletonFrameReady primeiro recupera os dados do esqueleto e armazena em um objeto SkeletonFrame. Em seguida analisa as variáveis iSkeleton e brushes para elaboração do esqueleto. A variável brushes é um vetor de System. Windows.Media.Brush, e assim cada elemento do vetor é do tipo SolidColorBrush objeto. Em seguida nui_SkeletonFrameReady limpa o display chamando Children.Clear no esqueleto WPF canvas.Agora o método desenha o esqueleto: primeiro os ossos e depois as articulações.O skeletonFrame.Skeletons é uma matriz de estrutura de SkeletonData que contem dados para um único esqueleto. Se o campo TrackingState do SkeletonData indicar que o esqueleto esta sendo monitorado, o nui_SkeletonFrameReady escolhe uma cor e chama o método getBodySegment diversas vezes para desenhar as linhas do esqueleto.O método getBodySegment tem 3 parâmetros:● Uma cloacae de articulacoes Microsoft.Research.Kinect.Nui.JointsCollection● A escova com qual desenha esta linha.● Um numero de variáveis JointID, onde cada valor identifica um determinado conjunto

sobre o esqueleto.A maior parte do código no getBodySegment envolve chamadas para métodos PointCollection e. PolylineSystem.Windows.Media. O método getBodySegment coleta pontos e os junta com segmento de linha. Esse method retorna uma Polyline que conecta as articulações. Os dados do esqueleto, cor, imagem, profundidade são baseados em diferentes sistemas de coordenadas. Para mostrar uma imagem consistente, o programa converte coordenadas

Page 20: Kinect

no espaço do esqueleto para o espaço da imagem, seguindo esses passos:1. Converter as coordenadas do esqueleto no intervalo [-1.0, 1.0] para as coordenadas de profundidade chamando o SkeletonEngine. SkeletonToDepthImage. Essa função retorna coordenadas x e y como números de ponto flutuante no intervalo 0.0 ate 1.0.2. Converter as coordenadas em ponto flutuante para valores no espaço 320x240 que é o intervalo que o NuiCamera.GetColorPixelCoordinatesFromDepthPixel requer.3. Converter a coordenada de profundidade para coordenada de imagem colorida chamando NuiCamera.GetColorPixelCoordinatesFromDepthPixel. Esse método retorna a coordenada de imagem colorida no intervalo de 640x480.4. Dimensionar a coordenada da imagem colorida para o tamanho do display do esqueleto na tela dividindo a coordenada x por 640 em a coordenada y por 480 e multiplicando o resultado pela altura e largura da área de exibição do esqueleto. A funcao getDisplayPosition esta descrita abaixo:private Point getDisplayPosition(Joint joint){ float depthX, depthY; nui.SkeletonEngine.SkeletonToDepthImage(joint.Position, out depthX, out depthY); depthX = Math.Max(0, Math.Min(depthX * 320, 320)); depthY = Math.Max(0, Math.Min(depthY * 240, 240)); int colorX, colorY; ImageViewArea iv = new ImageViewArea(); // only ImageResolution.Resolution640x480 is supported at this point nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel( ImageResolution.Resolution640x480, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY); return new Point((int)(skeleton.Width * colorX / 640.0), (int)(skeleton.Height * colorY / 480));}Quando todos os ossos foram desenhados o nui_SkeletonFrameReady desenha as articulações. Cada conjunto é desenhado como uma caixa 6X6. O método nui_SkeletonFrameReady transforma a posição inicial (x, y) de cada conjunto chamado getDisplayPosition, assim como o getBodySegment transformou a posição dos ossos. WPF lida com redenrizacao na tela, resultando cavas na tela.

Referências:[1] Real-Time Human Pose Recognition in Parts from Single Depth Images; Microsoft Research Cambridge & Xbox Incubation. [2] http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/guides.aspx, acessado em 21/10/2011 [3] http://www.neuronik.com.br/produtos/255-video, acessado em 21/10/2011 [4]http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/docs/

Page 21: Kinect

ProgrammingGuide_KinectSDK.pdf, acessado em 21/10/2011 [5] http://research.microsoft.com/apps/video/default.aspx?id=150286, acessado em 21/10/2011 [6] http://research.microsoft.com/enus/um/redmond/projects/kinectsdk/guides.aspx, acessado em 21/10/2011