Demo Kinect Comandos por voz en ESPAÑOL

En este post explicare sobre el uso de el arreglo de microfonos del Kinect con un ejemplo donde a travez de las librerias Microsoft Speech SDK y el SDK de KINECT podremos a travez de comandos de voz dar alguna instruccion al computador para mostrar algo en una aplicacion C# natural en WPF…

Se me ocurrio un ejemplo similar al que nos trae la SDK Toolkit de Kinect donde al hablar se muestra el espectro de voz de la persona que habla, pero en este caso, utilizare para explicarles sencillamente como ejemplo una aplicacion que al decir un color de los que muestre en la aplicacion esta sea capaz de identificarlo y de pintarlo en la aplicación, dando un rango de exito o de fracaso…

Pensaran que es algo complejo de analizar y de sintetizar la voz y esas cosas pero realmente no nos complicaremos la vida y por suerte ya existen herramientas a la mano para hacer estas cosas como

Bien para esto es necesario previamente tener el Microsoft Speech SDK en este caso la version mas actual que es la 11.0 que la podemos descargar en el siguiente enlace: Microsoft Speech SDK v11.0

Tambien necesitaremos de una libreria de idioma en nuestro caso Español que debemos de desacargar de la pagina Oficial de Kinect en nuestro caso ES-mx (Español-Mexico) o ES-es (Español-España): Kinect for Windows Language Packs v11.0

Una vez instalado estas, procedemos ahora a crear un nuevo proyecto WPF en C# en Visual Studio 2012 o 2010 como lo vayan preferiendo.

blogaudiokinet1

Una vez creada la solucion del proyecto nos vamos al archivo MainWindow.xaml donde escribiremos el siguiente codigo..


<Window x:Class="WpfAudioKinectPruebaHablar.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        
        Title="Kinect Audio Demo" Height="364" Width="680">
    <Grid Background="white">
        <TextBlock Text="KINECT Demo Audio Prueba" FontSize="36" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,5,0,0" FontWeight="Light" />
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Bottom" >
            <TextBlock x:Name="tbColor" Height="100" Width="500"/>            
            <TextBlock x:Name="tbInfo" Text="" Foreground="Black" Height="50" FontSize="20" TextAlignment="Center" Padding="0,10,0,0"/>
            <TextBlock x:Name="tbSpeech" Text="" Foreground="Black" Height="60" FontSize="16" TextAlignment="Left" Padding="0,10,0,10"/>
        </StackPanel>
        <TextBlock FontSize="16" HorizontalAlignment="Left" Margin="80,93,0,0" Text="diga algo para activar el reconocimiento" VerticalAlignment="Top" Width="379" FontWeight="Light" />
        
    </Grid>
</Window>

En el codigo aparecen tres TextBlock de nombre tbColor, tbInfo, tbSpeech estos son para mostrar una breve leyenda, el color procesado y el indice de acierto…

Antes de iniciar el codigo y la logica de la aplicacion debemos de agregar las referencias o DLLs que ya habiamos mencionado que son la Microsoft.Speech y Microsoft.Kinect

addreference

ahora bien, vamos con la parte mas interesante que es la de trabajar con las librerias de Microsoft Speech y por supuesto la libreria de KINECT sdk, procedemos a abrir el code-behind de MainWindow.xaml que es MainWindow.cs; donde ya abierto, agregamos la libreria Microsoft.Kinect, las siguientes Microsoft.Speech.AudioFormat y Microsoft.Speech.Recognition


using Microsoft.Kinect;

using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;

siguiendo con el codigo creamos 5 variables de tipo SolidColorBrush y le di a cada una un color y creamos la variable kinect de tipo KinectSensor, un DispatcherTimer que le llame readyTimer y dos variables muy interesantes que vienen de la libreria Speech que son EnergyCalculatingPassThroughStream que es la variable que contendra el espectro de la energia y la forma de la voz sintetizada para a travez de la segunda variable que es la que crea una especie de gramatica dependiendo de la cultura y el enfasis de la voz la como se muestra en el siguiente codigo.


 private readonly SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
 private readonly SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
 private readonly SolidColorBrush yellowBrush = new SolidColorBrush(Colors.Yellow);
 private readonly SolidColorBrush blueBrush = new SolidColorBrush(Colors.Blue);
 private readonly SolidColorBrush WhiteBrush = new SolidColorBrush(Colors.White);

 KinectSensor kinect;

 private DispatcherTimer readyTimer;
 private EnergyCalculatingPassThroughStream stream;
 private SpeechRecognitionEngine speechRecognizer;

