Este sitio utiliza cookies de Google para prestar sus servicios y analizar su tráfico. Tu dirección IP y user-agent se comparten con Google, junto con las métricas de rendimiento y de seguridad, para garantizar la calidad del servicio, generar estadísticas de uso y detectar y solucionar abusos.Más información

View site in english Ir a la página de inicio Contacta conmigo
lunes, 23 de julio de 2018

Reconocimiento de posturas con Kinect V

Con este artículo se cierra la serie sobre el reconocimiento de posturas utilizando el sensor Kinect de Microsoft. Para terminar, simplemente mostraré la implementación de un formulario básico que utiliza las librerías de clases comentadas en los artículos anteriores.

En este enlace puedes acceder al primer artículo de esta serie. En este otro puedes descargar el código fuente de la solución KinectGestures, escrita en csharp con Visual Studio 2015.

Aplicación KinectGestures

El proyecto KinectGestures contiene el formulario Gestures, que contiene tordo el código necesario para ejecutar la aplicación de ejemplo:

Formulario principal de KinectGestures
Formulario principal de KinectGestures

Se trata de un formulario muy sencillo, que consta de una barra de herramientas con un botón para poner en funcionamiento el sensor y otro para detenerlo, un panel central en el que se dibuja el esqueleto y cuatro paneles que rodean a este panel central con los que se indica si el usuario se encuentra fuera del enfoque por alguno de los laterales.

El formulario utiliza también un componente BackgroundWorker para realizar la lectura de los datos del sensor de manera asíncrona, con un proceso de fondo.

Existen dos variables locales para el control del sensor:

private ISensor _sensor = null;
private BodyVector _body = null;

La variable _sensor permite el acceso al sensor Kinect y la variable _body contiene el último cuerpo leído.

El sensor se obtiene en el constructor del formulario mediante reflexión. Esto se hace así para evitar tener que hacer referencia a una versión concreta de la librería del sensor, ya que puede ser necesario realizar varias versiones de dicha librería para adaptarse a la versión del hardware:

Assembly asm = Assembly.LoadFile(Path.Combine(
Path.GetDirectoryName(Application.ExecutablePath), "KTSensor.dll"));
foreach (Type t in asm.GetTypes())
{
if (t.GetInterface("ISensor") != null)
{
_sensor = t.GetConstructor(Type.EmptyTypes).Invoke(null) as ISensor;
break;
}
}

Para poner en marcha el sensor, solo es necesario llamar al método Start. También se pone en funcionamiento el BackgroundWorker para que vaya recogiendo las imágenes:

try
{
_sensor.Start();
bPlay.Enabled = false;
bStop.Enabled = true;
_lastFrame = DateTime.Now;
bgBodyReader.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

El BackgroundWorker ejecuta un bucle en el que simplemente obtiene el siguiente esqueleto con el método NextBody, pasándolo con el método ReportProgress al evento ProgressChanged, que se ejecuta en el mismo proceso que el formulario y no dará problemas a la hora de dibujar el esqueleto:

private void bgBodyReader_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (bgBodyReader.CancellationPending)
{
e.Cancel = true;
break;
}
BodyVector bv = _sensor.NextBody;
if (bv != null)
{
bgBodyReader.ReportProgress(1, bv);
}
}
}

El evento ProgressChanged se encarga de procesar el esqueleto:

private void bgBodyReader_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
if (e.UserState is BodyVector)
{
_body = e.UserState as BodyVector;
if (DateTime.Now.Subtract(_lastFrame).TotalMilliseconds >= 100)
{
_lastFrame = DateTime.Now;
NormalizedBody nb = new NormalizedBody(_body);
_normBody = nb.CompleteBody;
int lpos = int.Parse(nb.LeftForearm);
int rpos = int.Parse(nb.RightForearm);
if ((lpos >= 5) && (lpos <= 7) && (rpos >= 5) && (rpos <= 7))
{
pBody.BackColor = Color.Pink;
}
else if ((lpos >= 5) && (lpos <= 7))
{
pBody.BackColor = Color.Yellow;
}
else if ((rpos >= 5) && (rpos <= 7))
{
pBody.BackColor = Color.Cyan;
}
else
{
pBody.BackColor = Color.White;
}
Refresh();
}
}
}

El objeto BodyVector con el esqueleto se encuentra en la propiedad de tipo object UserState del argumento del evento. Solo mostraremos una imagen cada 100 milisegundos, para evitar el excesivo parpadeo. Con la clase NormalizedBody que vimos en artículos anteriores comprobamos el ángulo que forman los antebrazos izquierdo y derecho, de manera que cambiamos el color del fondo si tenemos uno u otro o los dos formando más o menos un ángulo de 90 grados con el brazo.

Para detener la captura, se cancela la ejecución del BackgroundWorker, lo que dispara el evento RunWorkerCompleted, donde se detiene el sensor con el método Stop:

private void bgBodyReader_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
try
{
_sensor.Stop();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
bPlay.Enabled = true;
bStop.Enabled = false;
}
}

El dibujo del esqueleto y el marcado de los bordes por los que el cuerpo se está saliendo del encuadre se realiza en el evento Paint del panel principal del formulario, utilizando las funciones auxiliares DrawBody y DrawBone, que cada uno puede implementar a su gusto según el aspecto que quiera darle al esqueleto.

Comparte este artículo: Compartir en Twitter Compártelo en Facebook Compartir en Google Plus Compartir en LinkedIn
Comentarios (1):
* (Su comentario será publicado después de la revisión)

E-Mail


Nombre


Web


Mensaje


CAPTCHA
Change the CAPTCHA codeSpeak the CAPTCHA code