Maicon Juliano Fritsch
CONSTRUÇÃO DE UM FRAMEWORK PARA REALIZAÇÃO DA LEITURA DA
FREQUÊNCIA CARDÍACA UTILIZANDO O KINECT V2.0
Palmas – TO
2017
Maicon Juliano Fritsch
CONSTRUÇÃO DE UM FRAMEWORK PARA REALIZAÇÃO DA LEITURA DA
FREQUÊNCIA CARDÍACA UTILIZANDO O KINECT V2.0
Trabalho de Conclusão de Curso (TCC) II elaborado e
apresentado como requisito parcial para obtenção do
título de bacharel em Sistemas de Informação pelo
Centro Universitário Luterano de Palmas
(CEULP/ULBRA).
Orientador: Prof. M.e Fabiano Fagundes.
Palmas – TO
2017
Maicon Juliano Fritsch
CONSTRUÇÃO DE UM FRAMEWORK PARA REALIZAÇÃO DA LEITURA DA
FREQUÊNCIA CARDÍACA UTILIZANDO O KINECT V2.0
Trabalho de Conclusão de Curso (TCC) II elaborado e
apresentado como requisito parcial para obtenção do
título de bacharel em Sistemas de Informação pelo
Centro Universitário Luterano de Palmas
(CEULP/ULBRA).
Orientador: Prof. M.e Fabiano Fagundes.
Aprovado em: _____/_____/_______
BANCA EXAMINADORA
____________________________________________________________
Prof. M.e Fabiano Fagundes
Orientador
Centro Universitário Luterano de Palmas – CEULP
____________________________________________________________
Prof. M.e Madianita Bogo
Centro Universitário Luterano de Palmas – CEULP
____________________________________________________________
Prof. M.e Fernando Luiz Oliveira
Centro Universitário Luterano de Palmas – CEULP
Palmas – TO
2017
AGRADECIMENTOS
Agradeço primeiramente a Deus! Sem ele, jamais chegaria até aqui, ele me deu forças
para sempre continuar e acreditar que era possível. Há muito caminho pela frente, mas tenho
certeza que ele me fortalecerá a cada desafio, A ti sou grato Senhor!
A minha esposa e parceira Angélica, pela paciência de esperar comigo esses anos, pela
compreensão das várias noites que não lhe dei a atenção merecida. Por me representar nas
diversas reuniões familiares que não pude ir. Agradeço-te por sempre me dar forças quando
estive fraco, por me dar ânimo quando o desânimo bateu a porta, por sempre segurar na minha
mão e dizer que estaria comigo, por tudo isso e muito mais obrigado!
Aos meus pais Maria Aparecida Fritsch e José Pedro Fritsch, por toda dedicação,
carinho, amor, atenção, paciência e incentivo. Obrigado por sempre acreditarem em mim, e
me apoiarem nas minhas jornadas. Vocês são o meu maior exemplo de vida, amo vocês.
Ao meu filho Vitor, meu grande companheiro de aventuras, que sempre soube
compreender que teria que ir dormir à noite antes de seu pai chegar da faculdade, que em
alguns dias ele teria que brincar sozinho, pois, seu pai tinha uma “tarefa da escola” para
entregar no dia seguinte.
Ao meu mestre Fabiano Fagundes, alguém que levarei para sempre em meu coração
com muito carinho. Mestre obrigado pelos sábios conselhos, pelos grandes “puxões de
orelha”, que me deu quando precisei. Obrigado por não só acreditar no meu potencial, mas
por me ensinar a usá-lo, auguro que a vida lhe seja repleta de bons frutos. Não me esquecerei
de suas aulas carregadas de momentos descontraídos, que permitiu a nós, aprendermos algo
difícil de forma super fácil. Também guardarei em minhas memórias as suas expressões ou os
seus gestos de nervosismos, ansiedade e aflição quando acompanhava seus “filhos”
(orientandos) durante as jornadas, eu tinha aquela sensação que você estava sentindo junto
com eles aquelas emoções, e claro sempre na torcida pelo nosso sucesso. Obrigado meu
orientador ou como chamávamos carinhosamente de “pai”.
Aos professores da banca: M.e Madianita pelo carinho que sempre me tratou, pela
dedicação em ser uma excepcional professora, pela sua paciência demonstrada nas aulas, que
foram explicadas tão detalhadamente, ponto a ponto, algo realmente incrível. Guardarei muito
bem seus completíssimos manuais de passo a passo; ao professor M.e Fernando pelas
correções sugeridas ao meu TCC, suas palavras foram de grande estima e sabedoria; Ao D.r
Pierre por ter contribuído no decorrer do meu estágio.
Ao M.e Jackson pelo tato demonstrado em sempre transformar suas aulas em
momentos ricos, tanto em aprendizagem quanto em interações com os colegas de turma, a
simplicidade de um verdadeiro oráculo de informações. Sempre lhe admirei, pelas suas
impecáveis ministrações de aulas, juro que sempre pensei que em algum lugar, havia uma
folha escondida com a explicação da sua aula redigida.
A professora M.e Parcilene pelas aulas que me fizeram ver a área da tecnologia por
outra lente, foram momentos riquíssimos, que com certeza vão fomentar em mim, um
profissional melhor. Agradeço também pela paciência e o bom humor, mostrado nas
correções dos longos textos que escrevi em minhas respostas.
Ao Paulinho que sempre me incentivou durante a faculdade, me apoiando sempre que
precisei, seja pelas incontáveis broncas por não iniciar logo a faculdade, ou por sempre estar
ao meu lado como um grande amigo.
Também aos grandes amigos que fiz que no decorrer do curso, amizades que por mais
que foram se desencontrando durante os períodos da faculdade, se mantém fora dela, a vocês
meu grande obrigado. E ainda aos amigos da vida que de forma direta ou indireta colaboraram
para a conclusão do curso.
“Pedras no caminho? Eu guardo todas. Um dia
vou construir um castelo.”
(Nemo Nox)
RESUMO
FRITSCH, Maicon Juliano. Construção de um framework para realização da leitura da
frequência cardíaca utilizando o Kinect v2.0. 2017. 91 f. Trabalho de Conclusão de Curso
(Graduação) – Curso de Sistema de Informações, Centro Universitário Luterano de Palmas,
Palmas/TO, 20171.
Este trabalho apresenta o desenvolvimento de um framework que visa realizar a leitura da
frequência cardíaca sem a necessidade da utilização de fios ligados diretamente ao corpo. O
framework disponibiliza uma biblioteca para ser utilizada por outras ferramentas, permitindo
que outras aplicações se utilizem das funcionalidades deste framework e integrem a leitura da
frequência cardíaca às suas aplicações. Os resultados obtidos pelo processamento realizado no
framework são demostrados em uma aplicação que consumirá este framework. Os dados
obtidos podem ser utilizados no futuro para uma validação deste método, comparando-o aos
métodos de medição da frequência cardíaca convencionais. Desta forma, a leitura da
frequência cardíaca se dará por meio do processamento de imagem realizada pelo Kinect,
permitindo às aplicações que utilizem esse framework a possibilidade de serem desprovida da
necessidade da utilização de cabos, fios ou qualquer contato físico diretamente ligado ao
corpo para seu funcionamento. Outra característica de grande relevância deste framework é o
fato de prover uma opção de exame não invasivo, sem dor e 100% seguro para seus
utilizadores.
Palavras-chave: Frequência cardíaca, RGB, Framework, Kinect 2.0, Rastreamento de Face.
LISTA DE FIGURAS
Figura 1 – Veias e Artérias da face .............................................................................. 17
Figura 2 – Círculos Cromáticos RGB .......................................................................... 18
Figura 3 – Modelo seletor de cores RGB típicos de software gráfico. ........................ 18
Figura 4 – Variação da cor vermelha na face .............................................................. 20
Figura 5 – Ilustração do uso de filtros para imagens espectro RGB ........................... 21
Figura 6 – ICA - Análise de Componente Independente ............................................. 21
Figura 7 – Método de análise do algoritmo JADE para extração das variações ......... 22
Figura 8 – Arquivo csv contendo os dados capturados pelo sensor ............................ 23
Figura 9 – Plotagem do gráfico obtido pela transformada de Fourier ......................... 25
Figura 10 – Resumo da metodologia ........................................................................... 26
Figura 11 – Etapas do funcionamento do framework. ................................................. 27
Figura 12 – Localização dos sensores do Kinect 2.0 ................................................... 29
Figura 13 – Adaptador Kinect para PC. ....................................................................... 30
Figura 14 – Arquitetura de desenvolvimento do framework ....................................... 32
Figura 15 – Fluxo do processamento do framework ................................................... 33
Figura 16 – ROI - Região de interesse da face ............................................................ 34
Figura 17 – Tela da aplicação modelo ......................................................................... 43
Figura 18 – Arquitetura de desenvolvimento do framework ....................................... 44
Figura 19 – Comunicação entre o framework, o Kinect e a aplicação. ....................... 45
Figura 20 – Protocolo de utilização do Framework ..................................................... 47
Figura 21 – DLL’S para utilização do framework ....................................................... 47
Figura 22 – Pastas e arquivos disponibilizados para utilização do framework ........... 49
Figura 23 – Tela da aplicação FACE BASIC HD ....................................................... 52
Figura 24 – Posições do ROI para leitura da frequência cardíaca na face................... 53
LISTA DE CÓDIGOS
Código 1 - Script utilizado no framework para uso do JADE e a FFT ...................... 36
Código 2 - Continuação do script em R ..................................................................... 37
Código 3 - Inicialização da enginer para executar o script em R .............................. 38
Código 4 - Função para processamento dos dados .................................................... 39
Código 5 - Configuração do framework .................................................................... 41
Código 6 - Método para inicializar os sensores do Kinect ......................................... 41
Código 7 - Método observable ................................................................................... 46
Código 8 - Método para inicializar e sincronizar com o framework ......................... 49
LISTA DE TABELAS
LISTA DE ABREVIATURAS E SIGLAS
IV Infravermelho
SDK Software Development Kit
RGB Red, Green, Blue
XAML eXtensible Application Markup Language
BPM Batimento por Minuto
ECG Eletrocardiogramas
ICA Independent Component Analysis
JADE Joint Approximate Diagonalization of Eigen-matrices
BSS Blind Source Separation (Separação cega de fontes)
ROI Region Of Interest (Região de interesse)
FlexICA Flexible Independent Component Analysis
AMUSE Algorithm for Multiple Unkown Signals Extraction)
CSV Comma Separated Values
DDL Dynamic-link library
SUMÁRIO
1 INTRODUÇÃO .................................................................................................... 13
2 REFERENCIAL TEÓRICO ............................................................................... 16
2.1 FREQUÊNCIA CARDÍACA ............................................................................. 16
2.2 RUBOR FACIAL ............................................................................................... 17
2.2.1 RGB ................................................................................................................. 17
2.2.2 Captura dos dados relativos à frequência ...................................................... 19
2.2.3 Separação do RGB .......................................................................................... 21
2.2.4 JADE e os algoritmos para separação cega .................................................. 22
2.2.5 Transformada Rápida de Fourier ................................................................... 24
3 MATERIAIS E MÉTODOS OU METODOLOGIA ........................................ 26
3.1 MÉTODOS ......................................................................................................... 26
3.2 MATERIAIS ...................................................................................................... 28
3.2.1 Local e período de realização do estudo ........................................................ 28
3.2.2 Hardware ........................................................................................................ 28
3.2.3 Sensor do Kinect 2.0. ...................................................................................... 28
3.2.4 Adaptador Kinect para Windows .................................................................... 29
3.2.5 Software .......................................................................................................... 30
3.2.6 SDK Microsoft Kinect v2.0 ............................................................................. 30
3.2.7 Linguagem de programação e softwares ........................................................ 31
3.3 ARQUITETURA DO FRAMEWORK ............................................................... 32
4 RESULTADOS E DISCUSSÃO ......................................................................... 33
4.1 fluxo de processamento ...................................................................................... 33
4.2 Mecanismos de utilização do algoritmo jade e da fft ......................................... 35
4.2.1 Cálculo do algoritmo JADE ........................................................................... 36
4.2.2 Calculo da transformada de Fourier .............................................................. 36
4.2.3 O script em R .................................................................................................. 38
4.2.4 Execução do script em R ................................................................................. 39
4.2.5 Configuração dos separadores de milhares ................................................... 40
4.2.6 Inicialização dos sensores do Kinect .............................................................. 41
4.3 DESENVOLVIMENTO DA APLICAÇÃO MODELO .................................... 42
4.3.1 Arquitetura da aplicação modelo ................................................................... 44
4.4 Comunicação entre o framework e a aplicação modelo ..................................... 44
4.5 Protocolo para uso do framework ....................................................................... 47
5 CONSIDERAÇÕES FINAIS .............................................................................. 51
REFERÊNCIAS ........................................................................................................... 54
APÊNDICES ................................................................................................................ 58
APÊNDICE A – Codigo fonte FrameWork ................................................................. 59
APÊNDICE B - Script em R - ProcessaFrequenciaCardiaca ....................................... 74
APÊNDICE C - Aplicação Modelo - DashBoardCompleta.cs, .................................... 75
APÊNDICE D - Aplicação Modelo - DashBoardCompleta.Xaml .............................. 85
1 INTRODUÇÃO
O uso da tecnologia como aliada da área da saúde tem se mostrado de extrema valia
para a sociedade. Com a evolução tecnológica novos recursos são empregados na área da
saúde e sua influência permitiu o surgimento de diversos novos exames, cirurgias e
tratamentos até então inexistentes.
A constante e acelerada evolução tecnológica observada nos últimos anos remodelou a
forma de se produzir aplicações, despertando novas necessidades ou tendências de
desenvolvimento. Desenvolver softwares que permitam uma maior interatividade com seus
usuários se tornou uma tendência a ser explorada. Desta forma, novas tecnologias estão sendo
construídas permitindo que novos ambientes surjam com foco em produzir aplicações mais
imersivas, e que propiciem uma maior interação entre homem e máquina.
Não demorou para a tecnologia transpor algumas barreiras existentes, como a
necessidade de mouses, teclados ou cabos para controlar dispositivos eletrônicos. Novos
dispositivos aptos a reconhecer comando de voz, de gestos da mão, do corpo ou até mesmo
toques com a ponta dos dedos trouxeram uma nova forma de se interagir com dispositivos
eletrônicos e deram uma maior liberdade aos movimentos dos seus utilizadores.
O passo evolutivo seguinte foi o desenvolvimento de dispositivos que captam não só
os movimentos corporais, mas informações dos atributos e características físicas dos seus
utilizadores. O Kinect foi um dos dispositivos precursores para captura de movimentos
corporais além de características físicas do seu usuário.
O Kinect foi patenteado pela Microsoft e desenvolvido em conjunto com a empresa
PrimeSense, que inicialmente o desenvolveu para ser utilizado no videogame Xbox 360
(Microsoft, 2010). Entretanto, não demorou muito para que o Kinect despertasse o interesse
para sua utilização em diversas outras áreas.
Se, por um lado, a crescente expansão no uso do Kinect fora do ambiente de
videogames elevou consideravelmente a quantidade de aplicações ou ferramentas
desenvolvidas para utilizar os recursos do Kinect. Por outro lado, tornou-se perceptível a
quantidade de aplicações que foram desenvolvidas para realizar uma mesma tarefa,
ocasionando um grande desperdício de esforço e tempo, ao se codificar várias vezes uma
mesma aplicação.
Desenvolver aplicações demanda um determinado nível de esforço e de tempo para
codificar toda a aplicação, geralmente levam-se dias, meses ou até mesmo anos para concluir.
Uma necessidade dos desenvolvedores atuais é desenvolver códigos que possam ser
14
reutilizados por outros sistemas, prática que poupa muito tempo aos desenvolvedores que não
necessitam codificar soluções ou tarefas já existentes. O foco desta prática é fornecer forma
de reuso que vai além de apenas código, mas engloba toda uma aplicação e seus resultados.
Os frameworks, dentre outras utilidades, permitem que programadores integrem em suas
aplicações recursos de outras aplicações sem a necessidade de conhecerem a fundo todo o
funcionamento daquele sistema. Permitir que outros sistemas possam integrar a leitura da
frequência cardíaca em seus recursos, traz a possibilidade de acompanhar a frequência
cardíaca de seus utilizadores e a reagirem a esses dados quando necessário.
Considerando essa necessidade de se reaproveitar código, facilitando o trabalho dos
desenvolvedores, aliado a necessidade do Grupo de Estudos e Pesquisa em Tecnologia, Saúde
e Qualidade de Vida – GEPETS em utilizar informações referentes à frequência cardíaca em
seus projetos, este trabalho almejou obter resposta para o seguinte problema de pesquisa: É
possível desenvolver um framework que permita a outras aplicações utilizarem os recursos da
leitura da frequência cardíaca disponíveis no sensor Kinect?
A hipótese versou sobre a premissa de que utilizando as ferramentas do Kinect, aliadas
à sua SDK, é possível desenvolver um framework que possibilite compartilhar com outras
aplicações os resultados obtidos pelo sensor Kinect referente à leitura da frequência cardíaca.
Isto posto, o objetivo geral deste trabalho é desenvolver um framework que permita a
utilização, por outras aplicações, dos dados referentes à captura da frequência cardíaca do
corpo humano sem a necessidade do uso de contato físico utilizando o sensor do Kinect.
Logo, objetivou-se especificamente:
● apresentar informações sobre frequência cardíaca, equipamentos tradicionais para
realização da leitura da frequência cardíaca, bem como os parâmetros necessários para
realizar as suas medições;
● apresentar os métodos computacionais de rastreamento de face, de separação de cores
em imagens de vídeo, de análise de variação de cores, que se fazem necessários para
detecção da frequência cardíaca por vídeo;
● apresentar informações sobre o algoritmo de rastreamento de face do SDK do Kinect
v2;
● desenvolver um framework para realizar a leitura da frequência cardíaca através do
Kinect;
● desenvolver uma aplicação para utilizar o framework e exibir os dados obtidos;
O trabalho foi estruturado da seguinte forma: o capítulo 2 apresenta o referencial
teórico dividido em: frequência cardíaca, que apresentar informações sobre os batimentos
15
cardíacos; rubor facial que norteia o conceito para aplicação da técnica de captura dos dados
referente à frequência cardíaca; separação das cores utilizando o ICA (Independent
Component Analysis) ou Análise de Componentes Independentes; o algoritmo JADE (Joint
Approximate Diagonalization of Eigen-matrices), que é utilizado para analisar a variação da
intensidade das cores, provendo dados para a transformada rápida de Fourier, que fornece um
espectro de potência desta variação em função do ciclo cardíaco.
No capítulo 3 é apresentada a metodologia bem como os materiais utilizados no
desenvolvimento deste trabalho. Em seguida o capítulo 4 exibe os resultados obtidos, e como
foi o processo de desenvolvimento tanto do framework quanto da aplicação modelo para
consumo do framework. O Capítulo 5 relaciona as conclusões deste trabalho, e por fim as
referências.
16
2 REFERENCIAL TEÓRICO
Muitos sistemas poderiam se beneficiar obtendo os dados da frequência cardíaca dos
seus usuários. Essas informações permitiriam acompanhar e reagir de forma adequada de
acordo com os sinais referentes ao estado de saúde dos seus usuários, seja em aplicações
como jogos da plataforma Xbox, sistemas da área da saúde ou qualquer outra aplicação que
necessite avaliar informações referentes à frequência cardíaca.
2.1 FREQUÊNCIA CARDÍACA
A frequência cardíaca é o termo utilizado para definir o número de batimentos do
coração, sendo medida em bpm (batidas por minuto). Esta medida representa o esforço que o
coração faz para suprir as necessidades energéticas do corpo. Segundo Robergs e Roberts
(2002), os valores dos batimentos cardíacos variam de no mínimo 50 bpm ou até os valores
máximos que podem atingir 200 bpm.
Os batimentos cardíacos podem ser divididos em ciclos, um ciclo correspondente ao
período da contração inicial (sístole) até o relaxamento do músculo cardíaco (diástole)
voltando ao estado de repouso (STEIN, 2001). Na sístole, ou contração inicial, os ventrículos
se contraem impulsionando o sangue para as artérias, o sangue impulsionado faz pressão
sobre as paredes dos vasos sanguíneos, com a pressão os vasos se expande, logo em seguida
ocorre à diástole, na qual o músculo cardíaco relaxa e o sangue volta liberando a pressão nos
vasos que se contraem (STEIN, 2001).
O movimento de expansão e contração dos vasos é denominado de pressão ou pulso
arterial, que costuma sofrer variações ocasionadas pela alteração no ritmo dos batimentos
cardíacos (STEIN, 2001). Diversos fatores são determinantes para a variação nos batimentos
cardíacos, como atividade física de alta intensidade; distúrbios físicos; fatores emocionais
como nervosismo, ansiedade ou stress.
Ao ser bombeado pelo coração o sangue flui por todo o nosso corpo, fluindo desde os
membros inferiores até os membros superiores como a cabeça, irrigando inclusive a pele da
face. O fluxo de sangue que circula pela face costuma ser imperceptível aos nossos olhos,
entretanto algumas situações permitem observar este fenômeno de aumento de fluxo
sanguíneo, como a vermelhidão que ocorre na face dos praticantes de atividades físicas
intensas ou a timidez que costuma alterar a coloração da face. Essa alteração da cor da face
devido a aumento da irrigação sanguínea é que se chama de rubor facial.
17
2.2 RUBOR FACIAL
Rubor facial é uma vermelhidão que acomete a face e geralmente ocorre
espontaneamente, sem nenhuma causa aparente, ou em resposta a estímulos de estresse,
atividade física intensa ou timidez. É uma condição involuntária, portanto, não é possível
controlá-la. A vermelhidão gerada na face torna-se perceptível dado à quantidade de veias e
vasos sanguíneos que compõem a face do corpo humano, conforme figura 01.
Figura 1 – Veias e Artérias da face
Fonte: Adaptado de http://www.flickr.com/photos/patrlynch/450142019
Nem sempre o rubor facial é visível aos nossos olhos, entretanto isso ocorre
continuamente no nosso corpo: o fluxo de sangue no organismo provocado pelos batimentos
do coração causa uma sutil variação na cor da face imperceptível aos nossos olhos. De acordo
com Verkruysse, Svaasand e Nelson (2008), embora essa variação de cor seja invisível a olho
nu é possível ser explorada para leitura da frequência cardíaca.
Para realizar a leitura desta variação de cor é necessário o uso de uma câmera RGB.
Conforme Hernández et al. (2016), as imagens obtidas com esse tipo de câmeras são
comumente conhecidas como imagens a cores, pois as imagens geradas por estas câmeras são
semelhantes às imagens que são percebidas pelo olho humano.
2.2.1 RGB
O termo RGB é um acrônimo para red (vermelho), green (verde), blue (azul) e a
escolha das cores vermelho, verde e azul denominadas de cores primárias está relacionada
com a fisiologia do olho humano. Poynton (2003) descreve que a teoria do modelo de cor
RGB é baseada na percepção humana das cores.
De acordo com Rocha (2010, p. 2):
18
2.2.1.1.1.1 Nossos olhos possuem na retina 2 tipos de sensores: os cones e os bastonetes; os cones permitem a
percepção das cores e os bastonetes a percepção dos tons de cinza. Nós herdamos de nossos
antepassados a visão tricrômica, ou seja, vemos todas as cores baseadas em apenas três: o
vermelho, o azul e o verde[...].
Segundo Poynton (2003), o RGB é um modelo aditivo em que as cores vermelho,
verde e azul podem ser combinadas a fim de se obter uma cor diferente denominada de
secundária. As diversas combinações possíveis entre cores primárias e secundárias resultam
em uma ampla variedade de cores, como ilustra a figura 2.
Figura 2 – Círculos Cromáticos RGB
Fonte:http://upload.wikimedia.org/wikipedia/commons/thumb/c/c2/AdditiveColor.svg/220px-
AdditiveColor.svg.png
A figura 2 apresenta no círculo cromático que, combinando o vermelho ao verde
obtém-se o amarelo; vermelho e azul resulta no magenta; verde com azul o ciano; e a mistura
de todas as três cores primárias em conjunto produz branco.
Bessani (2012, p. 36) define imagem colorida como sendo “uma composição de três
cores primárias em uma única imagem, onde a cor de cada pixel será uma tripla ordenada de
intensidade (R, G, B) ”. Para indicar a intensidade (quantidade) de vermelho, de verde e azul
serão adicionados na mistura, a quantidade de cada cor pode variar, por padrão é utilizado
para determinar a intensidade das cores os valores de 0 a 255, conforme figura 3.
Figura 3 – Modelo seletor de cores RGB típicos de software gráfico.
Fonte: http://upload.wikimedia.org/wikipedia/en/thumb/1/12/RGB_sliders.svg/220px-RGB_sliders.svg.png
A figura 3 ilustra um seletor de cores típico em softwares gráficos, para alcançar a cor
representada na figura 3 foi necessário informar a tripla ordenada (213,111,56).
19
2.2.2 Captura dos dados relativos à frequência
A técnica para capturar os dados relativos aos batimentos cardíacos é baseada na
observação dos efeitos ocasionados pela circulação do sangue no corpo, o coração ao bater
impulsiona o sangue pelas veias que circulam por todo o corpo até chegarem à face, o volume
de sangue na face faz com que mais luz seja absorvida nesta região e, consequentemente, uma
alteração no brilho é captada pela câmera RGB.
O fluxo sanguíneo na face provoca um leve rubor na região da face, que é
imperceptível a olho nu, mas que ocorre repetidamente a cada batida do coração ou ciclos. Ao
se combinar esta alteração no brilho com alteração na intensidade da cor vermelha na pele da
face levando em consideração os tempos de cada batida do coração ou ciclo, é possível,
através de propriedades matemáticas calcularem a frequência do batimento cardíaco.
Para realizar a leitura da variação da intensidade das cores da face, utiliza-se o fluxo
de dados obtidos da câmera RGB em conjunto com algum algoritmo de rastreamento de face,
os algoritmos de rastreamento de face utilizam em seus métodos, uma grande diversidade de
técnicas para realizar o rastreamento de faces humanas, inicialmente optou-se pelo algoritmo
de Viola e Jones (2004), um algoritmo composto por três partes, de acordo com Araújo (2010,
p 31-32):
2.2.2.1.1.1 A primeira delas é a representação da imagem em um espaço de características baseadas nos
filtros de Haar. Isto é feito com o auxílio da “imagem integral”. A segunda é a montagem de um
classificador baseado em Boosting capaz de selecionar as características mais relevantes. Por fim é
feita uma combinação em cascata destes classificadores de modo garantir bom desempenho e
velocidade de processamento.
Inicialmente foi escolhido o algoritmo Viola e Jones (2004) para o framework, a
escolha do algoritmo se deu pelo grande volume de publicações científicas acerca dos bons
resultados obtidos pelo algoritmo em questão. Segundo Viola e Jones (2004), o algoritmo leva
em torno de 15 quadros por segundo para detectar face humanas. Entretanto durante a fase de
desenvolvimento do framework o uso do algoritmo OpenCV se tornou uma opção inviável
para a arquitetura deste framework, não se adaptando a arquitetura planejada no decorrer deste
trabalho, como substituto para o rastreamento de face foi utilizado o próprio recurso de
rastreamento de face do SDK do Kinect v2.
Uma das necessidades de se utilizar o rastreamento da face é determinar o ROI (region
of interest - região de interesse) uma região na face que servirá para fornecer o local para
análise dos dados utilizados para calcular a intensidade da variação das cores, o cálculo leva
em consideração a variação das cores ao longo do tempo. A figura 4 exemplifica, para uma
melhor compreensão, a variação do vermelho na face.
20
Figura 4 – Variação da cor vermelha na face
Fonte: WU et al. (2012, p1).
A figura 4 foi gerada através do uso de um algoritmo desenvolvido por Wu et al.
(2012). O algoritmo, batizado de Eulerian video magnification, conta com inúmeras
possibilidades de usos e permite, por exemplo, amplificar a variação da cor vermelho durante
o ciclo do batimento cardíaco. A imagem original - parte (a) - foi modificada ampliando em
várias vezes a variação do vermelho na face e o resultado exibido na parte (b).
Segundo o autor, para obter este resultado o algoritmo decompõe o vídeo em uma
sequência temporal de quadros em diferentes bandas de frequência espacial denominado x,
em seguida aplica um mesmo filtro temporal a todas as bandas. As bandas espaciais filtradas
são então amplificadas por um determinado fator denominado de α e adicionado de volta ao
sinal original para gerar o vídeo modificado e amplificado, conforme figura 4.
É importante ressaltar que o algoritmo Eulerian video magnification não será utilizado
neste trabalho e foi apresentado apenas como ilustração didática para o rubor facial decorrente
do aumento de fluxo sanguíneo na face.
Para realizar a leitura da frequência cardíaca, considera-se a variação do vermelho na
face ao longo do tempo e, para realizar este processo, é necessário realizar a separação das
cores do ROI. Dado que como visto anteriormente as cores são formadas por uma tripla
ordenada de intensidades entre o vermelho, verde e azul, desta forma, a variação na
intensidade de qualquer uma dessas cores primárias poderia influenciar na intensidade do
vermelho. Diante disso é necessário realizar a separação das cores para analisar cada variação
de cor de forma independente uma da outra. A próxima subseção detalha o processo de
separação das cores.
21
2.2.3 Separação do RGB
Em imagens coloridas as cores RGB estão misturadas na imagem. Segundo Hernández
et al. (2016), é possível isolar os pixels de uma imagem separando-as por determinada cor,
conforme figura 5.
Figura 5 – Ilustração do uso de filtros para imagens espectro RGB
Fonte: Adaptado de Hernández et al. (2016).
Segundo Leite (2014), para realizar a separação dos dados do R, G e B o processo a
ser utilizado é uma técnica bem difundida na computação e recebe o nome de separação cega
ou ICA (Independent Component Analysis) - Análise de Componentes Independentes. Leite
(2014) definiu que “a separação cega ou o problema da separação de sinais (fontes) consiste
na recuperação de um conjunto de sinais ou fontes desconhecidas a partir de observações
feitas por sensores das misturas destes sinais”.
“Realizar a separação cega de fontes é fazer uma análise dos componentes
independentes. Isto significa dizer que o valor de um componente não fornece nenhuma
informação sobre o valor de qualquer outro” (MOURA, 2013). A figura 6 ilustra o uso do
ICA na separação das cores.
Figura 6 – ICA - Análise de Componente Independente
Fonte: Adaptado de Yokota (2012)
22
A figura 6 exibe o modelo de uso do ICA para separação cega, onde uma imagem
colorida e analisada a fim de se obter as cores presentes na imagem colorida. Para realizar a
separação cega utilizam-se algoritmos como o JADE (Joint Approximate Diagonalization of
Eigen-matrices), que será descrito na seção seguinte.
2.2.4 JADE e os algoritmos para separação cega
Diversos algoritmos podem ser utilizados para realizar esta separação cega, como
(LEITE, 2014), o algoritmo AMUSE (Algorithm for Multiple Unkown Signals Extraction),
Algoritmo FlexICA (Flexible Independent Component Analysis). Para este trabalho foi
escolhido o algoritmo JADE, um algoritmo algébrico que utiliza quantidade estatística e que
explora a independências dos componentes de uma mistura aleatória.
Segundo Leite (2014), o JADE tem como característica reduzir os dados de entrada
observados em um pequeno conjunto ou matriz de estatísticas. Os dados desta matriz podem
então ser utilizados por modelos matemáticos a fim de se analisar as informações ali
presentes. O uso do algoritmo JADE neste trabalho fornece para cada uma das cores
analisadas uma matriz contendo suas respectivas variações de intensidade ao longo do tempo,
conforme figura 7.
Figura 7 – Método de análise do algoritmo JADE para extração das variações
Fonte: adaptado de Poh, Mcduff e Picard (2010), p6.
A figura 7 exibe o método de extração das informações das variações obtidas pelo
algoritmo JADE, cada quadro da imagem de vídeo (letra a) capturada pela câmera RGB é
enviada para ser analisada pelo algoritmo, que inicialmente realiza a decomposição dos canais
23
nas cores primárias R, G e B (letra b), o algoritmo extrai os dados brutos da variação da
intensidade das cores (letra c), as informações obtidas passam por um processo de
normalização, processo que busca eliminar a leitura de dados incorretos que podem ocorrer
caso o framework deixe de reconhecer a face durante algum tempo, ou por algum travamento
do computador ou do sensor Kinect d). Os dados já normalizados são armazenados em um
arquivo CSV que é uma sigla inglesa para comma separated values, ou em português, valores
separados por vírgula, neste arquivo são armazenadas as seguintes informações: o tempo da
leitura em milésimo de segundo; a variação de intensidade da cor azul; da cor verde; do
vermelho e alfa, conforme figura 8.
Figura 8 – Arquivo csv contendo os dados capturados pelo sensor
No figura 7 é possível notar a presença de um elemento novo, o alfa que pode ser visto
como uma quarta variante nas definições de cores R G B e A(alfa), o elemento alfa e
utilizado para representar o quão transparente é o pixel, onde o valor mínimo
significa transparência total e o máximo significa totalmente opaco. Este arquivo contém as
variações e serão processados a fim de se obter a frequência cardíaca, os dados armazenados
nesse arquivo CSV serão transformados em sinais para análise em outro domínio, o domínio
24
de frequências que são similares às obtidas no ECG, para isso é utilizado a FFT (Fast Fourier
Transform - Transformada Rápida de Fourier).
2.2.5 Transformada Rápida de Fourier
A Transformada de Fourier foi proposta pelo matemático e físico francês Jean Baptiste
Joseph Fourier (1768-1830). Fourier provou que qualquer forma de onda ECG pode ser
representada através de uma somatória de senóides e cossenóides de diferentes frequências
(FECHINE, 2010). Joseph Fourier mostrou que onde senoidais podem servir para descrever
qualquer tipo de função. A transformada de Fourier de uma função f(x) é definida como:
𝐹(𝑘) = ∫ 𝑓(𝑥)𝑒−2𝜋𝑖𝑘𝑥∞
−∞
𝑑𝑡
Segundo Carvalho (2013), a transformada de Fourier consiste em multiplicar o sinal
inteiro por senos e cossenos de diferentes frequências, aplicando a integral de cada um desses
produtos. Para Carvalho (2013, p25) “[...] Pode-se afirmar que o que se está fazendo com esse
processo nada mais é que uma mudança de base”, ou seja, “[...] Ao se multiplicar o sinal por
uma senóide e a seguir calcular a integral desse produto, está sendo calculada a projeção do
sinal sobre aquela função”.
A Transformada de Fourier permite conseguir uma representação completa desse
sinal, contendo informações que possivelmente no domínio do tempo inicialmente não
aparecem de forma tão clara, como as informações a respeito do espectro de frequência do
sinal e o comportamento temporal do sinal. Portanto o objetivo da FTT é fornecer
informações sobre a distribuição (variação) da frequência do sinal, observando o
comportamento temporal do sinal.
Segundo Carvalho (2013), pode-se dizer que a transformada de Fourier ao analisar-se
um trecho de uma música, por exemplo, permitiria descobrir diversas informações das notas
que estão sendo tocadas em determinado trecho da música, qual a intensidade média de cada
uma das notas, além da intensidade tocada delas ao longo do tempo.
Para esse trabalho a Transformada Rápida de Fourier será utilizada para transformar os
valores obtidos pelo algoritmo JADE, em um espectro de potência das variações ocorridas.
Segundo Duque (2012, p. 51), o espectro de potência da variação fornece “informações
básicas de como a potência (variação) se distribui como uma função de frequência, onde as
séries de variação são indexadas pelo número de intervalo entre os batimentos”.
25
A figura 9 ilustra uma plotagem do resultado obtido pela transformada de Fourier
nesse gráfico a amplitude do espectro de potência apresenta uma variação de 0 até 0.4
positivos.
Figura 9 – Plotagem do gráfico obtido pela transformada de Fourier
Fonte adaptado: Gambi et al. (2017).
O pico da oscilação foi destacado na figura 9 e informam o local contendo valores
obtidos pela FFT, é através destes valores que poderemos mensurar o bpm, ou seja, a
frequência cardíaca. Entretanto para isso deve-se inicialmente realizar uma parametrização
dos valores obtidos pela transformada de Fourier, fazendo uma relação entre os valores do
espectro de potência e os bpm da frequência cardíaca.
Para exemplificar essa conversão entre os valores obtidos pela transformada e o bpm
(batimento por minuto), os valores do espectro de potência são expressos em Hertz que uma
representação de um sinal periódico expresso em segundos, já os batimentos cardíacos são
expressos em minutos. Para realizar a conversão de Hertz em BPM, utiliza-se a seguinte
fórmula hertz = bpm / 60 ou bpm = hertz * 60, logo 70 bpm corresponde a 1,167 hertz, dado
que, 70/60 = 1,167 hertz.
26
3 MATERIAIS E MÉTODOS OU METODOLOGIA
3.1 MÉTODOS
Para que o presente trabalho tivesse êxito em alcançar os objetivos de implementar um
framework para realizar a leitura da frequência cardíaca bem como o desenvolvimento de uma
aplicação que utilize este framework e apresenta os resultados obtidos, elaborou-se um
desenho de estudo de pesquisa aplicada para delimitar os passos que seriam seguidos. A
figura 10 ilustra a metodologia do trabalho.
Figura 10 – Resumo da metodologia
Iniciou-se (letra a) com uma fundamentação teórica através de pesquisa de artigos
científicos, teses e apresentações de conferências da área da saúde e tecnologia, visando a
descoberta das técnicas utilizadas para realizar a leitura da frequência cardíaca por meio de
vídeo, bem como a escolha de qual método utilizar, visto que existe mais de um método para
realizar a leitura por vídeo.
Optou-se (letra b) pelo uso da técnica de análise do rubor facial usando como objeto
de estudo a variação da cor R (vermelha). Importante ressaltar que além do método escolhido
neste trabalho existem outros métodos que conseguem obter bons resultados como, por
exemplo:
● o estudo de Verkruysse, Svaasand e Nelson (2008), que realiza a leitura optando pela
análise da cor verde que também pode ser utilizada para leitura de informações sobre o
batimento cardíaco;
● o estudo de Balakrishnan, Durand e Guttag (2013), que analisa o leve e involuntário
movimento da cabeça que acontece no momento do fluxo de sangue para a cabeça, o
estudo demonstra que apesar deste movimento ser imperceptível a olho nu é possível
de ser utilizado para realizar a leitura da frequência cardíaca (BALAKRISHNAN;
DURAND; GUTTAG, 2013).
Em seguida (letra c) foram determinadas as etapas para realizar a leitura da frequência,
como podem ser observadas na figura 11.
27
Figura 11 – Etapas do funcionamento do framework.
Na Figura 11 são apresentadas as seguintes etapas:
Na primeira etapa foi realizado o pré-processamento da imagem da câmera RGB,
realizando o reconhecimento de face do Kinect, que foi utilizado para determinar o
local de processamento para a segunda etapa.
A segunda etapa realiza o processamento da separação das camadas de cores do
RGB, para isso, foi realizada a separação cega das cores através do algoritmo
JADE, que atuou em cada quadro da imagem do vídeo capturada pela câmera RGB
do Kinect. Ao final da separação cega das cores, obtém-se três vetores, um para
cada uma das cores R, G e B – Vermelho, Verde e Azul.
Os vetores resultantes continham informações sobre a variação da intensidade das
cores capturada pela câmera RGB do Kinect. Estes valores foram utilizados na
etapa seguinte, de análise de variação de intensidade das cores.
A terceira etapa, a etapa responsável por acompanhar os picos de variação do
vermelho. Para isso, os dados obtidos pelo algoritmo na etapa anterior foram
transformados para um espectro de frequência de sinal através da transformada de
Fourier rápida (FFT), que fornece um espectro de variação por unidade de tempo
(Hertz). Na Etapa 4 é obtido o resultado do cálculo da leitura da frequência
cardíaca em bpm.
Posteriormente (letra d), deu-se início ao estudo do desenvolvimento do framework e
para alcançar o objetivo, foi necessário estudar o funcionamento do framework Vitruvius, um
framework para o Kinect que acelera o desenvolvimento de projetos, disponibilizando um
conjunto de utilitários prontos para uso. Levou-se em consideração questões como
comportamento, acesso a modificações de métodos e a arquitetura do Vitruvius. O
funcionamento do framework será detalhado em outra subseção.
1 Pré-processamento
Reconhecimento da face
2 Processamento
Separação das cores RGB
3 Analise da variação
4 Resultado
28
A próxima etapa (letra e), foi realizado o desenvolvimento de uma aplicação para
utilizar o framework e apresentar os resultados obtidos. A aplicação desenvolvida serviu
também como modelo para o uso dos componentes da interface da aplicação que serão
alimentados pelo framework. A aplicação e os recursos nelas disponíveis serão mais bem
explicados em outra subseção.
3.2 MATERIAIS
3.2.1 Local e período de realização do estudo
O trabalho está sendo desenvolvido como projeto integrante do Laboratório de
Tecnologia em Saúde localizado no Complexo de Informática do Centro Universitário
Luterano de Palmas (CEULP)
3.2.2 Hardware
Em relação aos hardwares que foram utilizados foi disponibilizado pelo Grupo de
Estudos e Pesquisa em Tecnologia, Saúde e Qualidade de Vida (GEPETS) do CEULP, o
Kinect acompanhado de um adaptador para uso do Kinect.
A seção a seguir descreve alguns dos hardwares que compõem o Kinect.
3.2.3 Sensor do Kinect 2.0.
O sensor do Kinect compõe-se, de forma geral, de uma câmera RGB, um sensor de
profundidade (Depth), um acelerômetro e um conjunto de microfones, como pode ser
observado na figura 12. Este conjunto de sensores provê ao Kinect a capacidade de captar os
movimentos tridimensionais, realizar reconhecimento facial e vocal.
29
Figura 12 – Localização dos sensores do Kinect 2.0
Fonte adaptado: Valoriani (2015, p7)
A câmera RGB do Kinect possui uma resolução de 192x1080 com taxa de atualização
de 60 FPS (frames por segundo) (Microsoft, 2014). Esta câmera pode ser utilizada para captar
as imagens coloridas do ambiente, assim, é possível utilizá-la para obter dados referente ao
rubor facial, fornecendo informações referentes a cor de determinado ponto da imagem
captada. O uso dessas informações proveniente da câmera do RGB será necessário para
realizar os procedimentos de análise, processamento e obtenção da frequência cardíaca.
Não é possível acessar diretamente os recursos disponíveis no Kinect como a câmera
de alta resolução, microfones, o sensor de profundidade, reconhecimento de voz e diversas
outras opções disponíveis. Desta forma, é necessário o uso de uma SDK que é responsável
por prover todos os métodos e funções dos recursos do Kinect.
3.2.4 Adaptador Kinect para Windows
Segundo a Microsoft (2014), o adaptador permite conectar o Kinect v2.0 em
computadores rodando o Windows 8 ou 8.1. Este adaptador figura 13, transforma todos os
sensores Kinect v2 – Kinect para Windows v2 e Kinect para Xbox One compatíveis com
computadores. A figura 13 apresenta o adaptador.
30
Figura 13 – Adaptador Kinect para PC.
Fonte: https://blogs.microsoft.com/blog/2014/10/22/microsoft-releases-kinect-sdk-2-0-new-adapter-
kit/#sm.0001nx5rie6e5cteqc61h2nhcow2l
O uso do sensor Kinect por desenvolvedores só se tornou possível com advento deste
adaptador, que permite a comunicação de aplicações com o dispositivo do Kinect. Com ele, as
aplicações podem ter acesso a uma gama de recursos que permitem, entre outras
possibilidades, a realização da leitura da frequência cardíaca.
3.2.5 Software
Com relação aos softwares utilizados, as seções a seguir abordarão em detalhes os
softwares e programas utilizados.
3.2.6 SDK Microsoft Kinect v2.0
O Software Development Kit do Kinect v2.0 para Windows permite criar aplicativos
ou experiências utilizando o Kinect e foi desenvolvido pela Microsoft para que os
desenvolvedores possam programar para o Kinect utilizando linguagens como C++, C#,
Visual Basic ou qualquer outra linguagem .NET (Microsoft, 2014).
Esse kit para desenvolvedores inclui acesso aos recursos dos sensores do Kinect sem
exigir que o programador tenha profundo conhecimento sobre todo o funcionamento dos
sensores. Esta peculiaridade possibilita aos desenvolvedores focar apenas em utilizar os dados
fornecidos pelos sensores.
O SDK da Microsoft fornece o método ColorStream responsável por fornecer acesso
ao fluxo de dados da câmera RGB do Kinect. Através deste método será realizada a captura
dos dados da câmera RGB que servirá para realizar a análise da variação do rubor facial. O
ColorStream fornece imagens de alta resolução e envia quadro a quadro os dados que são
atualizados frequentemente.
31
O Kinect inicialmente foi desenvolvido apenas para uso no console de videogame da
Microsoft o Xbox, pouco tempo depois a Microsoft através de uma parceria desenvolveu um
adaptador para permitir a utilização do Kinect no Windows.
3.2.7 Linguagem de programação e softwares
A seguir elaborou-se uma breve descrição das linguagens e softwares necessários:
● C# (CSharp) - linguagem de programação desenvolvida pela Microsoft para a
plataforma .NET. de tipagem dinâmica e orientada a objeto. O C# foi à linguagem
escolhida para o desenvolvimento do framework e da aplicação;
● Microsoft Windows 8.1 - versão do sistema operacional da Microsoft, utilizado nos
computadores do GEPETS é igualmente disponibilizado no notebook de uso pessoal;
● Microsoft Kinect SDK 2.0 - é o novo kit de desenvolvimento da Microsoft para
aplicações do Kinect 2.0;
● Visual Studio 2016 - IDE oficial da Microsoft destinada a plataforma .NET. Para o
desenvolvimento de softwares, utilizado como IDE para o desenvolvimento do
framework e da aplicação;
● MatLab 2016 - é um sistema que permite a resolução de muitos problemas numéricos
em apenas uma fração do tempo que se gastaria para escrever
um programa semelhante em linguagem, o MatLab possui diversas fórmulas prontas
como, por exemplo, a transformada de Fourier. Este servirá para realização de
diversos testes, para plotagem dos resultados obtidos pelo framework durante a
produção do framework;
● R Tools (RTVS) - o plug-in R Tools para Visual Studio (RTVS) é uma extensão e
possui código-fonte aberto sendo gratuito para Visual Studio 2015 ou superior. O uso
do R Tools permite utilizar a linguagem R em aplicações no Visual Studio, entretanto
ainda não há suporte para interoperabilidade entre o R e outras linguagens como C#
(Microsoft, 2015);
● DLL`s (Dynamic-link library - biblioteca de vínculo dinâmico) são bibliotecas que
contém códigos e dados que podem ser usados por mais de um programa ao mesmo
tempo. Por exemplo, em sistemas operacionais Windows, a DLL Comdlg32 executa
funções comuns relacionadas à caixa de diálogo. Portanto, cada programa pode usar a
funcionalidade contida dessa DLL para implementar uma caixa de diálogo. Isso ajuda
a promover a reutilização de código e o uso de memória eficiente.
● Linguagem R é uma linguagem para computação estatística e gráfica, fornece uma
32
grande variedade de estatísticas (modelagem linear e não linear, testes estatísticos
clássicos, análise de séries temporais, classificação, agrupamento) e é distribuído
gratuitamente sob a Licença Pública Geral GNU (Microsoft, 2015).
3.3 ARQUITETURA DO FRAMEWORK
Para compreender o funcionamento do framework desenvolvido, na figura 14, é
apresentada a arquitetura do mesmo.
Figura 14 – Arquitetura de desenvolvimento do framework
O framework não tem acesso direto aos recursos do Kinect e, isopor isto, faz-se
necessário utilizar o SDK Kinect, uma biblioteca que fornece métodos de acesso aos
componentes do Kinect como câmera, microfone e sensor de profundidade. O framework fará
uso do ColorStream do SDK, método responsável pela geração das imagens coloridas. O
ColorStream do SDK Kinect comunica-se com o hardware através de um driver desenvolvido
pela Microsoft que acompanha o instalador do SDK.
33
4 RESULTADOS E DISCUSSÃO
Neste trabalho foi implementado um framework para realizar a leitura da frequência
cardíaca utilizando o sensor do Kinect, sem a necessidade do uso de cabos ou fios conectados
ao corpo.
Esta seção está dividida em quatro partes. A princípio foi elaborada uma síntese sobre
todo o fluxo de processamento para se obter a frequência cardíaca pelo framework. Em
seguida é exposto o processo de desenvolvimento do framework para realizar a leitura da
frequência cardíaca, bem como a arquitetura de funcionamento do mesmo; segue tratando-se
do desfecho do desenvolvimento da aplicação modelo que irá utilizar o framework e exibir os
resultados obtidos, e posteriormente, apresenta-se um protocolo que descreve o passo a passo
para utilização do framework.
4.1 FLUXO DE PROCESSAMENTO
O fluxo do processamento realizado pelo framework foi divido em 4 partes, a seguir,
será descrito as etapas de processamento necessário para encontrar a frequência cardíaca
através do Kinect, a figura 15 ilustra esse fluxo.
Figura 15 – Fluxo do processamento do framework
Conforme apresentado na Figura 15, na etapa 1 o framework ativa a câmera RGB do
sensor Kinect para captura de vídeo. Na etapa 2 é realizado o rastreamento de face, que é
executado até que alguma face seja detectada pelo Kinect. Para isso o Kinect utiliza
algoritmos de detecção facial disponíveis em sua SDK e, após encontrar a face, é determinada
a região da testa para ser analisada, região essa que será denominada de ROI, conforme a
figura 16.
34
Figura 16 – ROI - Região de interesse da face
A figura 16 exibe uma tarja em azul, ilustrando o ROI, ou seja, a região de interesse da
face.
Na etapa 3 é realizada a captura e a separação das cores RGB presentes no ROI
registradas durante 60 segundos. Após esse tempo é aplicada uma normalização nos dados
capturados para eliminar possíveis valores nulos ou inválidos. Esses dados são armazenados
em um arquivo CSV e, ao final deste processo, o arquivo conterá as informações referentes ao
tempo de captura em milissegundos de cada leitura e as variações das intensidades das cores
R G B e A (alfa) durante os 60 segundos.
Os dados já normalizados serão processados pelo algoritmo JADE, explicado na seção
2.2.4, para obter-se as respectivas variações independentes para cada uma das cores, dado
que, como visto anteriormente, as cores são formadas por uma tripla ordenada de intensidades
entre o vermelho, verde e azul, e a variação na intensidade de qualquer uma dessas cores
primárias poderia influenciar na intensidade de outra cor.
A etapa 4 é responsável por calcular a frequência cardíaca. Os dados processados pelo
algoritmo JADE contêm as variações de intensidade das cores R, G e B. É importante
ressaltar que ainda não é possível extrair a frequência cardíaca desses dados, pois a frequência
35
cardíaca é calculada em relação ao tempo, e medido em bpm’s, medido em minutos, e os
dados das variações das cores estão indexadas pelos ciclos dos batimentos do coração e não
em relação ao tempo, para realizar a indexação destes dados em relação ao tempo e utilizada a
transformada de Fourier (FTT).
O uso da transformada de Fourier permite calcular uma projeção destes dados em
relação ao tempo, que segundo Carvalho (2013), este processo nada mais é que uma mudança
de base em que as variações passam a ser indexadas pelo tempo e contém informações sobre a
distribuição (variação) da frequência do sinal, desta forma, os valores que são resultantes da
FFT expressam a distribuição do sinal das variações em relação ao tempo. A FFT é expressa
em hertz, uma unidade de medida dividida em ciclos, sendo cada ciclo de 60 segundos.
Já os batimentos cardíacos (BPM) são medidos em minutos, ou seja, deve-se realizar
uma conversão dos valores obtidos pela transformada de Fourier de hertz para bpm,
convertendo segundo para minutos. Para realizar a conversão basta multiplicar os valores
obtidos em hertz por 60, exemplo hertz * 60 ou 1,133 * 60 = 80 BPM.
4.2 MECANISMOS DE UTILIZAÇÃO DO ALGORITMO JADE E DA FFT
Para utilização do algoritmo JADE e da FFT é necessária a utilização do plug-in R
Tool do Visual Studio que permite a utilização da linguagem R. O plug-in R Tools para
Visual Studio (RTVS) é uma extensão e possui código-fonte aberto sendo gratuito para Visual
Studio 2015 ou superior. O uso do R Tools permite utilizar a linguagem R em aplicações no
Visual Studio, entretanto ainda não há suporte para interoperabilidade entre o R e outras
linguagens como C# (Microsoft, 2015).
Para poder utilizar o R no framework, a comunicação é realizada através de uma
chamada a um script escrito em R que contém os métodos e funções. Este script recebe os
dados do framework e após realizar todas as operações o resultado é retornado para o
framework. A linguagem R oferece, diversos métodos e funções, as funções que são
importantes para o funcionamento do framework, descritos a seguir:
● o algoritmo JADE, já citado anteriormente, fornece uma análise de determinada
mistura, permitindo conhecer a composição desta determinada mistura, possibilitando
ao framework obter uma matriz das cores presente na imagem capturada pelo Kinect e
suas respectivas variações de intensidades;
● a transformada de Fourier ou FFT, que fornece informações sobre a distribuição
(variação) da frequência do sinal, observando o comportamento temporal do sinal ao
longo do tempo, resultando em um espectro de potência dessa variação expresso em
36
Hertz, esses resultados podem ser convertidos em bpm e retornados ao framework.
4.2.1 Cálculo do algoritmo JADE
O algoritmo JADE, presente na linguagem R, recupera os dados que foram coletados
pelo Kinect e armazenados no arquivo CVS. Esses dados fornecem algumas informações
básicas que servem de parâmetros de inicialização do JADE, como total de registros (leituras)
capturados e o tempo total de processamento, que são processados pelo JADE. O código 1
apresenta a seção para uso do algoritmo JADE.
Código 1 - Script utilizado no framework para uso do JADE e a FFT
No código 1 linha 1 calcula o total de registros, ou seja, total de linhas armazenadas no
arquivo CSV, em seguida na linha 2 calcula-se o tempo total entre o inicio da geração do
arquivo e o final, obtendo-se o tempo total para gerar o arquivo, na próxima linha, a linha 3
descobre-se a taxa de atualização de captura dos dados. A próxima linha, a linha 5 é realizada
a inicialização do algoritmo JADE que é atribuído à variável S.
Na linha 6 é inicializado uma matriz com 4 coluna, após isso, na linha 7 é atribuído os
registros que foram armazenados no arquivo CSV, contendo as informações capturadas pelo
Kinect, o passo seguinte é a execução do JADE na linha 8, informando ao algoritmo que são 4
canais de separação (vermelho, verde, azul, alfa). Cada cor é atribuída a uma variável
separada, conforme as linhas: 9, 10, 11, 12.
4.2.2 Calculo da transformada de Fourier
1 totalRegistros <- (sum(lengths(DadosCapturadosBBS[1])))
2 tempoTotal <- max (DadosCapturadosBBS$nSegundosCorridos)/1000
3 taxaFrame <- totalRegistros / tempoTotal
4 #GET JADE
5 S <- cbind(
DadosCapturadosBBS$nVermelho,
DadosCapturadosBBS$nVerde,
DadosCapturadosBBS$nAzul,
DadosCapturadosBBS$nAlfa)
6 A <- matrix(rnorm(16), ncol=4)
7 J <- S %*% t(A)
8 res<-JADE(J,4)
9 ica1 <- res$S[,1] //red
10 ica2 <- res$S[,2] //green
11 ica3 <- res$S[,3] //blue
12 ica4 <- res$S[,4] //alfa
37
Na prática, para utilizar a FFT, segundo Constantino, Silva e Palaretti (2017), alguns
dados e parâmetros são necessários:
Período ou taxa de amostragem: é o número de pontos (que são capturados pelo sensor
Kinect) por segundo. Para obter esse valor no framework, pega-se o total de tempo
decorrido no arquivo csv, e divide-se pelo total de registros capturado, conforme linha 3
do código 1;
Números complexos: “As rotinas de FFT geralmente trabalham com a representação de
ondas na forma de números complexos, contendo uma parte real e uma parte imaginária”.
(CONSTANTINO; SILVA; PALARETTI, 2017, p 3);
Entretanto apenas aqueles dados que são obtidos “a partir dos espectrômetros contêm
tanto a parte real quanto a parte imaginária. As demais funções que correspondem a sons, etc.,
geralmente são apenas números reais e para a parte imaginária é utilizada uma coleção de
zeros” (CONSTANTINO; SILVA; PALARETTI, 2017, p 3).
O Código 2 realiza a configuração do uso da FFT no framework utilizando o script em
R.
Código 2 - Continuação do script em R
No código 2 linhas 13, 14, 15, 16 é executada uma função interna que prepara os
dados para o uso pela transformada de Fourier nos valores obtidos pelo JADE, esta operação é
realizada para cada das cores (red, green, blue, alfa), conforme código 01 e código 02. Nas
linhas 17,18,19,20 é utilizada a fórmula para calcular a intensidade da frequência para cada
um dos pontos passando os valores reais e os valores da parte imaginária sendo uma coleção
de zeros.
Segundo Constantino, Silva e Palaretti (2017), a saída da função FFT será uma
coleção com a mesma quantidade de pontos que foram fornecidos na entrada e estão em
forma de números complexos (parte real e imaginária). Os autores explicam ainda que a
diferença entre o valor da entrada e o que se obtém na saída é que, agora, na saída, “o número
do ponto contém implicitamente a informação sobre a frequência da onda, e não sobre o
tempo. Teremos assim uma relação entre frequências e as correspondentes intensidades”.
Assim, pode-se obter a intensidade da frequência para cada um dos pontos através da formula
13 ica1_fft <- fft(res$S[,1]) //red
14 ica2_fft <- fft(res$S[,2]) //green
15 ica3_fft <- fft(res$S[,3]) //blue
16 ica4_fft <- fft(res$S[,4]) //alfa
17 pwr1 <- sqrt(Re(ica1_fft)^2+Im(ica1_fft)^2) //red
18 pwr2 <- sqrt(Re(ica2_fft)^2+Im(ica2_fft)^2) //green
19 pwr3 <- sqrt(Re(ica3_fft)^2+Im(ica3_fft)^2) //blue
20 pwr4 <- sqrt(Re(ica4_fft)^2+Im(ica4_fft)^2) //alfa
38
𝐼𝑛𝑡𝑒𝑛𝑠𝑖𝑑𝑎𝑑𝑒(𝑉𝑒𝑡𝑜𝑟) = √𝐼𝑛𝑡𝑒𝑛𝑠𝑖𝑑𝑎𝑑𝑒(𝑟𝑒𝑎𝑙)2 + 𝐼𝑛𝑡𝑒𝑛𝑠𝑖𝑑𝑎𝑑𝑒(𝑖𝑚𝑎𝑔𝑖𝑛𝑎𝑟𝑖𝑜)2. No
framework esta fórmula está sendo utilizada nas linhas 17,18,19,20.
O script em R executa essa formula para cada uma das cores R, G, B e A, como
observador nas linhas 17 (R), 18 (G), 19 (B) e 20 (A), ao final dessa execução obtêm-se as
intensidades das variações de cada cor em Hertz. Esses valores serão então convertidos pelo
framework nas intensidades da frequência cardíaca, ou seja, o BPM. Na subseção seguinte
serão descritos os passos para configurar e executar o script em R.
4.2.3 O script em R
O script em R é responsável por executar as instruções da linguagem R. Para isso é
utilizado uma enginer que roda o script e recebe o resultado, sendo uma ponte de
interoperabilidade em processo para R e C#. Para seu uso foi necessário adicionar ao projeto a
DLL RdoNet.dll. Uma DLL disponibilizada pela Microsoft que permite utilizar a
linguagem R em conjunto com outras linguagens de programação como, por exemplo, C#
(Microsoft, 2015). O código 03 mostra a inicialização da enginer.
Código 3 - Inicialização da enginer para executar o script em R
Após a DLL ser adicionada ao projeto, é necessário inicializar a DLL através de um
enginer no framework, o processo de inicialização e utilização da DLL é descrito no código 3,
na linha 1 é inicializada a enginer, para isso é atribuída a uma variável do tipo REngine uma
instância da enginer linha 1, em seguida é realizada a tentativa de inicialização (linha 2). Na
linha 4 confirma-se se foi inicializado com sucesso, caso negativo as linhas 5 e 6 realizam o
//Atribuo a variável da biblioteca R que será utilizado com o JADE
1 engine = REngine.GetInstance();
//Inicializo o jade
2 engine.Initialize();
3 engine.Evaluate("library('JADE')");
4 if (!m_JADE_Loaded)
{
5 engine.Evaluate("install.packages('JADE',
repos='http://cran.us.r-project.org')");
6 repos = 'https://cran.rstudio.com/bin/windows/
contrib/3.2/JADE_1.9-93.zip')");
7 engine.Evaluate("library('JADE')");
8 m_JADE_Loaded = true;
}
39
download do pacote JADE direto no site da Cran R Studio e refazem a tentativa de inicializar
a enginer.
Agora que o script foi configurado a próxima subseção descreve a forma de execução
do script em R.
4.2.4 Execução do script em R
A seguir será exibido o código referente à execução do script em R. Após gerar o
arquivo CSV este arquivo é enviado ao script para o processamento, conforme código 4.
Código 4 - Função para processamento dos dados
Na linha 2 do código 4, é carregado o arquivo CSV para a enginer. Em seguida na
linha 3 esses dados são enviados para o script em R passando a sua localização. Em seguida o
script é executado e o retorno do processamento é capturado na linha 5 e adicionado a uma
lista de bpm’s.
Para utilizar os métodos, funções e procedimentos faz-se necessário primeiro realizar
algumas configurações para evitar a ocorrência de falhas na execução. A próxima subseção
trata dessas configurações no framework.
private void ProcessaDados(){
//Normaliza o acesso ao diretório da aplicação
1 currentDir = System.Environment.CurrentDirectory.Replace('\\',
'/');
//carrega o arquivo csv gerado contendo as variações do R G B e alfa
2 engine.Evaluate(string.Format("DadosCapturadosBBS <-
read.csv('{0}')",
m_filePath.Replace('\\', '/')));
//passa a localização do script em R que deve ficar dentro da pasta
debug\RScript para execução
3 engine.Evaluate(string.Format("source(
'{0}/RScripts/ProcessaFrequenciaCardiaca_JADE.r')",
currentDir));
//captura os valores retornados do script
4 NumericVector bpmVetor = engine.GetSymbol("prw1").AsNumeric();
//pega o primeiro registro do vetor
5 double bpm = bpmVetor.First();
//adiciona a uma lista o valor do bpm encontrado pelo framework
6 Frequencia.Add(bpm);
}
40
4.2.5 Configuração dos separadores de milhares
Durante o processamento do framework um arquivo CSV é gerado contendo os dados
coletados pelo sensor do Kinect. Os dados presentes neste arquivo são separados por vírgula,
entretanto em algumas regiões como o Brasil utiliza-se o ponto “.” como separador de
milhares e a vírgula “,” para separar os decimais, dessa forma, os valores fracionados no
Brasil são separados por virgula.
Ao realizar a leitura de dados de arquivos tipo CSV, os valores coletados pelo sensor
podem conter partes fracionadas, a parte fracionária corresponde aos dígitos que aparecem
após o separador decimal. Ao usar a vírgula como separador decimal em arquivos CSV pode-
se confundir o framework e gerar erro na leitura, que não poderia determinar quando o valor
após a vírgula é de uma parte fracionária ou um novo registro. Para contornar essa
peculiaridade é necessário configurar o framework para utilizar o ponto como separador de
decimais. O código 5 exibe o processo de configuração.
41
Código 5 - Configuração do framework
No código 5 é realizado a configuração de regionalização no framework, na linha 1 e 2
é determinado o formato para datas e horas, na linha 3 determina-se a quantidade de dígito
após a casa decimal, na linha 5 e configurado o ponto como separador das casas decimais.
Essas alterações, indicadas no código 5 são restritas apenas ao framework não afetando as
configurações das aplicações consumidoras do framework.
A subseção a seguir tratará do método para inicializar os sensores do Kinect.
4.2.6 Inicialização dos sensores do Kinect
Para a inicialização do sensor Kinect, é utilizada a variável m_SensorKinect do tipo
KinectSensor acessada através do getDefault(), este método serve para informar qual
Kinect será feito a ligação, visto que a partir da versão 2.0 da SDK do Kinect é possível
utilizar mais de um Kinect no mesmo computador. O código 6 mostra os códigos com os
passos de inicialização e configuração do Kinect.
Código 6 - Método para inicializar os sensores do Kinect
private static void InicializaParametros()
{
//Configura Parametros para geração do arquivo
1 minhaCultura.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
2 minhaCultura.DateTimeFormat.ShortTimePattern = "HH:mm";
3 minhaCultura.NumberFormat.NumberDecimalDigits = 2;
4 minhaCultura.NumberFormat.NumberGroupSeparator = "_";
5 minhaCultura.NumberFormat.NumberDecimalSeparator = ".";
}
42
No código 6 é inicializado os sensores do Kinect, a linha 2 é inicializado o
m_MultSourceFReader, esta variável permite que várias aplicações acessem o sensor
Kinect ao mesmo tempo, desta forma, tanto o framework quanto as aplicações que utilizem o
framework deverão passar a utilizar esse método para acessar os sensores do Kinect. A Linha
9 inicializa uma classe da SDK do Kinect que é utilizada para realizar o mapeamento das
coordenadas da face após o rastreamento. A Linha 11 é utilizada para receber e tratar os
frames vindos do Kinect a cada nova atualização dos sensores. A linha 13 realiza a ativação
do sensor Kinect. E por fim a linha 16 que é utilizada para carregar a classe ColorFrame que
captura a informação referente a cor do frame na câmera RGB, sendo que este método retorna
à descrição da cor em formato Brga, ou seja, B (azul) R (vermelho) G (verde) A (alfa).
A seção a seguir descreve o desenvolvimento da aplicação modelo que irá utilizar o
framework.
4.3 DESENVOLVIMENTO DA APLICAÇÃO MODELO
Desenvolveu-se uma aplicação para utilizar o framework e exibir os dados obtidos
pelo framework. A tela da aplicação contém um contador regressivo sincronizado com o
framework s que é reiniciado a cada 60 segundos, que é o tempo necessário para realizar o
processamento dos dados capturados e obter a frequência cardíaca. A figura 16 exibe as
informações da tela da aplicação modelo.
private static void InitializeSensor()
{
1 m_SensorKinect = KinectSensor.GetDefault();
2 m_MultSourceFReader = m_SensorKinect.OpenMultiSourceFrameReader(
3 FrameSourceTypes.Body |
4 FrameSourceTypes.BodyIndex |
5 FrameSourceTypes.Color |
6 FrameSourceTypes.Infrared |
7 FrameSourceTypes.Depth);
8
9 m_CoordMapper = m_SensorKinect.CoordinateMapper;
10
11 m_MultSourceFReader.MultiSourceFrameArrived +=
M_MSFReader_MultiSourceFrameArrived;
12
13 m_SensorKinect.Open();
14
15
16 FrameDescription colorFrameDescription = m_SensorKinect.
ColorFrameSource.CreateFrameDescription(
ColorImageFormat.Bgra);
17
}
43
Figura 17 – Tela da aplicação modelo
A figura 17 exibe a tela da aplicação modelo, apresentando as imagens capturadas pela
câmera RGB do Kinect, além das informações obtidas pelo framework. Do lado direito da tela
constam os valores da frequência cardíaca, exibindo a frequência atual, máxima e mínima,
bem como o tempo para a próxima atualização da frequência cardíaca. Na região inferior
apresentam-se um gráfico do monitoramento da frequência cardíaca.
44
4.3.1 Arquitetura da aplicação modelo
Elaborou-se a figura 18 para facilitar a compreensão da arquitetura da aplicação que
utilizará o framework.
Figura 18 – Arquitetura de desenvolvimento do framework
A figura 18 ilustra a arquitetura do framework que demonstra o modo de acesso tanto
do framework quanto da aplicação aos recursos do Kinect. Para isso, ambos acessam a SDK
do Kinect, está SDK é responsável por fornece métodos para utilização dos recursos do
Kinect. O framework ficará rodando em background realizando todo o processamento e
disponibilizando novos dados a cada 60 segundos. A cada 60 segundos o framework atualiza
um vetor que contém uma lista de bpm’s, e desta forma, as aplicações podem utilizar métodos
para realizar buscas periódicas no framework e manter as informações dos bpm’s atualizada.
A subseção a seguir irá detalhar o processo de comunicação entre a aplicação modelo
e o framework.
4.4 COMUNICAÇÃO ENTRE O FRAMEWORK E A APLICAÇÃO MODELO
Para realizar a comunicação entre o framework e a aplicação é utilizada uma thread de
comunicação que realiza a troca de mensagens entre o framework e a aplicação. Após aberta a
45
thread o framework passa a monitorar a frequência cardíaca obtendo uma atualização dessa
frequência cardíaca a cada 60 segundos. A figura 19 ilustra essa comunicação entre o
framework e a aplicação modelo.
Figura 19 – Comunicação entre o framework, o Kinect e a aplicação.
Tanto a aplicação quanto o framework acessam o sensor Kinect através de uma nova
instancia do Kinect Reader, disponível na versão 2.0 da SDK do Kinect. Este método fornece
para cada instancia aberta o acesso ao Kinect Service Source, responsável por permitir que
mais de uma aplicação possa acessar o sensor Kinect. Para isso, tanto o framework quanto a
aplicação modelo, acessam o Kinect de forma independente uma da outra. Para realizar a
troca de dados entre o framework e a aplicação, utiliza-se uma thread que realiza a troca de
mensagens entre as duas.
O uso de threads para troca de mensagens permite manter uma comunicação entre o
framework e a aplicação, entretanto é importante observar que existem mais de uma forma de
se utilizar ou configurar a execução das threads. Para facilitar o uso de threads, bem como a
configuração a aplicação modelo utiliza um método conhecido como observable, esse
método possibilita o uso de threads de uma maneira simples e prática, já que todo processo de
configuração e utilização é realizado automaticamente pelo método observable. O
método pode ser ligado a um temporizador e configurado para executar uma determinada ação
a cada segundo, minuto ou hora. O observable executa uma função chamada
subscribe sempre que alguma condição previamente configurada for atendida.
O observable da aplicação modelo foi programado para buscar atualizações no
framework a cada 1 segundo, ou seja, a cada 1 segundo a thread criada pelo observable
realiza uma nova consulta nas variáveis publicas disponibilizada pelo framework, por se tratar
46
de uma consulta a apenas 3 variáveis, sendo elas: um contador; uma lista contendo as
frequências já capturas; e caso haja dados, a uma lista contendo as informações para exibição
dos gráficos. Essa simples consulta tem um baixo custo computacional, o que tornou viável a
realizar desta consulta com periodicidade de 1 segundo. Código 7 exibe o método
observable.
Código 7 - Método observable
O código 7 exibe a configuração do método observable utilizado para criar uma
scheduler (tarefa agendada) para repetir a cada 1 segundo. O framework disponibiliza alguns
métodos públicos, como o contador regressivo, a frequência atual, a frequência máxima e a
frequência mínima e uma variável para montagem de gráficos. Estes valores são atribuídos as
variáveis da aplicação modelo, mantendo as informações da aplicação sempre sincronizada
com o framework.
1 try
2 {
3 var AcessoAoFramework = Observable
.Interval(TimeSpan.FromSeconds(1))
.ObserveOn(this.Dispatcher)
.Subscribe((x) =>
{
4 lblContador.Text =
FrameWorkVFC.ProcessaFrequencia.m_contador.ToString();
5 lblFrequencia.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaAtual.ToString();
6 lblFrequenciaMaxima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMaxima.ToString();
7 lblFrequenciaMinima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMinima.ToString();
8 if (FrameWorkVFC.ProcessaFrequencia.calculado)
9 {
10 var values = new List<double>();
11
12 values = FrameWorkVFC.ProcessaFrequencia.getGrafico;
13
14 if (values != null)
15 {
16 _sourceData = values.ToArray();
17 }
18 }
19 );
20}
21catch (Exception e)
22{
23 Debug.WriteLine("Erro no funcinamento do framework {0}",
e.Message.ToString());
24}
47
Para utilizar o framework algumas regras e procedimento são necessários. A próxima
seção descreve um protocolo a ser seguido.
4.5 PROTOCOLO PARA USO DO FRAMEWORK
O framework desenvolvido neste trabalho é disponibilizado aos seus usuários através
de uma DLL que será adicionada nas referências da aplicação que irá utilizar o framework.
Essa DLL disponibiliza métodos que permitem a sua utilização, entretanto além de adicionar
a DLL do framework na aplicação, alguns passos são necessários para que tudo funcione
corretamente. A figura 20 foi elaborada para auxiliar neste processo.
Figura 20 – Protocolo de utilização do Framework
No passo 1 deve-se adicionar a DLL do FrameWorkVFC gerada e disponibilizada no
decorrer deste trabalho, além da DLL FrameWorkVFC algumas outras DLL’s devem ser
adicionada a aplicação que utilizará o framework. A figura 21 exibe as essas DLL’s
necessárias
Figura 21 – DLL’S para utilização do framework
48
As DLL’s: DynamicInterop utilizada para a manipulação de arquivos, como o arquivo
CSV; a RDotNeT e RDotNet.NativeLibrary que permitem a utilização da linguagem R e são
instaladas juntamente com o R Tools presente no pacote de instalação do Visual Studio a
partir da versão 2015 e nas versões posteriores; As DLL’s Microsoft.Kinect e
Microsoft.Kinect.Face utilizada para acessar os métodos da SDK do Kinect e para o
rastreamento de face do Kinect, disponibilizadas juntamente com a instalação da SDK do
Kinect.
Além das 5 DLL’s System.Reactive que são utilizadas pelo método Observable e
não acompanham o pacote de instalação do Visual Studio e necessitam de instalação para sua
utilização, elas estão presentes no pacote Reactive Extensions (Rx). Pode-se realizar o
download e a instalação através do Package Manager Console do Visual Studio utilizando o
comando Install-Package System.Reactive -Version 3.1.1, que realiza o download e a
instalação automaticamente na aplicação.
Passo 2 prepara o ambiente para o funcionamento do framework. Para isso é
necessário instalar o (pacote, programa, aplicativo?) R-3.4.3 for Windows (32/64 bit), que
executará a enginer do R, e para isso deve-se realizar o download do R no site https://cran.r-
project.org/bin/windows/base/ de acordo com a versão do sistema operacional.
Após a instalação é importante copiar as seguintes pastas e arquivo para dentro do
projeto da aplicação consumidora, / endereço_da_pasta_da_aplicação_consumidora
/bin/x64/Debug ou /bin/x32/Debug dependendo da versão do sistema operacional. A figura 22
foi elaborada para exibir as pastas e arquivo a serem copiados.
49
Figura 22 – Pastas e arquivos disponibilizados para utilização do framework
O passo 4 estabelece algumas regras para garantir o funcionamento tanto do
framework quanto a aplicação consumidora, que deverão ter a possibilidade de acessar os
sensores do Kinect ao mesmo tempo, e para que isso seja possível, tanto a aplicação quanto o
framework deverão, por padrão, utilizar os métodos:
● Microsoft.Kinect.KinectSensor.GetDefault() Para conectar ao sensor ;
● OpenMultiSourceFrameReader() Para designar os tipos de leituras de serão realizados
pelo sensor, exemplo corpo(body), infravermelho(Infrared), profundidade(Depth),
conforme descrito e exemplificado no código 6;
● MultiSourceFrameArrived() Para acessar os frames de atualização do sensor do Kinect.
E, por fim, o passo 5 é responsável por inicializar o framework e sincronizar a
aplicação a ele. Toda a comunicação é realizada por meio do método Observable que
mantém os dados entre o framework e a aplicação sincronizados.
Código 8 - Método para inicializar e sincronizar com o framework
50
No método Main() da aplicação, é utilizado na linha 2 o método
FrameWorkVFC.ProcessaFrequencia.Run() para inicializar o framework. Logo em seguida na linha 3
e 11 uma try-catch é utilizada para se sincronizar com o framework, que fornece alguns
métodos auto descritivos como frequência máxima, frequência mínima, frequência atual e
contador.
public Main() { 1 InitializeComponent();
//Inicializa o framework
2 FrameWorkVFC.ProcessaFrequencia.Run();
//Sincroniza dado com o framework
3 try
{
4 var AcessoAoFramework = Observable
.Interval(TimeSpan.FromSeconds(1))
.ObserveOn(this.Dispatcher)
.Subscribe((x) =>
{
5 lblContador.Text =
FrameWorkVFC.ProcessaFrequencia.m_contador.ToString();
6 lblFreq.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaAtual.ToString();
7 lblFreqMaxima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMaxima.ToString();
8 lblFreqMinima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMinima.ToString();
9 });
10 }
11 catch (Exception e)
12 {
13 Debug.WriteLine("Erro no funcionamento do framework {0}",
e.Message.ToString())
14 }
15}
51
5 CONSIDERAÇÕES FINAIS
Este trabalho apresentou o desenvolvimento de um framework para realização da
leitura da frequência cardíaca utilizando o Kinect, permitindo a outras aplicações utilizar este
recurso apenas incorporando o framework em seu sistema, tudo de forma simples e prática. O
objetivo inicialmente proposto foi alcançado, o que tornou possível a utilização do
framework.
A técnica utilizada neste trabalho para captura da frequência cardíaca através do
Kinect já foi objeto de estudo e validação. Segundo estudo de Gambi et al. (2017) a leitura da
frequência cardíaca obtida através do Kinect foi validada com uma margem de erro dentro do
recomendado pela OMS Organização Mundial de Saúde, mostrando-se um método apto para
o seu uso.
Como visto nos capítulos anteriores, foi possível desenvolver um framework rodando
em background que realiza a leitura da frequência cardíaca mantendo um sincronismo destes
valores com as aplicações que o utilizam, funcionando de forma prática e simples. O Kinect
não disponibiliza um método automático para a leitura de frequência cardíaca, sendo
necessária uma série de métodos e técnicas de processamento para realizar a leitura da
frequência cardíaca.
Para o desenvolvimento do framework foi essencial a utilização dos seguintes recursos
e procedimentos: acesso simultâneo ao sensor do Kinect; Determinação do ROI; separação
das cores; normalização dos dados; análise independente das variações de intensidade das
cores RGB; o algoritmo JADE e a transformada de Fourier. Assim, a combinação de todas
essas técnicas permitiu alcançar com sucesso objetivo do framework.
Alguns testes foram realizados no framework, além de utiliza-lo na aplicação modelo
o framework foi testado na aplicação FACE BASIC HD, distribuída em conjunto com o SDK
2.0 do Kinect. O teste foi realizado para verificar tanto o funcionamento quanto a facilidade
de configuração do framework em outras aplicações, seguindo o protocolo de uso do
framework descrito na seção 4.5, foram obtidos ótimos resultados em ambos os testes.
Permitindo o seu uso na aplicação FACE BASIC HD, conforme figura 23.
52
Figura 23 – Tela da aplicação FACE BASIC HD
A figura 23 exibe a tela da aplicação FACE BASIC HD, essa aplicação exibe alguns
dados capturados pelo sensor do Kinect, se os olhos do utilizador estão aberto ou fechado, se
apresenta expressão de felicidade. Na figura 23 na parte superior, circulada em vermelho é
apresentado o tempo do contador e a frequência cardíaca que foram capturados pelo
framework.
Contudo expõem-se algumas das falhas encontradas no seu uso, como a divergência
dos valores capturados em ambientes com baixa luminosidade ou em constante alteração de
luminosidade. Outra falha do framework é referente aos valores obtidos na primeira leitura da
frequência cardíaca realizada pelo Kinect que aparentemente traz resultados ou muito alto ou
muito a baixo do esperado.
Para trabalhos futuros pode-se trabalhar a detecção de mais de uma face na aplicação
do framework, que hoje detecta apenas uma face. O que permitiria realizar a leitura da
frequência cardíaca de vários usuários ao mesmo tempo. Também pode se complementar a
técnicas para leitura da frequência cardíaca, utilizando a leitura da variação na região do
pescoço, ou na região das bochechas, ou em ambas as regiões o que poderia melhorar a
precisão da leitura da frequência cardíaca, a figura 24.
53
Figura 24 – Posições do ROI para leitura da frequência cardíaca na face
Fonte: Gambi et al. (2017).
Conforme Gambi et al. (2017), a figura 24 exibe as regiões de interesse para leitura da
frequência cardíaca na face, sendo: a) região da testa; b1) e b2) a região das bochechas; c)
região do pescoço. Gambi et al. (2017) detalha na foto alguns dos ROI’s considerando o
percentual da área da face detectada para cada região mostrada na figura.
E, por fim, propõem-se para o futuro realizar uma comparação dos dados obtidos pelo
framework com alguns dos métodos tradicionais.
54
REFERÊNCIAS
ARAUJO, Gabriel Matos. Algoritmo para reconhecimento de características faciais
baseado em filtros de correlação. 2010. 78 f. Dissertação (Mestrado) - Curso de Engenharia
Elétrica, Universidade Federal do Rio de Janeiro, Rio de Janeiro, 2010.
BALAKRISHNAN, Guha; DURAND, Fredo; GUTTAG, John. Detecting Pulse from Head
Motions in Video. 2013 Ieee Conference On Computer Vision And Pattern
Recognition, [s.l.], p.1-8, jun. 2013. IEEE. http://dx.doi.org/10.1109/cvpr.2013.440.
BESSANI, Michel. Viabilidade de um sistema para auxílio no diagnóstico de cáries através de
imagem de fluorescência e processamento digital de imagens. 2012. 79 f. TCC (Graduação) -
Curso de Engenharia Elétrica com ênfase em Eletrônica, Universidade de São Paulo, São
Carlos, 2012.
CARDOSO, J.-f.; COMON, P.. Independent component analysis, a survey of some algebraic
methods. 1996 Ieee International Symposium On Circuits And Systems. Circuits And
Systems Connecting The World. Iscas 96, [s.l.], 15 maio 1996. IEEE.
http://dx.doi.org/10.1109/iscas.1996.540360.
CARDOSO, J.f.; SOULOUMIAC, A. Blind beamforming for non-gaussian signals. Iee
Proceedings F Radar And Signal Processing, [s.l.], v. 140, n. 6, p.362-370, 1993.
Institution of Engineering and Technology (IET). http://dx.doi.org/10.1049/ip-f-2.1993.0054.
CARVALHO, J. L. A. Ferramenta para Análise Tempo-Freqüencial da Variabilidade da
Freqüência Cardíaca. Dissertação de Mestrado, Publicação ENE.DM-156A/03,
Departamento de Engenharia Elétrica, Universidade de Brasília , Brasília , DF, 99 p.
https://doi.org/10.1109/IEMBS.2003.1280441
CONSTANTINO, Mauricio Gomes; SILVA, Gil Valdo José da; PALARETTI,
Vinicius. Transformada Discreta de Fourier: Aspectos Práticos Programa FFT 2018.
Faculdade de Filosofia, Ciências e Letras de Ribeirão Preto. Disponível em:
<http://artemis.ffclrp.usp.br/Software/FFT2018/Aspectos Práticos da Transformada de
Fourier.zip>. Acesso em: 1 out. 2017.
DUQUE, Juliano Jinzenji. Avaliação da q-transformada de Fourier como ferramenta não
linear de estudos de sinais biomédicos. 2012. 117 f. Dissertação (Mestrado) - Curso de
Ribeirão Preto, Universidade de Sao Paulo Sistema Integrado de Bibliotecas - SIBiUSP,
Ribeirão Preto, 2012. http://dx.doi.org/10.11606/d.59.2012.tde-29112012-121139
FECHINE, Oseana Macêdo. A Transformada de Fourier e Suas Aplicações. Campina
Grande: Grupo Pet Computação, 2010. 56 slides, color. Disponível em:
<http://www.dsc.ufcg.edu.br/~pet/ciclo_seminarios/tecnicos/2010/TransformadaDeFourier.pd
f>. Acesso em: 25 jan. 2018
55
Freedman, B., Shpunt, A., Machline, M. e Arieli, Y., U.S. Patent 20100118123
http://www.patentsencyclopedia.com/app/20100118123, 2010.
GAMBI, Ennio et al. Heart Rate Detection Using Microsoft Kinect: Validation and
Comparison to Wearable Devices. Sensors, v. 17, n. 8, p.1776-1794, 2 ago. 2017. MDPI
AG. http://dx.doi.org/10.3390/s17081776.
Guyton A; Hall J. Tratado da Fisiologia Humana. 9 ed. Editora Guanabara Koogan, Rio de
Janeiro, 1997.
HAYKIN, Simon; CHEN, Zhe. The Cocktail Party Problem. Neural Computation, [s.l.], v.
17, n. 9, p.1875-1902, set. 2005. MIT Press-Journals
http://dx.doi.org/10.1162/0899766054322964.
HERNÁNDEZ, Diana Carolina Morón et al. Quality Evaluation of Chromatic Interpolation
Algorithms for Image Acquisition System. Journal Of Aerospace Technology And
Management, [s.l.], v. 8, n. 3, p.339-351, 10 ago. 2016. Institute of Aeronautics and Space.
http://dx.doi.org/10.5028/jatm.v8i3.540
HSU, Rein-lien; ABDEL-MOTTALEB, M.; JAIN, A.k.. Face detection in color images. Ieee
Transactions On Pattern Analysis And Machine Intelligence, [s.l.], v. 24, n. 5, p.696-706,
maio 2002. Institute of Electrical and Electronics Engineers (IEEE).
http://dx.doi.org/10.1109/34.1000242.
JENG, Shi-hong et al. Facial feature detection using geometrical face model: An efficient
approach. Pattern Recognition, [s.l.], v. 31, n. 3, p.273-282, mar. 1998. Elsevier BV.
http://dx.doi.org/10.1016/s0031-3203(97)00048-4.
LEITE, Valéria Cristina Maria Nascimento. Separação cega de sinais: Análise comparativa
entre algoritmos. 182. 182 f. Dissertação (Mestrado) - Curso de Engenharia Elétrica,
Universidade Federal de Itajubá, Itajubá - MG, 2004.
LIN, Chiunhsiun; FAN, Kuo-chin. Triangle-based approach to the detection of human
face. Pattern Recognition, [s.l.], v. 34, n. 6, p.1271-1284, jun. 2001. Elsevier BV.
http://dx.doi.org/10.1016/s0031-3203(00)00075-3.
MARENGONI, Maurício; STRINGHINI, Stringhini. Tutorial: Introdução à visão
computacional usando OpenCV. Revista de Informática Teórica e Aplicada, v. 16, n. 1, p.
125-160, 2009.
MICROSOFT. PrimeSense Supplies 3-D-Sensing Technology to “Project Natal” for Xbox
360. 2010. Disponível em: <https://news.microsoft.com/2010/03/31/primesense-supplies-3-d-
sensing-technology-to-project-natal-for-xbox-360/>. Acesso em: 25 jan. 2018.
56
MICROSOFT. Microsoft Kinect: Kinect for Windows. 2015. Disponível em:
<http://www.microsoft.com/en-us/kinectforwindows/>. Acesso em:17 de abril de 2017.
MOURA, Natanael Nunes de. Detecção e Classificação de Sinais de Sonar Passivo Usando
Métodos de Separação Cega de Fontes. 2013. 161 f. Tese (Doutorado) - Curso de
Engenharia Elétrica, UFRJ Universidade Federal do Rio de Janeiro, Rio de Janeiro, 2013.
POYNTON, Charles A. Morgan Kaufmann series in computer graphics and geometric
modeling: Digital Video and HDTV: Algorithms and Interfaces. Morgan Kaufmann, 2003.
692 p. (ISBN:1558607927, 9781558607927).
POH, Ming-zher; MCDUFF, Daniel J.; PICARD, Rosalind W.. Non-contact, automated
cardiac pulse measurements using video imaging and blind source separation. Optics
Express, [s.l.], v. 18, n. 10, p.10762-10774, 7 maio 2010. The Optical Society.
http://dx.doi.org/10.1364/oe.18.010762.
ROCHA, João Carlos. Cor luz, Cor Pigmento e os Sistemas RGB e CMY. Revista Belas
Artes, 2011. Disponível em <
http://www.belasartes.br/revistabelasartes/downloads/artigos/3/cor-luz-cor-pigmento-e-os-
sistemas-rgb-e-cmy.pdf http://www.belasartes.br/revistabelasartes/downloads/artigos/3/cor-
luz-cor-pigmento-e-os-sistemas-rgb-e-cmy.pdf%20> Acesso em: 07/05/2017.
ROBERGS, R.A.; Roberts, S.O. Princípios fundamentais de Fisiologia do Exercício para
Aptidão, Desempenho e saúde. 1. ed. Phorte. 2002.p.144-163; 250-264.
ROWLEY, H.a.; BALUJA, S.; KANADE, T.. Neural network-based face detection. Ieee
Transactions On Pattern Analysis And Machine Intelligence, [s.l.], v. 20, n. 1, p.23-38,
1998. Institute of Electrical and Electronics Engineers (IEEE).
http://dx.doi.org/10.1109/34.655647.
STEIN, E. Análise rápida dos eletrocardiogramas: um guia de estudos. São Paulo: Monole,
2001. - 390 P. ISBN 85-204-1113-4
SUNG, K.-k.; POGGIO, T.. Example-based learning for view-based human face
detection. Ieee Transactions On Pattern Analysis And Machine Intelligence, [s.l.], v. 20,
n. 1, p.39-51, 1998. Institute of Electrical and Electronics Engineers (IEEE).
http://dx.doi.org/10.1109/34.655648.
VERKRUYSSE, Wim; SVAASAND, Lars O; NELSON, J Stuart. Remote plethysmographic
imaging using ambient light. Optics Express, [s.l.], v. 16, n. 26, p.21434-21445, 12 dez.
2008. The Optical Society. http://dx.doi.org/10.1364/oe.16.021434.
57
VALORIANI, Matteo. Programming with kinect v2. Palmas: Pt.slideshare.net, 2015. 62
slides, color. Disponível em: <https://pt.slideshare.net/MatteoValoriani/programming-with-
kinect-v2/>. Acesso em: 17/07/2015.
VIOLA, Paul; JONES, Michael J.. Robust Real-Time Face Detection. International Journal
Of Computer Vision, [s.l.], v. 57, n. 2, p.137-154, maio 2004. Springer Nature.
http://dx.doi.org/10.1023/b:visi.0000013087.49260.fb.
WANG, Jianguo; TAN, Tieniu. A new face detection method based on shape information.
Pattern Recognition Letters, [s.l.], v. 21, n. 6-7, p.463-471, jun. 2000. Elsevier BV.
http://dx.doi.org/10.1016/s0167-8655(00)00008-8.
WANG, Jianguo; TAN, Tieniu. A new face detection method based on shape
information. Pattern Recognition Letters, [s.l.], v. 21, n. 6-7, p.463-471, jun. 2000. Elsevier
BV. http://dx.doi.org/10.1016/s0167-8655(00)00008-8.
WU, Hao-yu et al. Eulerian video magnification for revealing subtle changes in the
world. Acm Transactions On Graphics, [s.l.], v. 31, n. 4, p.1-8, 1 jul. 2012. Association for
Computing Machinery (ACM). http://dx.doi.org/10.1145/2185520.2185561.
WU, Haiyuan; CHEN, Qian; YACHIDA, M.. Face detection from color images using a fuzzy
pattern matching method. Ieee Transactions On Pattern Analysis And Machine
Intelligence, [s.l.], v. 21, n. 6, p.557-563, jun. 1999. Institute of Electrical and Electronics
Engineers (IEEE). http://dx.doi.org/10.1109/34.771326.
YOKOTA, Tatsuya. Independent Component Analysis: Independent Component
Analysis for Blind Source Separation. 2012. Shinagawa-ku, Tokyo, Japan. Disponível em:
<https://pt.slideshare.net/yokotatsuya/independent-component-analysis-11359849>.
https://pt.slideshare.net/yokotatsuya/independent-component-analysis-
11359849%3e.%20Acesso em: 16 jun. 2017
ZORZI, Rafael Luiz de Andrade. Corpo humano - órgãos, sistemas e
funcionamento. Nacional: Senac Nacional, 2010. 232 p. (8574582778).
58
APÊNDICES
59
APÊNDICE A – Codigo fonte FrameWork
Classe ProcessaFrequencia
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Shapes;
using Microsoft.Kinect;
using Microsoft.Kinect.Face;
using RDotNet;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Threading;
using System.Diagnostics;
namespace FrameWorkVFC
{
public static class ProcessaFrequencia
{
//Variaveis de uso do Kinect
private static KinectSensor m_SensorKinect;
private static MultiSourceFrameReader m_MultSourceFReader;
private static CoordinateMapper m_CoordMapper;
private static Body m_PontosCorpoRastreadoAtual;
private static ulong m_IdRastreamentoAtual;
private static FaceFrameSource m_FaceSource;
private static FaceFrameFeatures m_faceframefeature =
FaceFrameFeatures.BoundingBoxInColorSpace |
FaceFrameFeatures.BoundingBoxInInfraredSpace |
FaceFrameFeatures.PointsInColorSpace |
FaceFrameFeatures.PointsInInfraredSpace;
private static FaceFrameReader m_FaceReader;
private static WriteableBitmap m_colorBitmap = null;
//Utilizado para determinar os limites da face
private static RectI m_LimitesDaFace;
60
//Variaveis para permitir a geracao dos arquivos cvs contendo os dados
capturados
private static System.IO.FileStream m_fileStream;
private static string m_filePath;
private static string currentDir;
//Variaveis para controle e gerenciamento do tempo
private static System.Timers.Timer m_tempo;
private static bool m_tempoIniciado;
public static int m_contador;
private static int Contador_Inicial = 60;
private static System.Diagnostics.Stopwatch m_segundosPercorrido;
//Variavel para verificação da inicialização do jade
private static bool m_JADE_Loaded;
//Declaro a biblioteca do jade
private static REngine engine;
//Vetor contendo os valores das frequencia cardiaca
private static List<double> frequencia = new List<double>();
private static List<double> grafico = new List<double>();
private static List<double> aux = new List<double>();
public static Boolean calculado = false;
//Inicio Parametros de configurações para forca o uso do ponto como
separador de milhares
private static CultureInfo minhaCultura = new CultureInfo("pt-BR");
public static void Run() {
Debug.WriteLine("Rodando");
CapturaFrequencia();
}
private static void CapturaFrequencia()
{
m_tempo = new System.Timers.Timer(1000);
m_tempo.Elapsed += M_timer_Elapsed;
m_contador = Contador_Inicial;
m_segundosPercorrido = new System.Diagnostics.Stopwatch();
Console.WriteLine("Resultado");
InitializeR();
InicializaParametros();
InitializeSensor();
Console.WriteLine("Resultado");
}
61
private static void InicializaParametros()
{
//Configura Parametros para geração do arquivo
minhaCultura.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
minhaCultura.DateTimeFormat.ShortTimePattern = "HH:mm";
minhaCultura.NumberFormat.NumberDecimalDigits = 2;
minhaCultura.NumberFormat.NumberGroupSeparator = "_";
minhaCultura.NumberFormat.NumberDecimalSeparator = ".";
}
private static void InitializeR()
{
//Atribuo a variavel para utilizar a engine da biblioteca R que
será utilizado com o JADE
engine = REngine.GetInstance();
//Inicializo o jade
engine.Initialize();
engine.Evaluate("library('JADE')");
if (!m_JADE_Loaded)
{
//engine.Evaluate("install.packages('JADE',
repos='http://cran.us.r-project.org')");
//repos
='https://cran.rstudio.com/bin/windows/contrib/3.2/JADE_1.9-93.zip')");
engine.Evaluate("library('JADE')");
m_JADE_Loaded = true;
}
}
private static void M_timer_Elapsed(object sender,
System.Timers.ElapsedEventArgs e)
{
--m_contador;
calculado = false;
if (m_contador == 0)
{
m_segundosPercorrido.Stop();
m_tempo.Stop();
m_contador = Contador_Inicial;
Dispatcher.CurrentDispatcher.Invoke(
delegate ()
{
Debug.WriteLine("Iniciando processamento");
closeFile();
62
m_tempoIniciado = false;
ProcessaDados();
});
}
Dispatcher.CurrentDispatcher.Invoke(
delegate ()
{
Debug.WriteLine("Tempo Restante : {0} Segundos -- FrameWork",
m_contador);
});
}
public static void ProcessaDados()
{
//Normalizo o acesso ao diretorio debug da aplicacao
currentDir = System.Environment.CurrentDirectory.Replace('\\', '/');
//carrego o arquivo cvs gerado anteriormente contendo as variacoes do
R G B e alfa e atribuo a variavel a ser utilizada no script R
engine.Evaluate(string.Format("DadosCapturadosBBS <- read.csv('{0}')",
m_filePath.Replace('\\', '/')));
//localizacao do script em R que deve ficar dentro da pasta
debug\RScript
engine.Evaluate(string.Format("source('{0}/RScripts/ProcessaFrequenciaCardiaca
_JADE.r')", currentDir));
NumericVector hrVetor1= engine.GetSymbol("hr1").AsNumeric(); //
Vermelho
NumericVector hrVetor4 = engine.GetSymbol("hr4").AsNumeric(); //
Infravermelho
double hr1 = hrVetor1.First();
double hr4 = hrVetor4.First();
Debug.WriteLine(hr1.ToString());
Debug.WriteLine(hr4.ToString());
double hr = 0;
GenericVector hrGrafico;
Debug.WriteLine(Frequencia.Count().ToString());
63
if (Frequencia.Count() == 0)
{
hr = (hr1 > hr4) ? hr4 : hr1;
hrGrafico = (hr1 > hr4) ? engine.GetSymbol("graficoinf").AsList()
: engine.GetSymbol("graficored").AsList();
}
else
{
hr = (hr1 > hr4) ? hr1 : hr4;
hrGrafico = (hr1 > hr4) ? engine.GetSymbol("graficored").AsList()
: engine.GetSymbol("graficoinf").AsList();
}
grafico = hrGrafico.AsNumeric().ToList();
Frequencia.Add(hr);
calculado = true;
Debug.WriteLine("Frequencia: {0} ", hr.ToString());
System.IO.File.Delete(m_filePath);
}
private static void initializeFile()
{
m_filePath =
string.Format("{0}\\DadosCapturadosBBS_{1}{2}{3}{4}{5}{6}.{7}.csv",
System.Environment.CurrentDirectory, DateTime.Now.Year, DateTime.Now.Month,
DateTime.Now.Day, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second,
DateTime.Now.Millisecond);
m_fileStream = new System.IO.FileStream(m_filePath,
System.IO.FileMode.CreateNew, System.IO.FileAccess.ReadWrite,
System.IO.FileShare.ReadWrite, 512, true);
string header = "nMillisecondsElapsed,nBlue,nGreen,nRed,nAlpha,nIr\n";
var headerBytes = System.Text.Encoding.UTF8.GetBytes(header);
m_fileStream.Write(headerBytes, 0, headerBytes.Length);
}
private static void InitializeSensor()
{
m_SensorKinect = KinectSensor.GetDefault();
m_MultSourceFReader =
m_SensorKinect.OpenMultiSourceFrameReader(FrameSourceTypes.Body |
FrameSourceTypes.BodyIndex | FrameSourceTypes.Color |
FrameSourceTypes.Infrared | FrameSourceTypes.Depth);
m_CoordMapper = m_SensorKinect.CoordinateMapper;
m_MultSourceFReader.MultiSourceFrameArrived +=
M_MSFReader_MultiSourceFrameArrived;
64
m_SensorKinect.Open();
FrameDescription colorFrameDescription =
m_SensorKinect.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
m_colorBitmap = new WriteableBitmap(colorFrameDescription.Width,
colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null);
}
private static void M_MSFReader_MultiSourceFrameArrived(object sender,
MultiSourceFrameArrivedEventArgs e)
{
var frameRef = e.FrameReference;
var msFrame = frameRef.AcquireFrame();
if (null != msFrame)
{
using (var bodyFrame = msFrame.BodyFrameReference.AcquireFrame())
{
ProcessBodyFrame(bodyFrame);
}
using (var colorFrame =
msFrame.ColorFrameReference.AcquireFrame())
{
ProcessColorFrame(colorFrame);
}
}
}
private static void ProcessColorFrame(ColorFrame colorFrame)
{
if ((null == colorFrame)) return;
FrameDescription colorFrameDescription = colorFrame.FrameDescription;
using (KinectBuffer colorBuffer = colorFrame.LockRawImageBuffer())
{
m_colorBitmap.Lock();
if ((colorFrameDescription.Width == m_colorBitmap.PixelWidth) &&
(colorFrameDescription.Height == m_colorBitmap.PixelHeight))
{
colorFrame.CopyConvertedFrameDataToIntPtr(
m_colorBitmap.BackBuffer,
(uint)(colorFrameDescription.Width *
colorFrameDescription.Height * 4),
ColorImageFormat.Bgra);
m_colorBitmap.AddDirtyRect(new Int32Rect(0, 0,
m_colorBitmap.PixelWidth, m_colorBitmap.PixelHeight));
}
65
m_colorBitmap.Unlock();
}
}
private static void ProcessBodyFrame(BodyFrame bodyFrame)
{
if (null != bodyFrame)
{
if (m_PontosCorpoRastreadoAtual != null)
{
m_PontosCorpoRastreadoAtual =
FindBodyWithTrackingId(bodyFrame, m_IdRastreamentoAtual);
if (m_PontosCorpoRastreadoAtual != null)
{
return;
}
}
Body selectedBody = FindClosestBody(bodyFrame);
if (selectedBody == null) { return; }
m_PontosCorpoRastreadoAtual = selectedBody;
m_IdRastreamentoAtual = selectedBody.TrackingId;
InitializeFace();
}
}
private static void InitializeFace()
{
m_FaceSource = new FaceFrameSource(m_SensorKinect,
m_IdRastreamentoAtual, m_faceframefeature);
m_FaceReader = m_FaceSource.OpenReader();
m_FaceReader.FrameArrived += M_FaceReader_FrameArrived;
}
private static void M_FaceReader_FrameArrived(object sender,
FaceFrameArrivedEventArgs e)
{
var frameRef = e.FrameReference;
using (var frameDaFace = frameRef.AcquireFrame())
{
if (frameDaFace != null)
{
if (frameDaFace.FaceFrameResult != null)
66
{
m_LimitesDaFace =
frameDaFace.FaceFrameResult.FaceBoundingBoxInColorSpace;
var RegiaoDaFace = new RectI();
RegiaoDaFace.Top = Math.Abs(m_LimitesDaFace.Top - 36);
RegiaoDaFace.Bottom = Math.Abs(m_LimitesDaFace.Bottom -
12);
RegiaoDaFace.Left = Math.Abs(m_LimitesDaFace.Left + 26);
RegiaoDaFace.Right = Math.Abs(m_LimitesDaFace.Right - 20);
if (m_tempoIniciado)
{
RecordData(RegiaoDaFace, frameDaFace);
}
else
{
initializeFile();
m_tempoIniciado = true;
m_tempo.Start();
m_segundosPercorrido.Start();
}
}
}
}
}
private static void RecordData(RectI faceRegion, FaceFrame faceFrame)
{
using (var irFrame = faceFrame.InfraredFrameReference.AcquireFrame())
{
using (var depthFrame =
faceFrame.DepthFrameReference.AcquireFrame())
{
using (var colorFrame =
faceFrame.ColorFrameReference.AcquireFrame())
{
if ((null == irFrame) || (null == colorFrame) || (null ==
depthFrame)) return;
DepthSpacePoint[] depthSpacePoints = new
DepthSpacePoint[colorFrame.FrameDescription.Height *
colorFrame.FrameDescription.Width];
FrameDescription depthFrameDescription =
depthFrame.FrameDescription;
67
// faco uma copia do frame para processa-lo
using (KinectBuffer depthFrameData =
depthFrame.LockImageBuffer())
{
m_CoordMapper.MapColorFrameToDepthSpaceUsingIntPtr(
depthFrameData.UnderlyingBuffer,
depthFrameData.Size,
depthSpacePoints);
}
//Capturo o pixel
m_colorBitmap.Lock();
unsafe
{
byte* pixels = (byte*)m_colorBitmap.BackBuffer;
var startPixel = faceRegion.Left * faceRegion.Top;
var endPixel = faceRegion.Right * faceRegion.Bottom;
float alpha = 0;
float vermehlo = 0;
float verde = 0;
float azul = 0;
float ir = 0;
ushort[] irFrameData = new
ushort[irFrame.FrameDescription.Height * irFrame.FrameDescription.Width];
irFrame.CopyFrameDataToArray(irFrameData);
//Agora pego o Vermelho Verde e azul para a regiao da
face
for (int i = startPixel; i < endPixel; i += 4)
{
//var pixel = pixels[i];
int irIndex = (int)depthSpacePoints[i].X *
(int)depthSpacePoints[i].Y;
azul += pixels[i]; // << 24;
verde += pixels[i + 1]; // << 16;
vermehlo += pixels[i + 2];// << 8;
alpha += pixels[i + 3];
if (irIndex < irFrameData.Length)
ir += irFrameData[irIndex];
else
ir += 0;
}
float size = Math.Abs(startPixel - endPixel);
68
float avg_alpha = alpha / size;
float avg_vermelho = vermehlo / size;
float avg_verde = verde / size;
float avg_azul = azul / size;
float avg_ir = ir / size;
double std_alpha = 0;
double std_vermelho = 0;
double std_verde = 0;
double std_azul = 0;
double std_ir = 0;
double var_alpha = 0;
double var_vermelho = 0;
double var_verde = 0;
double var_azul = 0;
double var_ir = 0;
//Pego o desvio padrao
for (int i = startPixel; i < endPixel; i += 4)
{
//var pixel = pixels[i];
var_azul = (double)(pixels[i] - avg_azul);
std_azul += Math.Pow(var_azul, 2.0);
var_verde = (pixels[i + 1] - avg_verde);
std_verde += Math.Pow(var_verde, 2);
var_vermelho = (pixels[i + 2] - avg_vermelho);
std_vermelho += Math.Pow(var_vermelho, 2);
var_alpha = (pixels[i + 3] - avg_alpha);
std_alpha += Math.Pow(var_alpha, 2);
int irIndex = (int)depthSpacePoints[i].X *
(int)depthSpacePoints[i].Y;
if (irIndex < irFrameData.Length)
var_ir = irFrameData[irIndex] - avg_ir;
else
var_ir = avg_ir;
std_ir += Math.Pow(var_ir, 2);
}
std_alpha = Math.Sqrt(std_alpha / size);
std_vermelho = Math.Sqrt(std_vermelho / size);
std_verde = Math.Sqrt(std_verde / size);
std_azul = Math.Sqrt(std_azul / size);
69
std_ir = Math.Sqrt(std_ir / size);
double prime_alpha = 0;
double prime_vermelho = 0;
double prime_verde = 0;
double prime_azul = 0;
double prime_ir = 0;
for (int i = startPixel; i < endPixel; i += 4)
{
//var pixel = pixels[i];
var_azul = (double)(pixels[i] - avg_azul);
prime_azul += var_azul / std_azul;
var_verde = (pixels[i + 1] - avg_verde);
prime_verde += var_verde / std_verde;
var_vermelho = (pixels[i + 2] - avg_vermelho);
prime_vermelho += var_vermelho / std_vermelho;
var_alpha = (pixels[i + 3] - avg_alpha);
prime_alpha += var_alpha / std_alpha;
int irIndex = (int)depthSpacePoints[i].X *
(int)depthSpacePoints[i].Y;
if (irIndex < irFrameData.Length)
var_ir = irFrameData[irIndex] - avg_ir;
else
var_ir = avg_ir;
prime_ir += var_ir / std_ir;
}
//dados normalizados
double normalizado_alpha = prime_alpha / size;
double normalizado_vermelho = prime_vermelho / size;
double normalizado_azul = prime_azul / size;
double normalizado_verde = prime_verde / size;
double normalizado_ir = prime_ir / size;
string data = string.Format(minhaCultura,
"{0},{1},{2},{3},{4},{5}\n", m_segundosPercorrido.ElapsedMilliseconds,
normalizado_alpha, normalizado_vermelho, normalizado_verde, normalizado_azul,
normalizado_ir);
try
{
70
var bytesToWrite =
System.Text.Encoding.UTF8.GetBytes(data);
m_fileStream.WriteAsync(bytesToWrite, 0,
bytesToWrite.Length);
m_fileStream.FlushAsync();
} catch (Exception e ){
Debug.WriteLine(e.Message);
}
}
m_colorBitmap.Unlock();
}
}
}
}
private static Body FindClosestBody(BodyFrame bodyFrame)
{
Body result = null;
double closestBodyDistance = double.MaxValue;
Body[] bodies = new Body[bodyFrame.BodyCount];
bodyFrame.GetAndRefreshBodyData(bodies);
foreach (var body in bodies)
{
if (body.IsTracked)
{
var currentLocation =
body.Joints[JointType.SpineBase].Position;
var currentDistance = VectorLength(currentLocation);
if (result == null || currentDistance < closestBodyDistance)
{
result = body;
closestBodyDistance = currentDistance;
}
}
}
return result;
}
private static double VectorLength(CameraSpacePoint point)
{
var result = Math.Pow(point.X, 2) + Math.Pow(point.Y, 2) +
Math.Pow(point.Z, 2);
71
result = Math.Sqrt(result);
return result;
}
private static Body FindBodyWithTrackingId(BodyFrame bodyFrame, ulong
trackingId)
{
Body result = null;
Body[] bodies = new Body[bodyFrame.BodyCount];
bodyFrame.GetAndRefreshBodyData(bodies);
foreach (var body in bodies)
{
if (body.IsTracked)
{
if (body.TrackingId == trackingId)
{
result = body;
break;
}
}
}
return result;
}
private static ImageSource ImageSource { get { return m_colorBitmap; } }
private static List<double> Frequencia
{
get
{
return frequencia;
}
set
{
frequencia = value;
}
}
private static List<double> Grafico
{
get
{
return grafico;
72
}
set
{
grafico = value;
}
}
public static List<double> getGrafico
{
get
{
if (Grafico.Count == 0)
return null;
aux = grafico;
grafico = null;
return aux;
}
set
{
grafico = value;
}
}
public static double FrequenciaAtual
{
get
{
if (frequencia.Count == 0)
return 0;
return Math.Round(frequencia.Last(), 2);
}
}
public static double FrequenciaMaxima
{
get
{
if (frequencia.Count == 0)
return 0;
return Math.Round(frequencia.Max(), 2);
}
}
public static double FrequenciaMinima
{
get
{
73
if (frequencia.Count == 0)
return 0;
return Math.Round(frequencia.Min(), 2);
}
}
private static void closeFile()
{
if (null != m_fileStream)
{
m_fileStream.Flush();
m_fileStream.Close();
m_fileStream = null;
}
}
}
}
74
APÊNDICE B - Script em R - ProcessaFrequenciaCardiaca
totalRecords <- (sum(lengths(DadosCapturadosBBS[1])))
time <- max (DadosCapturadosBBS$nMillisecondsElapsed)/1000
frameRate <- totalRecords/time
#GET JADE
S <- cbind(DadosCapturadosBBS$nRed, DadosCapturadosBBS$nGreen,
DadosCapturadosBBS$nBlue, DadosCapturadosBBS$nIr)
A <- matrix(rnorm(16), ncol=4)
X <- S %*% t(A)
res<-JADE(X,4)
ica1 <- res$S[,1]
ica2 <- res$S[,2]
ica3 <- res$S[,3]
ica4 <- res$S[,4]
ica1_fft <- fft(res$S[,1])
ica2_fft <- fft(res$S[,2])
ica3_fft <- fft(res$S[,3])
ica4_fft <- fft(res$S[,4])
pwr1 <- sqrt(Re(ica1_fft)^2+Im(ica1_fft)^2)
pwr2 <- sqrt(Re(ica2_fft)^2+Im(ica2_fft)^2)
pwr3 <- sqrt(Re(ica3_fft)^2+Im(ica3_fft)^2)
pwr4 <- sqrt(Re(ica4_fft)^2+Im(ica4_fft)^2)
pwr1 <- pwr1[1:(totalRecords/2)]
pwr2 <- pwr2[1:(totalRecords/2)]
pwr3 <- pwr3[1:(totalRecords/2)]
pwr4 <- pwr4[1:(totalRecords/2)]
bpm1 <- max(pwr1)
bpm2 <- max(pwr2)
bpm3 <- max(pwr3)
bpm4 <- max(pwr4)
freq <- frameRate/2 * seq(0,1,length=totalRecords/2+1)
lowHz <- 0.75/(frameRate/2)
lowHzIndex <- which(freq < lowHz)
lowHzIndex <- lowHzIndex[(length(lowHzIndex))] + 1
highHz <- 4/(frameRate/2)
highHzIndex <- which(freq > highHz)
highHzIndex <- highHzIndex[1] - 1
hr1 <- max(pwr1[lowHzIndex:highHzIndex])
hr2 <- max(pwr2[lowHzIndex:highHzIndex])
hr3 <- max(pwr3[lowHzIndex:highHzIndex])
hr4 <- max(pwr4[lowHzIndex:highHzIndex])
graficored <- sqrt(Re(ica1_fft)^2+Im(ica1_fft)^2)
graficoinf <- sqrt(Re(ica4_fft)^2+Im(ica4_fft)^2)
75
APÊNDICE C - Aplicação Modelo - DashBoardCompleta.cs,
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Timers;
using System.Windows.Input;
using SciChart.Charting.Common.Helpers;
using SciChart.Charting.Model.DataSeries;
using SciChart.Data.Model;
using SciChart.Examples.ExternalDependencies.Common;
using SciChart.Examples.ExternalDependencies.Data;
using Microsoft.Kinect;
using Microsoft.Kinect.Face;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Media;
using System.Reactive.Linq;
using System.Diagnostics;
using System.Windows.Threading;
using FrameWorkVFC;
namespace FrameWorkVFC {
/// <summary>
/// Interaction logic for DashBoard.xaml
/// </summary>
public partial class DashBoardCompleta {
//Variaveis de uso do Kinect
private KinectSensor m_SensorKinect;
private MultiSourceFrameReader m_MultSourceFReader;
private CoordinateMapper m_CoordMapper;
private Body m_PontosCorpoRastreadoAtual;
private ulong m_IdRastreamentoAtual;
private FaceFrameSource m_FaceSource;
private Microsoft.Kinect.Face.FaceFrameReader m_FaceReader;
private WriteableBitmap m_colorBitmap = null;
private FaceModel _faceModel = null;
private double[] _sourceData;
private DispatcherTimer _timer;
private const int TimerInterval = 20;
private int _currentIndex;
private int _totalIndex;
76
private TraceAOrB _whichTrace = TraceAOrB.TraceA;
private XyDataSeries < double, double > _dataSeriesA;
private XyDataSeries < double, double > _dataSeriesB;
private Int32Rect cropRect;
private CroppedBitmap croppedImage;
private enum TraceAOrB {
TraceA,
TraceB,
}
//Utilizado para determinar os limites da face
private RectI m_LimitesDaFace;
public DashBoardCompleta() {
InitializeComponent();
this.DataContext = this;
_dataSeriesA = new XyDataSeries < double, double > ()
{FifoCapacity = 5000 };
_dataSeriesB = new XyDataSeries < double, double > ()
{FifoCapacity = 5000 };
FrameWorkVFC.ProcessaFrequencia.Run();
try {
var AcessoAoFramework = Observable
.Interval(TimeSpan.FromSeconds(1))
.ObserveOn(this.Dispatcher)
.Subscribe((x) => {
if (FrameWorkVFC.ProcessaFrequencia.m_contador < 60)
{imgZoom.Source = img; }
lblContador.Text =
FrameWorkVFC.ProcessaFrequencia.m_contador.ToString();
lblFrequencia.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaAtual.ToString();
lblFrequenciaMaxima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMaxima.ToString();
lblFrequenciaMinima.Text =
FrameWorkVFC.ProcessaFrequencia.FrequenciaMinima.ToString();
if (FrameWorkVFC.ProcessaFrequencia.calculado) {
var values = new List < double > ();
values =
FrameWorkVFC.ProcessaFrequencia.getGrafico;
77
if (values != null) {
// List<double> list = new List<double>();
// list.AddRange(values);
values.RemoveAll(r => r/60 < 0.45);
values.RemoveAll(r => r >=
FrameWorkVFC.ProcessaFrequencia.FrequenciaAtual + 0.5);
_sourceData = values.ToArray();
//list.ToArray();
}
}
});
var AcessoAoFramework2 = Observable
.Interval(TimeSpan.FromMilliseconds(20))
.ObserveOn(this.Dispatcher)
.Subscribe((x) => {
if (_sourceData != null) {
if (_sourceData.Count() > 0) {
const double sampleRate = 800;
// As timer cannot tick quicker than ~20ms, we
append 10 points
// per tick to simulate a sampling frequency of
500Hz (e.g. 2ms per sample)
for (int i = 0; i < 10; i++)
AppendPoint(sampleRate);
traceSeriesA.DataSeries = _dataSeriesA;
traceSeriesB.DataSeries = _dataSeriesB;
}
}
});
}
catch (Exception e) {
Debug.WriteLine("Erro no funcinamento do framework {0}",
e.Message.ToString());
}
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
InitializeSensor();
}
private void InitializeSensor() {
m_SensorKinect = Microsoft.Kinect.KinectSensor.GetDefault();
78
m_MultSourceFReader =
m_SensorKinect.OpenMultiSourceFrameReader(FrameSourceTypes.Body |
FrameSourceTypes.BodyIndex | FrameSourceTypes.Color |
FrameSourceTypes.Infrared | FrameSourceTypes.Depth);
m_CoordMapper = m_SensorKinect.CoordinateMapper;
m_MultSourceFReader.MultiSourceFrameArrived + =
M_MSFReader_MultiSourceFrameArrived;
m_SensorKinect.Open();
// create the colorFrameDescription from the ColorFrameSource
using Bgra format
FrameDescription colorFrameDescription =
m_SensorKinect.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);
// create the bitmap to display
this.m_colorBitmap = new
WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height,
96.0, 96.0, PixelFormats.Bgr32, null);
imgKinectView.Source = this.ImageSource;
}
private void M_MSFReader_MultiSourceFrameArrived(object sender,
MultiSourceFrameArrivedEventArgs e) {
var frameRef = e.FrameReference;
var msFrame = frameRef.AcquireFrame();
if (null != msFrame) {
using (var bodyFrame =
msFrame.BodyFrameReference.AcquireFrame()) {
ProcessBodyFrame(bodyFrame);
}
using (var colorFrame =
msFrame.ColorFrameReference.AcquireFrame()) {
ProcessColorFrame(colorFrame);
}
}
}
private void ProcessColorFrame(ColorFrame colorFrame) {
if ((null == colorFrame))return;
FrameDescription colorFrameDescription =
colorFrame.FrameDescription;
using (KinectBuffer colorBuffer = colorFrame.LockRawImageBuffer())
{
this.m_colorBitmap.Lock();
// verify data and write the new color frame data to the
display bitmap
79
if ((colorFrameDescription.Width ==
this.m_colorBitmap.PixelWidth) && (colorFrameDescription.Height ==
this.m_colorBitmap.PixelHeight)) {
colorFrame.CopyConvertedFrameDataToIntPtr(
this.m_colorBitmap.BackBuffer,
(uint)(colorFrameDescription.Width *
colorFrameDescription.Height * 4),
ColorImageFormat.Bgra);
this.m_colorBitmap.AddDirtyRect(new Int32Rect(0, 0,
this.m_colorBitmap.PixelWidth, this.m_colorBitmap.PixelHeight));
}
this.m_colorBitmap.Unlock();
}
}
private void ProcessBodyFrame(BodyFrame bodyFrame) {
if (null != bodyFrame) {
if (this.m_PontosCorpoRastreadoAtual != null) {
this.m_PontosCorpoRastreadoAtual =
FindBodyWithTrackingId(bodyFrame, this.m_IdRastreamentoAtual);
if (this.m_PontosCorpoRastreadoAtual != null) {
return;
}
}
Body selectedBody = FindClosestBody(bodyFrame);
if (selectedBody == null) {
return;
}
this.m_PontosCorpoRastreadoAtual = selectedBody;
this.m_IdRastreamentoAtual = selectedBody.TrackingId;
InitializeFace();
}
}
private static Body FindClosestBody(BodyFrame bodyFrame) {
Body result = null;
double closestBodyDistance = double.MaxValue;
Body[] bodies = new Body[bodyFrame.BodyCount];
bodyFrame.GetAndRefreshBodyData(bodies);
foreach (var body in bodies) {
80
if (body.IsTracked) {
var currentLocation =
body.Joints[JointType.SpineBase].Position;
var currentDistance = VectorLength(currentLocation);
if (result == null || currentDistance <
closestBodyDistance) {
result = body;
closestBodyDistance = currentDistance;
}
}
}
return result;
}
private static double VectorLength(CameraSpacePoint point) {
var result = Math.Pow(point.X, 2) + Math.Pow(point.Y, 2) +
Math.Pow(point.Z, 2);
result = Math.Sqrt(result);
return result;
}
private void InitializeFace() {
m_FaceSource = new
Microsoft.Kinect.Face.FaceFrameSource(m_SensorKinect, m_IdRastreamentoAtual,
Microsoft.Kinect.Face.FaceFrameFeatures.BoundingBoxInColorSpace |
Microsoft.Kinect.Face.FaceFrameFeatures.BoundingBoxInInfraredSpace |
Microsoft.Kinect.Face.FaceFrameFeatures.PointsInColorSpace |
Microsoft.Kinect.Face.FaceFrameFeatures.PointsInInfraredSpace);
m_FaceReader = m_FaceSource.OpenReader();
m_FaceReader.FrameArrived + = M_FaceReader_FrameArrived;
}
private void M_FaceReader_FrameArrived(object sender,
Microsoft.Kinect.Face.FaceFrameArrivedEventArgs e) {
var frameRef = e.FrameReference;
using (var frameDaFace = frameRef.AcquireFrame()) {
if (frameDaFace != null) {
//get the Color Region
if (frameDaFace.FaceFrameResult != null) {
m_LimitesDaFace =
frameDaFace.FaceFrameResult.FaceBoundingBoxInColorSpace;
var RegiaoDaFace = new RectI();
RegiaoDaFace.Top = Math.Abs(m_LimitesDaFace.Top - 36);
81
RegiaoDaFace.Bottom = Math.Abs(m_LimitesDaFace.Bottom
- 12);
RegiaoDaFace.Left = Math.Abs(m_LimitesDaFace.Left +
26);
RegiaoDaFace.Right = Math.Abs(m_LimitesDaFace.Right -
20);
MarcaRosto(RegiaoDaFace);
}
}
}
}
private void MarcaRosto(RectI regiao) {
if (null == regiao)return;
var newLeft = regiao.Left;
var newTop = regiao.Top;
var newRight = regiao.Right;
var newBottom = regiao.Bottom;
var width = Math.Abs((newRight + 4) - (newLeft));
var height = Math.Abs(newBottom - newTop);
m_colorBitmap.Lock();
unsafe {
byte * pixels = (byte * )m_colorBitmap.BackBuffer;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
var leftEdge = ((newTop + j) *
m_colorBitmap.PixelWidth * 4) + (newLeft);
var rightEdge = ((newTop + j) *
m_colorBitmap.PixelWidth * 4) + (newRight * 4);
var index = ((newTop + j) * m_colorBitmap.PixelWidth *
4) + ((newLeft * 4) + 4 * i);
if ((index >= leftEdge) && (index <= rightEdge)) {
//valid pixels
//Blue
pixels[index] = 255;
//Green
//pixels[index + 1] = 255;//
(byte)((int)pixels[index + 1] - 100);
//Red
//pixels[index + 2] = 255;//
(byte)((int)pixels[index + 1] + 10);
//Alpha - no effect right now
82
//pixels[index + 3] = 50;
}
}
}
}
m_colorBitmap.Unlock();
}
/// <summary>
/// Find if there is a body tracked with the given trackingId
/// </summary>
/// <param name="bodyFrame">A body frame</param>
/// <param name="trackingId">The tracking Id</param>
/// <returns>The body object, null of none</returns>
private static Body FindBodyWithTrackingId(BodyFrame bodyFrame, ulong
trackingId) {
Body result = null;
Body[] bodies = new Body[bodyFrame.BodyCount];
bodyFrame.GetAndRefreshBodyData(bodies);
foreach (var body in bodies) {
if (body.IsTracked) {
if (body.TrackingId == trackingId) {
result = body;
break;
}
}
}
return result;
}
/// <summary>
/// Gets the bitmap to display
/// </summary>
public ImageSource ImageSource {get {return this.m_colorBitmap; }}
private void Window_Unloaded(object sender, RoutedEventArgs e) {
DisposeOfObjects();
}
private void Page_Unloaded(object sender, RoutedEventArgs e) {
if (_faceModel != null) {
_faceModel.Dispose();
_faceModel = null;
}
83
GC.SuppressFinalize(this);
}
private void DisposeOfObjects() {
m_colorBitmap = null;
m_FaceReader.Dispose();
m_FaceReader = null;
m_PontosCorpoRastreadoAtual = null;
m_MultSourceFReader.Dispose();
m_MultSourceFReader = null;
m_CoordMapper = null;
m_SensorKinect.Close();
m_SensorKinect = null;
}
private void AppendPoint(double sampleRate) {
if (_currentIndex >= _sourceData.Length) {
_currentIndex = 0;
}
// Get the next voltage and time, and append to the chart
double voltage = _sourceData[_currentIndex]/60;
double time = (_totalIndex / sampleRate) % 600.0;
Debug.WriteLine("Voltagem {0} -- {1}", voltage, voltage * 60);
if (_whichTrace == TraceAOrB.TraceA) {
_dataSeriesA.Append(time, voltage);
_dataSeriesB.Append(time, double.NaN);
traceSeriesA.Opacity = 1.0;
traceSeriesB.Opacity = 0.5;
}
else {
_dataSeriesA.Append(time, double.NaN);
_dataSeriesB.Append(time, voltage);
traceSeriesA.Opacity = 0.5;
traceSeriesB.Opacity = 1.0;
}
_currentIndex++;
_totalIndex++;
// Toggle which trace is active
if (_totalIndex % 10000 == 0) {
84
_whichTrace = _whichTrace ==
TraceAOrB.TraceA?TraceAOrB.TraceB:TraceAOrB.TraceA;
}
}
}
}
85
APÊNDICE D - Aplicação Modelo - DashBoardCompleta.Xaml
<Window x:Class="FrameWorkVFC.DashBoardCompleta" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:FrameWorkVFC" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:s="http://schemas.abtsoftware.co.uk/scichart" xmlns:s3D="http://schemas.abtsoftware.co.uk/scichart3D" mc:Ignorable="d" TextElement.Foreground="{DynamicResource MaterialDesignBody}" TextElement.FontWeight="Regular" TextElement.FontSize="13" TextOptions.TextFormattingMode="Ideal" TextOptions.TextRenderingMode="Auto" Background="{DynamicResource MaterialDesignPaper}" FontFamily="{DynamicResource MaterialDesignFont}" ResizeMode="NoResize" WindowState="Maximized" d:DesignWidth="838" Loaded="Window_Loaded" Unloaded="Window_Unloaded" > <Grid> <!--Grid da aplicacao contendo duas colunas e duas linhas redimencionaveis--> <Grid.ColumnDefinitions> <ColumnDefinition Width="5*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="3*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--Local para video da camera do Kinect--> <materialDesign:Card Margin="10 10 10 10" Grid.Column="0" Grid.Row="0"> <Image x:Name="imgKinectView" Stretch="UniformToFill" Margin="5,5,5,5"/> </materialDesign:Card> <!--Local destinado ao graficos --> <!--Em teste e utilizado para exibir as informações capturadas pelos sensores --> <Grid Margin="5,5,5,5" Grid.Row="1" Grid.ColumnSpan="2"> <!-- Define the chart with databindings to DataSet, X and Y VisibleRange --> <s:SciChartSurface s:ThemeManager.Theme="Oscilloscope" MaxFrameRate="20" x:Name="sciChartSurface"> <s:SciChartSurface.RenderSurface> <s3D:Direct3D10RenderSurface/> </s:SciChartSurface.RenderSurface> <s:SciChartSurface.RenderableSeries> <s:FastLineRenderableSeries x:Name="traceSeriesA" StrokeThickness="1"/> <s:FastLineRenderableSeries x:Name="traceSeriesB" StrokeThickness="1"/> </s:SciChartSurface.RenderableSeries> <s:SciChartSurface.XAxis>
86
<!-- Axis is given a range of 10 seconds. We have chosen our FIFO capacity given the sample rate --> <!-- to show exactly 10 seconds of data as well --> <s:NumericAxis AxisTitle="Segundos (s)" TextFormatting="0.00s" VisibleRange="0, 600" AutoRange="Always"/> </s:SciChartSurface.XAxis> <s:SciChartSurface.YAxis> <s:NumericAxis AxisTitle="Power (Hz)" VisibleRange="0.40, 2.5" AutoRange="Always"/> </s:SciChartSurface.YAxis> </s:SciChartSurface> </Grid> <!--Local para exibição dos valores--> <materialDesign:Card Margin="5 5 5 5" Grid.Column="1" Grid.Row="0"> <Grid Margin="5,5,5,5"> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="3*" /> <RowDefinition Height="3*" /> <RowDefinition Height="3*" /> </Grid.RowDefinitions> <materialDesign:Card Margin="5 5 5 10" Width="250" Height="auto" Grid.Row="0" Grid.Column="1"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="5*" /> <RowDefinition Height="4*" /> <RowDefinition Height="2*" /> </Grid.RowDefinitions> <Image x:Name="imgZoom" Height="auto" Width="auto" /> <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0 0 16 -20" Grid.Row="0"> <materialDesign:PackIcon Kind="Face" /> </Button> <StackPanel Grid.Row="1" Width="Auto" Height="Auto" > <Button Style="{StaticResource MaterialDesignFloatingActionButton}" Margin="5 5 5 5" materialDesign:ButtonProgressAssist.IsIndicatorVisible="True" materialDesign:ButtonProgressAssist.IsIndeterminate="True" Background="#FFE63F3F"> <TextBlock x:Name="lblContador" FontWeight="Bold" FontSize="40" Foreground="White" HorizontalAlignment="Center">60</TextBlock> </Button> <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center">BPM Atual</TextBlock> </StackPanel> </Grid> </materialDesign:Card> <materialDesign:Card Margin="5 5 5 5" Grid.Row="1" Grid.Column="1"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid HorizontalAlignment="Center" VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions>
87
<materialDesign:PackIcon Kind="HeartPulse" Width="100" Height="100" Foreground="Red" /> <StackPanel Grid.Column="2" Margin="5 5 5 5" VerticalAlignment="Center"> <TextBlock x:Name="lblFrequencia" Style="{StaticResource MaterialDesignDisplay1TextBlock}" TextWrapping="Wrap">Aguarde</TextBlock> <TextBlock Foreground="#FFD64A7D">Frequência Cardíaca Atual </TextBlock> </StackPanel> </Grid> </Grid> </materialDesign:Card> <materialDesign:Card Margin="5 5 5 5" Grid.Row="2" Grid.Column="1"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid HorizontalAlignment="Center" VerticalAlignment="Top" Margin="10 10 10 10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <materialDesign:PackIcon Kind="HeartPulse" Width="100" Height="100" Foreground="Red" /> <StackPanel Grid.Column="2" Margin="5 5 5 5" VerticalAlignment="Center"> <TextBlock x:Name="lblFrequenciaMaxima" Style="{StaticResource MaterialDesignDisplay1TextBlock}" TextWrapping="Wrap">Aguarde</TextBlock> <TextBlock Foreground="#FFD64A7D">Frequência Cardíaca Máxima</TextBlock> </StackPanel> </Grid> </Grid> </materialDesign:Card> <materialDesign:Card Margin="5 5 5 5" Grid.Row="3" Grid.Column="1"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid HorizontalAlignment="Center" VerticalAlignment="Top" Margin="10 10 10 10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <materialDesign:PackIcon Kind="HeartPulse" Width="100" Height="100" Foreground="Red" /> <StackPanel Grid.Column="2" Margin="5 5 5 5" VerticalAlignment="Center"> <TextBlock x:Name="lblFrequenciaMinima" Style="{StaticResource MaterialDesignDisplay1TextBlock}" TextWrapping="Wrap">Aguarde</TextBlock> <TextBlock Foreground="#FFD64A7D">Frequência Cardíaca Minima</TextBlock> </StackPanel> </Grid> </Grid>
88
</materialDesign:Card> </Grid> </materialDesign:Card> </Grid> </Window>
89
ANEXOS
Top Related