«Suena dificil y complejo pero son solo sencillas primitivas para trabajar el reconocimiento de voz en nuestras aplicaciones».. aca les dejo unas rutinas establecidas para el control de la voz, sintetizarla y despues tomarla en la variable para asi ahora poder trabajar con ella a travez del Kinect, por ahora solo les dejo el siguiente codigo:

 private class EnergyCalculatingPassThroughStream : Stream
 {
   private const int SamplesPerPixel = 10;

   private readonly double[] energy = new double[500];
   private readonly object syncRoot = new object();
   private readonly Stream baseStream;

   private int index;
   private int sampleCount;
   private double avgSample;

 public EnergyCalculatingPassThroughStream(Stream stream)
 {
   this.baseStream = stream;
 }

 public override long Length
 {
   get { return this.baseStream.Length; }
 }

 public override long Position
 {
   get { return this.baseStream.Position; }
   set { this.baseStream.Position = value; }
 }

 public override bool CanRead
 {
   get { return this.baseStream.CanRead; }
 }

 public override bool CanSeek
 {
   get { return this.baseStream.CanSeek; }
 }

 public override bool CanWrite
 {
   get { return this.baseStream.CanWrite; }
 }

 public override void Flush()
 {
   this.baseStream.Flush();
 }

 public void GetEnergy(double[] energyBuffer)
 {
   lock (this.syncRoot)
   {
     int energyIndex = this.index;
     for (int i = 0; i < this.energy.Length; i++)       {       energyBuffer[i] = this.energy[energyIndex];       energyIndex++;       if (energyIndex >= this.energy.Length)
     {
       energyIndex = 0;
     }
   }
  }
 }

  public override int Read(byte[] buffer, int offset, int count)
  {
    int retVal = this.baseStream.Read(buffer, offset, count);
    const double A = 0.3;
    lock (this.syncRoot)
    {
    for (int i = 0; i < retVal; i += 2)      {        short sample = BitConverter.ToInt16(buffer, i + offset);        this.avgSample += sample * sample;        this.sampleCount++;        if (this.sampleCount == SamplesPerPixel)        {          this.avgSample /= SamplesPerPixel;          this.energy[this.index] = .2 + ((this.avgSample * 11) / (int.MaxValue / 2));          this.energy[this.index] = this.energy[this.index] > 10 ? 10 : this.energy[this.index];

        if (this.index > 0)
        {
          this.energy[this.index] = (this.energy[this.index] * A) + ((1 - A) * this.energy[this.index - 1]);
        }

     this.index++;

     if (this.index >= this.energy.Length)
     {
       this.index = 0;
     }

       this.avgSample = 0;
       this.sampleCount = 0;
    }
   }
  }

  return retVal;
 }

  public override long Seek(long offset, SeekOrigin origin)
  {
    return this.baseStream.Seek(offset, origin);
  }

  public override void SetLength(long value)
  {
    this.baseStream.SetLength(value);
  }

  public override void Write(byte[] buffer, int offset, int count)
  {
    this.baseStream.Write(buffer, offset, count);
  }
 }

Y aca les dejo todo codigo fuente para poner a andar nuestro Kinect por voz y en español!…. despues les seguire mostrando detalladamente cada una de las funcionalidades de este demo para que lo apliquen a un sin fin de cosas.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

using Microsoft.Kinect;

using Microsoft.Speech.AudioFormat;
using Microsoft.Speech.Recognition;
namespace WpfAudioKinectPruebaHablar
{
 ///
 /// Interaction logic for MainWindow.xaml
 ///
 public partial class MainWindow : Window
 {
 private readonly SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
 private readonly SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
 private readonly SolidColorBrush yellowBrush = new SolidColorBrush(Colors.Yellow);
 private readonly SolidColorBrush blueBrush = new SolidColorBrush(Colors.Blue);
 private readonly SolidColorBrush WhiteBrush = new SolidColorBrush(Colors.White);

 KinectSensor kinect;

 private DispatcherTimer readyTimer;
 private EnergyCalculatingPassThroughStream stream;
 private SpeechRecognitionEngine speechRecognizer;

 #region class EnergyCalculatingPassThroughStream

 private class EnergyCalculatingPassThroughStream : Stream
 {
 private const int SamplesPerPixel = 10;

 private readonly double[] energy = new double[500];
 private readonly object syncRoot = new object();
 private readonly Stream baseStream;

 private int index;
 private int sampleCount;
 private double avgSample;

 public EnergyCalculatingPassThroughStream(Stream stream)
 {
 this.baseStream = stream;
 }

 public override long Length
 {
 get { return this.baseStream.Length; }
 }

 public override long Position
 {
 get { return this.baseStream.Position; }
 set { this.baseStream.Position = value; }
 }

 public override bool CanRead
 {
 get { return this.baseStream.CanRead; }
 }

