ColorImageFrameReadyEventArgs e)
{
imagemCamera.Source =
ObterImagemSensorRGB(e.OpenColorImageFrame() );
}
Caso o Kinect leve mais tempo para inicializar que a primeira chamada do mé-
todo descrito anteriormente, a aplicação lançará uma exceção, então, antes do comando using no método ObterImagemSensorRGB é interessante fazer uma va-
lidação do quadro atual, ou seja, verificar se o quadro não está nulo.
Se você fez tudo certo e executar a aplicação já irá ver o vídeo em tempo real do que o Kinect está vendo!
Utilizando um filtro de imagem
Que tal incrementar ainda mais nossa aplicação utilizando um filtro de imagem?
Iremos fazer o exemplo utilizando um filtro simples para deixar a imagem em escala cinza (popularmente chamado de preto e branco). Primeiro faremos com que o filtro para escala cinza seja opcional, então criaremos um componente do tipo CheckBox no mesmo local onde ficava o botão para bater foto e este componente irá nos indicar quando deveremos aplicar o filtro.
Para
aplicarmos
o
filtro,
é
preciso
refatorar
o
método
ObterImagemSensorRGB. Como você deve ter percebido nós podemos ma-
nipular cada pixel da imagem do quadro atual, então vamos utilizar um algoritmo simples para converter as cores RGB deste pixel em escala cinza.
Após copiarmos os dados da imagem para o array de pixel
byte[]
bytesImagem, faremos um laço de repetição para percorrer todos os pixels subs-
tituindo o valor das cores R, G e B pelo valor mais alto dos três (utilize o método Math.Max). Isso será feito porque toda a escala cinza de cores possui os valores RGB idênticos.
É importante ter em mente nesta implementação que um pixel não é necessari-
amente um byte. Dependendo do formato de cores são necessários mais de um byte
para preencher um pixel esta informação pode ser obtida através da propriedade 36
Casa do Código
Capítulo 4. Fluxo de Cores
BytesPerPixel do objeto ColorImageFrame. No caso do formato padrão, cada
pixel é representado por 4 bytes, um para o R, um para o G, um para o B e um para o Alpha (transparência), sendo assim, nosso laço de repetição deve pular de 4 em
4 bytes. Caso você tenha tentado refatorar o método, ele deve estar semelhante ao código a seguir.
private BitmapSource ObterImagemSensorRGB(ColorImageFrame quadro)
{
if (quadro == null) return null;
using (quadro)
{
byte[] bytesImagem = new byte[quadro.PixelDataLength];
quadro.CopyPixelDataTo(bytesImagem);
if (chkEscalaCinza.IsChecked.HasValue &&
chkEscalaCinza.IsChecked.Value)
for (int indice = 0;
indice < bytesImagem.Length;
indice += quadro.BytesPerPixel)
{
byte maiorValorCor = Math.Max(bytesImagem[indice],
Math.Max(bytesImagem[indice + 1],
bytesImagem[indice + 2]));
bytesImagem[indice] = maiorValorCor;
bytesImagem[indice + 1] = maiorValorCor;
bytesImagem[indice + 2] = maiorValorCor;
}
return BitmapSource.Create(quadro.Width, quadro.Height,
96, 96, PixelFormats.Bgr32, null, bytesImagem,
quadro.Width * quadro.BytesPerPixel);
}
}
O componente de tela no meu caso se chama chkEscalaCinza e ele é utilizado na
verificação antes do laço de repetição. Com isso, já podemos executar nosso código e ao marcar o CheckBox teremos um resultado semelhante ao da figura 4.6.
37
4.3. Refatoração
Casa do Código
Figura 4.6: Filtro para escala cinza
4.3
Refatoração
Você deve se lembrar que na seção 1.3 havia sido mencionado o Kinect
toolkit;
pois bem, utilizaremos o toolkit (Microsoft.Kinect.Toolkit, Micro-
soft.Kinect.Toolkit.Controls e Microsoft.Kinect.Toolkit.Controls.Interaction) para fazer uma refatoração em nosso código.
Refatorando a DLL AuxiliarKinect
Primeiro vamos refatorar nosso projeto AuxiliarKinect, você se lembra que o método de inicialização não prevê nenhum tipo de problema? Bom, já está na
hora de resolvermos isso. Primeiro adicione a referência somente à DLL Micro-
soft.Kinect.Toolkit. Nesta DLL há uma classe chamada KinectSensorChooser,
que é feita para ser utilizada como um seletor do sensor Kinect e ela mesma já gerencia as possíveis exceções.
38
Casa do Código
Capítulo 4. Fluxo de Cores
Refatoração
Nesta seção iremos refatorar a DLL AuxiliarKinect, então caso você
queira manter as implementações antigas sugiro que faça uma cópia deste
projeto antes de efetuar estas novas alterações.
A primeira coisa que faremos na nossa classe InicializadorKinect é criar
uma propriedade da classe KinectSensorChooser chamada SeletorKinect.
Sugiro que esta propriedade tenha o método get público e o método set privado.
Também criaremos uma propriedade totalmente pública neste classe do tipo
Action<KinectSensor>. Esta propriedade é um delegate para a função que precisará ser invocada sempre que o sensor for inicializado, falaremos mais sobre ela mais tarde.
Criaremos também um construtor para nossa classe, que deve instanciar a
propriedade
SeletorKinect, associar um método que interprete o evento
KinectChanged e inicializar o seletor, conforme o código a seguir.
public Action<KinectSensor> MetodoInicializadorKinect