Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
596
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
39
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 1. IntroduçãoO Kinect é um aparelho desenvolvido para o videogame Xbox360 da microsoft. Estedisponibiliza uma série de características: Sensor de movimentos (possibilitando rastreare reconhecer movimentos do corpo todo); Rastreamento de esqueleto (reconhece e poderepresentar o esqueleto); Reconhecimento facial (Podendo a partir da leitura facial fazer o logindo usuário); Reconhecimento de voz (microfones estrategicamente posicionados dentro dosensor).Sendo o único videogame de mercado em que é possível jogar sem o uso de controles, okinect foi um grande sucesso de vendas. Curiosamente seu projeto inicialmente se chamavaprojeto Natal, em alusão a cidade de Natal no Brasil. A Microsoft explica o porque do nome daempresa: “Um de nossos pesquisadores, Alex Kipman, é do Brasil e ele escolheu a cidade deNatal 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 nomeencaixou perfeitamente”.O Sensor Kinect conta com uma câmera, um sensor de profundidade, um microfone embutidoe 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 doKinect SDK. Imagem 1: Kinect Sensor [3]2. Objetivos
  • 2. Este trabalho tem como objetivo explicar como se utiliza o aparelho Kinect em um computadore 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ósa instalação ser possível rodar o Demo da kinect SDK [5]; Serão realizados testes demodificação de código após a compreensão dos tutoriais [6]; Caso todos os passos acimaforem executados com sucesso, será feito uma aplicação própria usando as bibliotecas e oaparelho Kinect.3. Kinect SDKAqui será apresentado um guia sucinto sobre os requerimentos, instalação, configuraçãoe as principais funções do kinect SDK. É sugerido que o leitor já tenha noções básicas deprogramaçã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 SDKem 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ídeocompátivel com o windows 7 e o directx 9.0; 2GB de memória RAM; sensor Kinect paraxbox360. Os requerimentos de software são: Microsoft visual studio 2010; microsoft .NETframework 4 (instalado com o visual studio); Directx SDK.3.2 Instalação1. 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 odispositivo Kinect.4. Certifique-se de todo o software necessário foi instalado no seu computador, conformedescrito 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.
  • 3. Para configurar o driver do kinect, basta plugar o usb em seu computador e esperar oreconhecimento do driver pelo windows. Lembrando que o kinect deve estar ligado com umafonte externa de energia. Se instalado corretamente, o kinect apresenta´ra um led verdepiscando.3.3 Protegendo seu KinectO sensor kinect é protegido contra o aquecimento por uma ventoinha. Caso a temperaturachegue acima de 90° celsius, a câmera desliga automaticamente. Não existe uma interface desoftware que controle a ventoinha.O controle da câmera é realizado através de API’s, o mecanismo de rotação do aparelho paramelhor visualização da câmera não é projetado para uso frequente, caso isso ocorra poderesultar na quebra do aparelho.Posicione o kinect em uma superfície plana e longe de possíveis quedas. Não coloque osensor perto de soms ou fortes vibrações e mantenha em lugar protegido do sol. Enquanto okinect está em uso, lembre-se que este funciona apenas para reconhecimento de esqueletode pessoas em pé e que sua áre de reconhecimento está entre 1.2 e 3.5 metros. Abaixo maisespecificações: Imagem 2: Especificações do Kinect[4]3.4 NUI API
  • 4. O SDK beta fornece uma biblioteca de software sofisticados e ferramentas para ajudar osdesenvolvedores a utilizar o kinect. O sensor e a biblioteca NUI interagem para gerar aaplicaçã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
  • 5. 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çãoPara 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.AudioC++:1) Inclua <windows.h> no código fonte.2) Para usar o NUI API, inclua o MSR_NuiApi.h.Local: Program FilesMicrosoft Research KinectSDKinc3) Para usar o Kinect Audio API, inclua MSRKinectAudio.h.Local: Program FilesMicrosoft Research KinectSDKinc4) Link com o MSRKinectNUI.lib.Local: Program FilesMicrosoft Research KinectSDKlib5) Certifique-se de que as beta SDK DLLs estão no mesmo caminho quando for rodar o projeto.Local: Program FilesMicrosoft Research KinectSDKA tabela a seguir descreve o que incluir em seu projeto: ..Program Files (x86)Microsoft Research KinectSDKinc MSR_NuiApi.h MSR_NuiImageCamera.h MSR_NuiProps.h MSR_NuiSkeleton.h MSRKinectAudio.h NuiImageBuffer.hPara usar a API você pode usar as seguintes funções:
  • 6. 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 acâ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 osdados de esqueleto e câmeras.4.Chame Runtime.Shutdown quando o uso do sensor Kinect estiver terminado.3.4.2 Streams de dados de imagensNa 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ãode imagens. Também é fornecido insofrmações adicionais sobre a stream como resolução, tipode imagem, buffer e etc. Uma aplicação tem acesso 3 tipos de dados de imagem:Color data - Formatos RGB e YUVDepth 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 podemurilizar dos dados de profundidade para suportar funcionalidades personalizadas diversas, taiscomo rastreamento de movimentos dos usuários ou identificação de objetos de fundo paraignorar durante o funcionamento da aplicação.Player segmentation data - Identifica duas figuras humanas em frente ao sensor e, emseguida, cria um mapa de Segmentação do jogador. Este mapa é um quadro de bits em que ovalor 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étodode recuperação de frame e passando um buffer. O último frame de dados é copiado no buffer.
  • 7. 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 deimagens, então chama por um frame e especifica o tempo máximo de espera. Caso o tempomáximo for colocado como infinito, a aplicação aguardará o quanto for necessário pelo próximoframe. Quando a requisição for retornada com sucesso, o novo frame estará disponível paraprocessamento.Em uma aplicação em C++ chama-se NuiImageStreamOpen para abrir uma stream de cor ouprofundidade. 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 deimagem está pronto, o evento é avisado. Qauqluer thread a espera acorda e recebe o quadrode esqueleto dados chamando NuiImageGetNextFrame. Durante este período, o evento éreiniciado pelo NUI API.3.4.3 Reconhecimento de esqueletoO NUI Skeleton API fornece a localização de até dois jogadores com a informação detalhadade posição e orientação. A informação é dada para a plicação como um conjunto de pontos quecompõem o esqueleto. Aplicações que usam informações de esqueleto devem incicializar noNUI Initialization e devem habilitar o skeleton tracking.
  • 8. 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 umfunção passando o buffer e o tempo máximo de espera. Podendo ser usado o polling ou oevent model.Em C++ para usar o polling model é usado NuiSkeletonGetNextFrame e para usar o eventmodel é 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. Oreconhecimento de esqueleto passivo pode reconhecer até 4 jogadores. Por padrão o Kinectescolhe ativamente 2 esqueletos como mostrado abaixo:
  • 9. Imagem 6: 2 esqueletos reconhecidos ativamente[4]A informação de esqueleto é retornada em um skeleton frame que contem um vetor deestrutura de dados, um para cada esqueleto reconhecido. As seguintes informaçõessão fornecidas:Estado de tracking de cada esqueleto; Um ID único para cada esqueletoencontrado; Um posição a partir do centro de massa do jogador.3.4.4 TransformaçõesProfundidade:Frames da imagem de profundidade são 640x480, 320×240, ou 80x60 pixels. Cada pixelrepresenta a distância cartesiana em mm do plano da câmera até o objeto mais próximonaquela coordenada x e y. Um pixel de valor 0 indica que nenhum objeto foi encontradonaquela posição.
  • 10. 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 osensor seja posicionado em uma superfície não plana, pode ser possível que o eixo y nãoesteja perpendicular ao chão, podendo resultar em um efeito que pessoas em pé possamaparecer 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 quedetermina a localização do chão. A equação geral do plano é Ax + By + Cz + D = 0, onde:A = vFloorClipPlane.xB = vFloorClipPlane.yC = vFloorClipPlane.zD = vFloorClipPlane.wA equação é normalizada para que D seja interpretado como a altura da câmera em relaçãoao chão, em metros. Caso o chão não esteja visível. o floor clip plane é um vetor com valor
  • 11. zero. Tal vetor pode ser encontrado em vFloorClipPlane que é um campo da estruturaNUI_SKELETON_FRAME.Espelhamento de esqueleto:Por padrão o esqueleto espelha o usuário. Dependendo da aplicação pode ser interessanteusar uma matriz de transformação para mudar o espelhamento.4. Exemplo: SkeletalViewerC++: 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 aclasse CSkeletalViewerApp que captura dados do kinect e transforma para renderização);DrawDevice.cpp e DrawDevice.h (implementam a classe DrawDevice que usa Direct3D para arenderizaçã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 paraprocessar 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:
  • 12. ● 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 esqueletoe 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%SamplesKinectSDKSamples.zipA implementação é feita nos seguintes arquivos:
  • 13. Construir e rodar o exemplo1. No Windows Explorer, navegue ate SkeletalViewerCS 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 principalO 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 oSystem.Windows para classes elementares e também para adição de característicasespecificas. è de extrema importância incluir a Microsoft.Research.Kinect.Nui para acessar ocódigo de interface do beta SDK.O código abaixo mostra a inicialização do código MainWindownamespace 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))},
  • 14. {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 continuesO metodo Window_Loaded manuseia a Window.Loaded e inicializa a NUI APIO nui_DepthFrameReady, nui_SkeletonFrameReady, e nui_ColorFrameReady sãomanipuladores 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 paracalcular o numero de frames por segundoDefine depthFrame32 como um vetor de bytes, suficiente para armazenar o frame de 32 bitspor pixel e define o RED_IDX, GREEN_IDX, e BLUE_IDX como constantes para indexar ovetor.Cria jointColors como uma classe System.Collections.Generic.Dictionary que associa cor aoesqueletoInicialização RunTime O método Window_Loaded cria o nui objeto runtime, abrindo o vídeo e streams, setando oevento 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 space return; } lastTime = DateTime.Now;
  • 15. 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 dachamada qualquer outro método do objeto. Runtime.Initialize inicializa uma aplicaçãointerna que recupera dados do sensor e sinais quando um frame esta pronto. O métodoInitialize 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 osframes por segundo.Existem dois modos para recuperar frames. Uma aplicação pode usar oImageStream.GetNextFrame ou SkeletonEngine.GetNextFrame que espera ate que oframe esteja pronto, ou a orientada a eventos. O SkeletalViewer usa o tipo orientadoa eventos. Para implementar o modelo orientado a evento, o método Window_Loadedcria eventos: DepthFrameReady, SkeletonFrameReady, e VideoFrameReady para anui Runtime e associa com manipuladores que são nomeados nui_DepthFrameReady,nui_SkeletonFrameReady, e nui_ColorFrameReady.
  • 16. Processando dados do VídeoQuando o frame do vídeo esta pronto, o runtime sinaliza o evento VideoFrameReady echama 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 à classeImageFrameReadyEventArgs que possui uma propriedade ImageFrame. OImageFrame.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 umbitmap 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 profundidadeQuando a imagem da câmera de profundidade esta pronta, o runtime sinaliza oDepthFrameReady e chama nui_DepthFrameReady. Este manipulador de evento recuperaa imagem converte no formato desejado e chama o método BitmapSource. O métodonui_DepthFrameReady também atualiza os frames-por-segundo. Abaixo a demonstraçãodo 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
  • 17. 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ícildistinguir 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 bytesde dados de profundidade. Segue os passos:1. Guarda o numero do jogador a partir dos 3 primeiros bits na variável players. Valores sãoentre 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 paraexibição de imagens, assim objetos mais pertos ficam mais brilhantes e objetos maisdistantes 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 dojogador, 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);
  • 18. 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 SkeletonO método nui_SkeletonFrameReady manipula o evento SkeletonFrameReady. Estemétodo recupera os dados do esqueleto, limpa o display, e em seguida desenha linhas querepresenta 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));
  • 19. 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 armazenaem um objeto SkeletonFrame. Em seguida analisa as variáveis iSkeleton e brushes paraelaboraçã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 seguidanui_SkeletonFrameReady limpa o display chamando Children.Clear no esqueleto WPFcanvas.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 contemdados para um único esqueleto. Se o campo TrackingState do SkeletonData indicar que oesqueleto esta sendo monitorado, o nui_SkeletonFrameReady escolhe uma cor e chama omé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étodosPointCollection e. PolylineSystem.Windows.Media. O método getBodySegment coletapontos e os junta com segmento de linha. Esse method retorna uma Polyline que conectaas articulações.Os dados do esqueleto, cor, imagem, profundidade são baseados em diferentes sistemasde coordenadas. Para mostrar uma imagem consistente, o programa converte coordenadas
  • 20. 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 ResearchCambridge & Xbox Incubation.[2] http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/guides.aspx, acessadoem 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/
  • 21. 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, acessadoem 21/10/2011