 public override bool CanSeek
 {
 get { return this.baseStream.CanSeek; }
 }

 public override bool CanWrite
 {
 get { return this.baseStream.CanWrite; }
 }

 public override void Flush()
 {
 this.baseStream.Flush();
 }

 public void GetEnergy(double[] energyBuffer)
 {
 lock (this.syncRoot)
 {
 int energyIndex = this.index;
 for (int i = 0; i < this.energy.Length; i++) { energyBuffer[i] = this.energy[energyIndex]; energyIndex++; if (energyIndex >= this.energy.Length)
 {
 energyIndex = 0;
 }
 }
 }
 }

 public override int Read(byte[] buffer, int offset, int count)
 {
 int retVal = this.baseStream.Read(buffer, offset, count);
 const double A = 0.3;
 lock (this.syncRoot)
 {
 for (int i = 0; i < retVal; i += 2) { short sample = BitConverter.ToInt16(buffer, i + offset); this.avgSample += sample * sample; this.sampleCount++; if (this.sampleCount == SamplesPerPixel) { this.avgSample /= SamplesPerPixel; this.energy[this.index] = .2 + ((this.avgSample * 11) / (int.MaxValue / 2)); this.energy[this.index] = this.energy[this.index] > 10 ? 10 : this.energy[this.index];

 if (this.index > 0)
 {
 this.energy[this.index] = (this.energy[this.index] * A) + ((1 - A) * this.energy[this.index - 1]);
 }

 this.index++;

 if (this.index >= this.energy.Length)
 {
 this.index = 0;
 }

 this.avgSample = 0;
 this.sampleCount = 0;
 }
 }
 }

 return retVal;
 }

 public override long Seek(long offset, SeekOrigin origin)
 {
 return this.baseStream.Seek(offset, origin);
 }

 public override void SetLength(long value)
 {
 this.baseStream.SetLength(value);
 }

 public override void Write(byte[] buffer, int offset, int count)
 {
 this.baseStream.Write(buffer, offset, count);
 }
 }
 #endregion

public MainWindow()
{
 InitializeComponent();

 kinect = KinectSensor.KinectSensors[0];

 if (this.kinect != null)
 {
 this.InitializeKinect();
 }
}

 private void Start()
 {
 var audioSource = this.kinect.AudioSource;
 audioSource.BeamAngleMode = BeamAngleMode.Adaptive;

 audioSource.EchoCancellationMode = EchoCancellationMode.CancellationAndSuppression;
 var kinectStream = audioSource.Start();

 this.stream = new EnergyCalculatingPassThroughStream(kinectStream);
 this.speechRecognizer.SetInputToAudioStream(this.stream, new SpeechAudioFormatInfo
 (EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
 this.speechRecognizer.RecognizeAsync(RecognizeMode.Multiple);
 }

 private static RecognizerInfo GetKinectRecognizer()
 {
 Func<RecognizerInfo, bool> matchingFunc = r =>
 {
 string value;
 r.AdditionalInfo.TryGetValue("Kinect", out value);
 return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) &&
 "es-ES".Equals(r.Culture.Name,StringComparison.InvariantCultureIgnoreCase);
 };
 return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
 }

 private SpeechRecognitionEngine CreateSpeechRecognizer()
 {
 RecognizerInfo ri = GetKinectRecognizer();
 if (ri == null)
 {
 MessageBox.Show(@"Hay un problema al inicializar el dispositivo de reconocimiento de voz.
 asegurese de tener Microsoft Speech SDK instalado y configurado.",
 "Falla al cargar Speech SDK", MessageBoxButton.OK, MessageBoxImage.Error);

 this.Close();
 return null;
 }

 SpeechRecognitionEngine sre;
 try
 {
 sre = new SpeechRecognitionEngine(ri.Id);
 }
 catch
 {
 MessageBox.Show(@"Hay un problema al inicializar el dispositivo de reconocimiento de voz.
 asegurese de tener Microsoft Speech SDK instalado y configurado.", "Falla al cargar Speech SDK",
 MessageBoxButton.OK, MessageBoxImage.Error);
 this.Close();
 return null;
 }

 var colors = new Choices();
 colors.Add("rojo");
 colors.Add("verde");
 colors.Add("azul");
 colors.Add("amarillo");

 var gb = new GrammarBuilder { Culture = ri.Culture };
 gb.Append(colors);

 // Create the actual Grammar instance, and then load it into the speech recognizer.
 var g = new Grammar(gb);

 sre.LoadGrammar(g);
 sre.SpeechRecognized += this.SreSpeechRecognized;
 sre.SpeechHypothesized += this.SreSpeechHypothesized;
 sre.SpeechRecognitionRejected += this.SreSpeechRecognitionRejected;

 return sre;
 }

 private void RejectSpeech(RecognitionResult result)
 {
 string status = "Rechazado: " + (result == null ? string.Empty : result.Text + " " + result.Confidence);
 this.ReportSpeechStatus(status);

 Dispatcher.BeginInvoke(new Action(() => { tbColor.Background = WhiteBrush; }), DispatcherPriority.Normal);
 }

 private void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
 {
 this.RejectSpeech(e.Result);
 }

 private void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
 {
 this.ReportSpeechStatus("Hipotetico: " + e.Result.Text + " " + e.Result.Confidence);
 }

 private void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
 {
 SolidColorBrush brush;

 if (e.Result.Confidence < 0.7) { this.RejectSpeech(e.Result); return; } switch (e.Result.Text.ToUpperInvariant()) { case "ROJO": brush = this.redBrush; break; case "VERDE": brush = this.greenBrush; break; case "AZUL": brush = this.blueBrush; break; case "AMARILLO": brush = this.yellowBrush; break; default: brush = this.WhiteBrush; break; } string status = "Reconocido: " + e.Result.Text + " " + e.Result.Confidence; this.ReportSpeechStatus(status); Dispatcher.BeginInvoke(new Action(() => { tbColor.Background = brush; }), DispatcherPriority.Normal);
 }

 private void ReportSpeechStatus(string status)
 {
 Dispatcher.BeginInvoke(new Action(() => { tbSpeech.Text = status; }), DispatcherPriority.Normal);
 }

 private void UpdateInstructionsText(string instructions)
 {
 Dispatcher.BeginInvoke(new Action(() => { tbInfo.Text = instructions; }), DispatcherPriority.Normal);
 }

 private void MainWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
 {
 this.UninitializeKinect();
 }

 private void ReadyTimerTick(object sender, EventArgs e)
 {
 this.Start();
 this.ReportSpeechStatus("Listo para reconocer voz!");
 this.UpdateInstructionsText("Diga: 'rojo', 'verde', 'amarillo' o 'azul'");
 this.readyTimer.Stop();
 this.readyTimer = null;
 }

 private void InitializeKinect()
 {
 var sensor = this.kinect;
 this.speechRecognizer = this.CreateSpeechRecognizer();
 try
 {
 sensor.Start();
 }
 catch (Exception)
 {

 }

 if (this.speechRecognizer != null && sensor != null)
 {
 // NOTE: Need to wait 4 seconds for device to be ready to stream audio right after initialization
 this.readyTimer = new DispatcherTimer();
 this.readyTimer.Tick += this.ReadyTimerTick;
 this.readyTimer.Interval = new TimeSpan(0, 0, 4);
 this.readyTimer.Start();

 this.ReportSpeechStatus("Inicializando el dispositivo de audio Kinect...");
 this.UpdateInstructionsText(string.Empty);

 this.Closing += this.MainWindowClosing;
 }
 }

 private void UninitializeKinect()
 {
 var sensor = this.kinect;

 if (this.speechRecognizer != null && sensor != null)
 {
 sensor.AudioSource.Stop();
 sensor.Stop();
 this.speechRecognizer.RecognizeAsyncCancel();
 this.speechRecognizer.RecognizeAsyncStop();
 }

 if (this.readyTimer != null)
 {
 this.readyTimer.Stop();
 this.readyTimer = null;
 }
 }

 }
}

nota: esta entrada esta sujeta a cambios por la actualizacion de la libreria KINECT, luego les seguire comentando acerca de esto, por ahora solo les adelanto este post!…

Tutorial KINECT camaras RGB y de profundidad (DEPTH)

Hola
Empezaremos por lo básico, en Visual Studio 2010 creamos un nuevo proyecto WPF y en mi caso le llamare KinectPruebaCamaras.

Ahora bien una vez creado el proyecto con sus archivos iniciales nos vamos a agregar la referencia para trabajar con nuestra KINECT

Ok hasta este punto ahora vamos a nuestro código XAML de nuestra aplicación e insertamos dos controles de imagen dentro del Grid inicial donde la primera le llamaremos VideoImage y a la otra DepthImage

Bien ahora vamos a lo que en realidad tenemos que hacer, nos vamos al code behind de nuestro archivo MainPage.xaml y empezamos por lo siguiente agregando la Referencia con el siguiente código.

using Microsoft.Kinect;

ahora declaramos una variable de tipo KinectSensor que yo llamare sensor y dos variables una de tipo byte que llamare pixeldata y otra de tipo short que llamare pixel.

KinectSensor sensor = KinectSensor.KinectSensors[0];
byte[] pixelData;
short[] pixel;

Ahora debemos de habilitar las camaras con estas dos rutinas, uno para la cámara de RGB y la otra para la cámara de profundidad (DEPTH),  debemos de indicar al Kinect que vamos a utilizar en este caso las dos camaras antes mencionadas.

public partial class MainWindow : Window
{
      KinectSensor sensor = KinectSensor.KinectSensors[0];
      byte[] pixelData;
      short[] pixel;

      public MainWindow()
      {
             InitializeComponent();
             sensor.ColorStream.Enable();
             sensor.DepthStream.Enable();
          
      }
}

“Vamos bien ahí”… bueno ahora necesitamos indicar tanto iniciar el KINECT al correr la aplicación y cuando cerremos la aplicación con estas dos sencillas rutinas Start() y Stop()… (“sencillo no!?”) y en la sección del método Window_Loaded vamos a crear dos eventos ruteados dentro de la variable sensor que yo llame que contiene toda la información del dispositivo que son ColorFrameReady y DepthFrameReady.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
   sensor.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(sensor_ColorFrameReady);
   sensor.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(sensor_DepthFrameReady);

   sensor.Start();
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
   sensor.Stop();
}

Bien ya creamos los objetos, tenemos las librerías de Kinect, inicializamos y cerramos nuestro dispositivo ahora bien falta capturar lo que nos llega de las dos camaras y guardarlas en una variable y mostrarlas en nuestra aplicación para poder empezar a trabajar en nuestro KINECT ahora vamos al método sensor_ColorFrameReady y colocamos el siguiente código.

void sensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
{
  bool receivedData = false;

  using (ColorImageFrame CFrame = e.OpenColorImageFrame())
  {
     if (CFrame == null)
     {
     }
     else
     {
     pixelData = new byte[CFrame.PixelDataLength];
     CFrame.CopyPixelDataTo(pixelData);
     receivedData = true;
     }
   }

   if (receivedData)
   {
     BitmapSource source = BitmapSource.Create(640, 480, 96, 96,
     PixelFormats.Bgr32, null, pixelData, 640 * 4);

     VideoImage.Source = source;
   }
      
 }

En el código que acabo de colocar creamos una variable de tipo boleana para detectar mediante una condición para capturar lo que nos arroja la cámara copiando los datos de lo que nos contiene CFrame y después volvemos a preguntar por nuestra variable boleana y de ser verdadero procede a crear un BitmapSource en el cual abrimos dándole los parámetros que demuestro en el código, detenerme a explicar los parámetros no son necesarios por ahora para este tutorial rapido pero en mi primer tutorial son los mismos parámetros que describi y explique, la novedad en esa función es que se pueden jugar ahora con valores superiores o inferiores en escalas proporcionales a la que colocamos por default que es 640 x 480.

Entonces bien ahora vamos a la siguiente cámara que es la de profundidad dando este codigo

void sensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            bool receivedData = false;

            using (DepthImageFrame CFrame = e.OpenDepthImageFrame())
            {
                if (CFrame == null)
                {
                }
                else
                {
                    pixel = new short[CFrame.PixelDataLength];
                    CFrame.CopyPixelDataTo(pixel);
                    receivedData = true;
                }
            }

            if (receivedData)
            {
                BitmapSource source = BitmapSource.Create(320, 240, 96, 96,
                        PixelFormats.Gray16, null, pixel, 320 * 4);

                DepthImage.Source = source;
            }
        }

Es el mismo tratamiento que explique en el método anterior pero lo que cambia es el formato de Pixeles y le colocamos Gray16 (PixelFormats.Gray16) y cambiamos la resolución a 320 x 240 claro también podemos colocarle la resolución de la otra cámara 640 x 480

Ahora corremos nuestra aplicación y tenemos las 2 camaras, tanto la de profundidad  como la RGB funcionando y habilitadas para trabajar con nuestra KINECT, es importante destacar que estos son los métodos básicos para tener  en funcionamiento estas 2 camaras, ya el resto queda de parte de nuestra creatividad

Estoy trabajando actualmente en el siguiente Tutorial para reconocimiento del personaje y de las librerías Skeleton para hacer infinidades de cosas con nuestro KINECT.

Salu2

Tutorial KINECT Hello World en la PC

Hola, en este artículo vamos a explicar y a demostrar conceptos básicos acerca del dispositivo de interfaz natural de usuario o NUI (Natural User Interface) , Microsoft KINECT y explicar brevemente como conectar, inicializar y mostrar las diferentes cámaras y funciones de reconocimiento de gestos a través de este dispositivo con el SDK oficial liberado hace ya unos meses por Microsoft.
Como la mayoría sabemos KINECT es un dispositivo que fue creado por Microsoft en un principio para el entretenimiento a través de la consola XBOX 360, pero este a su vez ha sido un tema a fondo para el uso en diversos aspectos como la investigación académica, la ciencia, para fines empresariales, para nuevas tendencias, etc…
Dejando a un lado la diversión y colocando temas muy serios en cuanto a revolucionar nuestro estilo de vida de cómo interactuar con los sistemas bajo un esquema donde la persona es el ratón o el teclado…
Pero bien basta de charlar y vamos al código, a lo que en verdad quiero demostrarles.
Primero debemos de tener a la mano todo para comenzar:
Hardware:
  • Sensor Microsoft KINECT.
  • Cable conversor de puerto KINECT a USB. (este cable esta incluido cuando compras el KINECT aparte, para la versión que viene con el Xbox 360 no viene con esta extensión, pero se puede conseguir fácilmente en una tienda Microsoft Store o en tiendas electrónicas, Amazon, etc.)
  • PC con 2.66 GHZ de procesador o mucho más rápido con 2Gb de memoria RAM si tienes más, excelente.
  • Microsoft Windows 7 en sus diferentes versiones compatible con tarjetas gráficas con Direct X 9.0c
Software:
Teniendo estos requisitos básicos procedemos primero con la instalación del Microsoft KINECT SDK tal y como se muestra en la siguiente imagen.

 
Ahora bien iniciamos Visual Studio 2010 y creamos un nuevo proyecto de aplicación Windows WPF, yo lo llame HelloWorldKinect, pueden colocarle el nombre que deseen.
Una vez creado el proyecto en el diseñador nos situamos en el codigo XAML y colocamos dos controles de tipo imagen, para este ejemplo solo coloque uno grande que abarque toda la ventana de nombre depthImagenpara la camara de profundidad  y uno pequeño en la parte superior derecha que me mostrara el contenido de la camara de video de nombre videoImage tal como se muestra en la figura.
 En la pestaña de Explorador de Soluciones, hacemos clic derecho en la carpeta References y le damos a la opción Add References y agregamos la referencia Microsoft.Research.Kinect tal como se muestra en las imágenes. Esta parte es importante ya que sin las referencias no podemos iniciar los controles para poder trabajar con nuestro dispositivo KINECT.
 
Si estas en el diseñador solo debes de presionar F7 y estarás en el Code Behind del formulario, nos situamos en la parte superior del código ahora bien debemos de hacer la referencia en el código colocando la siguiente línea tal y como se muestra en la imagen.

 
Debemos de crear una variable de tipo Runtime (esta variable es en si el dispositivo como tal para poder manejar sus diferentes streamsy funciones que nos ofrece la SDK de KINECT) tal y como muestra la imagen.
 
Luego de esto debemos de crear dos eventos ruteados, uno de nombre Loaded y otro Unloaded (esto es porque estos métodos deben de asegurarnos tanto el cargar todo en memoria como el otro para cerrar el dispositivo).
 Así como también debemos de crear dos eventos ruteados para crear y abrir las cámaras del dispositivo como se muestra en la imagen.
Bien ahora en el evento Loaded debemos de inicializar el dispositivo y posteriormente se deben de crear dos rutinas para abrir el dispositivo para poder mostrar los streams de profundidad y el otro de RGB, dándole como variables el tipo de stream, la resolución y el tipo de imagen del stream.
En el evento Unloaded con solo colocar la siguiente rutina este se encargara de cerrar el dispositivo una vez terminada la aplicación.

Como último código propuesto vamos a los dos métodos VideoFrameReadyy DepthFrameReady  crearemos una variable de tipo PlanarImage asignándole lo que nos trae el resultado del evento que siempre está en ejecución mostrando la imagen y después una variable de tipo BitmapSource en la que crearemos a partir del stream correspondiente, pasándole como parámetros, el ancho, el alto, los DPIde cada imagen (por defecto son 96), el formato de sus pixeles en lo que la diferencia de que una es RGB o BGR32 para el video y para profundidad daré una paleta de colores gris de 16 bits o Gray16correspondiente en cada método, luego los bits en memoria a partir de la variable image y por ultimo asignamos todo al control de tipo imagen que habíamos preparado y definido en el diseño de la interfaz.  

Bien nuestro código está listo, ahora solo corremos la aplicación F5 y como buena recompensa nuestro Hello World en KINECT.

Es de resaltar que estos códigos prácticamente son preestablecidos y son primordiales para iniciar el dispositivo y trabajar en conjunto con él para cualquier fin, ya vendrá de parte de Microsoft el ofrecer otras alternativas o mediante otras versiones nuevas de esta SDKmuchas más opciones para programar y trabajar con este dispositivo, es de constar también que esta SDK es de solo desarrollo e investigación ya que no está no fue liberada bajo un perfil comercial o para un fin de lucro, ya veremos en las siguientes versiones.

En mi siguiente entrega les explicare mediante una aplicación definiendo a fondo el uso de las funciones Skeletons a través de sus Joints(coyunturas), y el uso de patrones para reconocer gestos, estos son en sí combinados con profundidad para el diseño de aplicaciones bien interesantes para el uso común, diversión, investigación y afines.
Aca les dejo un enlace para descargar el codigo fuente y el proyecto como tal
http://www.megaupload.com/?d=6QX8U3Y8

Enjoy… Salu2… 😀

PHPLinq!!… una mejor forma de utilizar los datos




Language-Integrated Query (LINQ) es un conjunto de características presentado en Visual Studio 2008 y 2010 que agrega capacidades de consulta eficaces a la sintaxis de los lenguajes C# y Visual Basic. LINQ incluye patrones estándar y de fácil aprendizaje para consultar y actualizar datos, y su tecnología se puede extender para utilizar potencialmente cualquier tipo de almacén de datos.

Tradicionalmente, las consultas con datos se expresan como cadenas sencillas, sin comprobación de tipos en tiempo de compilación ni compatibilidad con IntelliSense. Además, es necesario aprender un lenguaje de consultas diferente para cada tipo de origen de datos: bases de datos SQL, documentos XML, servicios Web diversos, etc. LINQ convierte una consulta en una construcción de lenguaje de primera clase en C# y Visual Basic. Las consultas se escriben para colecciones de objetos fuertemente tipadas, utilizando palabras clave del lenguaje y operadores con los que se está familiarizado. La ilustración siguiente muestra una consulta LINQ parcialmente completada en una base de datos SQL Server en C#, con comprobación de tipos completa y compatibilidad con IntelliSense.

Ahora bien PHPLinq nos ofrece la oportunidad de ahorrar código PHP para nuestros ciclos o bucles de lenguaje como un while o un foreach. Es difícil contarlo con palabras, voy a comentarlo mejor con un ejemplo.

Ejemplo:

Digamos que tenemos una matriz de cadenas y deseamos seleccionar sólo las cadenas cuya longitud es < 5. Con PHPLinq la manera de lograr esto sería el siguiente:

$nombres = array(“John”, “Peter”, “Joe”, “Patrick”, “Donald”, “Eric”);

$resultado = from($nombre‘)->in($nombres)

->where($nombre => strlen($nombre) < 5)

->select($nombre);

Lo ves familiar a SQL verdad? No escribirá un bucle más de esta serie, el control de la cadena de longitud, y añadiendo una variable temporal todo solucionado. Se entiende fácilmente con el ejemplo.

De todos modos acá les muestro como es la arquitectura de capas de esta librería de datos muy potente para mostrar los forma mas sencilla y dinámica.

Papervision 3D: Otra alternativa 3D para flash


Cada vez que navegamos por la Web, nos encontramos con la tecnología Flash en variadas aplicaciones. Ello se debe a que de un tiempo a esta parte, son bastante empleadas en los sitios Web para enriquecer el contenido, elaborando animaciones que resaltan las estructuras, es decir, páginas Web totalmente multimedia.

Hasta hace un tiempo, solo se veía a esta tecnología cómo un conjunto de fotogramas, que permitían elaborar un contenido interactivo, pero en forma 2D (dos dimensiones). Esto era una desventaja evidente frente a la tecnología que le hacia competencia directamente, el Javascript. Esta última trabaja, con aplicaciones que permiten generar un ambiente 3D (tres dimensiones).

Qué es Papervision 3D

Papervision 3D, es un proyecto que ha visto la luz hace ya tiempo, pero que recién nos esta mostrando sus primeros frutos, los cuales son realmente sorprendentes. Se trata de una iniciativa privada, libre y adesarrollada través de un proyecto colaborativo.

Así, Papervision 3D, se constituye en una implementación novedosa y esencial a los parámetros que ya tenia Flash. Dejando de lado las simples animaciones de fotogramas en 2D, para pasar a una interactiva secuencia en 3D.

Ejemplos en 3D elaborados con Papervision – imagen obtenida del Blog oficial, autor Den Ivanovs.

Ventajas de emplear Papervision 3D

Más allá de que se haya convertido en necesidad una tecnología en tres dimensiones para Flash, esta vendrá incorporando muchas posibilidades nuevas, así por ejemplo, tendremos la posibilidad de:

1. Diseños Web más dinámicos, las nuevas Web que se desarrollen con esta tecnología podrían incorporar espacios virtuales donde los artículos, noticias, y demás contenidos puedan ser desplegados de una manera totalmente diferente. Imagínense un Banco de contenido, cuya información este disponible en 3D, esto será posible.

2. Aplicaciones más dinámicas. Flash no solo es usado para las páginas Web, es notoria su presencia en distintas aplicaciones, desde un celular o Ipod hasta una sofisticada computadora.

En el campo de los juegos, flash se ha convertido en un motor de desarrollo importante, lográndose con el un diseño aceptable y facilitando la jugabilidad. Imagínense los juegos que podrían diseñarse al incorporarse una tecnología que ofrezca el aspecto 3D, pues simplemente seria una novedad y disfrute para el usuario y excelente beneficio para un sitio Web comercial que se dedique a promocionar ests tipo de entretenimiento.

3. Mayor interacción para los visitantes. Si de diseño dinámico se trata, con esta nueva tecnología, las paginas no solo serán vistosas y bonitas. Permitirá que los visitantes interactúen de una manera diferente, encontrándose tal vez, con una Tienda virtual, una visita guiada, etc.

4. A través de Papervision 3D Flash, tendrás a disposición toda la tecnología 3D. Empleando por ejemplo, aplicaciones como SourceBinder, un nodo de base visual para facilitar la creación de un prototipo, podrás aplicar Flash basádote en aplicaciones visuales.

Algunas limitaciones de Papervision

Papervisión 3D está disponible para descarga en la Web del proyecto o ingresando a Google code. La versión actual de Papervision3D disponible es la 2.0 alfa AKA “Great White” y puede encontrarse en forma de repositorio.

Google code Papervision 3D – imagen obtenida de Google code.

Sin embargo hay algunas limitaciones, las cuales se van solucionando a través de la colaboración y desarrollo en la Web del proyecto, más que todo se trata de incompatibilidades con otros software o cuestiones relacionadas con el lenguaje y la programación. Sin embargo no son asuntos que conlleven mucha importancia ni impidan su uso extensivo.

1. Limitaciones de Hardware, Al parecer la tecnología 3D solo va a poder ser utilizada en equipos con un rendimiento óptimo. Dónde aspectos como la velocidad del bus, la cache, memoria de video y otros requerimiento técnicos van a influir definitivamente.

Esto significa que si deseas instalarlo y emplearlo debes tener un ordenador con una buena memoria de video (128 mínimo), un procesador Pentium IV o similar con una velocidad de procesamiento aceptable (unos 2.4 GHZ como mínimo) y una memoria RAM sufiente para ayudar al funcionamiento ligero de la aplicación (estimo que sobre 1 GB). Este punto es un limitante, sobre todo si es un software libre, pero desgraciadamente, y lo sabemos todos, los programas de diseño requieren necesariamente de un soporte de hardware importante.

2. Flash no debe depender de aspectos físicos. Otra de las posturas es que flash no debería de depender de aspectos cómo la aceleración de gráficos y otros. Estas tecnología son comerciales y se contraponen al empleo libre del software; sin embargo como señalé, este es un programa especializado por lo tanto esta orientado a desarrolladores y creadores 3D, labor que no todos podemos (me incluyo) realizar, por lo que de todas formas su uso se vera limitado a un grupo sectorizado.

Con todo lo expuesto hasta aquí, coincidimos en que Flash no deja de sorprendernos. Siendo una tecnología abiertamente conocida, es de gran interés todo lo que tras ella se este incubando. Al parecer Flash va a estar a la altura de su tecnología competidora, Javascript. Muchos de los desarrolladores del mundo entero, ya están desarrollando y compartiendo sus modelos 3D a traves de la Wiki de Papervision, en el mismo blog oficial o a través de Google Code Papervision 3D el asunto es ahora lograr la interacción de este software con los diferentes programas y lenguajes similares.

Imagínense desarrollar modelos 3D de un edificio, de una tienda, de un juego. Las posibilidades son inmensas y muy prometedoras. Flash, ha dejado de ser una aplicación con diseños bonitos y pocos efectos, la tecnología 3D le otorgará un nuevo rostro.

Papervision 3D, bastante útil.

No cabe duda de que, Adobe (actual propietaria de los derechos de Flash) dejará completamente libre esta tecnología y el software necesario para implementarlo. Sin embargo gracias al proyecto colaborativo de Papervision 3D, podemos tener acceso a diseños Webs mucho más interactivos, así como la posibilidad de desarrollar presentaciones, modelos y aplicaciones interactivas para páginas Web y Blogs.

Papervisión se presenta además como una alternativa útil para el diseño publicitario, y como una forma de presentar proyectos (como por ejemplo una casa) en un desarrollo tridimensional.