Kinect: Obtener posición de las partes del cuerpo.
NOTA: los siguientes ejemplos se realizaron con: Microsoft visual studio
2010, KinectDeveloperToolkit-v1.7.0 y KinectSDK-v1.7.
NOTA: este código es parte de un proyecto, pero solo se mostraran los métodos
básicos para que quien lo lea pueda implementarlo sin problemas.
Una de las tareas que se produce cuando se quiere realizar un proyecto
con la kinect es el de reconocer el espacio y la distancia en la que está el
cuerpo, para lo cual les presentare este código que me ayuda a saber dónde está
el cuerpo.
Cabe mencionar que las distancias se calculan con respecto a la kinect,
esto es por la obviedad que las distancias son captadas por los sensores de la
kinect.
A continuación mostrare el código C# que genera la distancia en tiempo real
y posteriormente explicare lo esencial para entender este código.
-------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Kinect;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
namespace FullSkeletonSKD1
{
public partial class Form1 : Form
{
KinectSensor sensor;
public Form1()
{
InitializeComponent();
sensor = KinectSensor.KinectSensors[0];
}
private void button1_Click(object sender, EventArgs e)
{
sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
sensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
sensor.SkeletonStream.Enable();
sensor.AllFramesReady += FramesReady;
sensor.Start();
}
void FramesReady(object sender, AllFramesReadyEventArgs e)
{
ColorImageFrame VFrame = e.OpenColorImageFrame();
if (VFrame == null) return;
byte[] pixelS = new byte[VFrame.PixelDataLength];
Bitmap bmap = ImageToBitmap(VFrame);
SkeletonFrame SFrame = e.OpenSkeletonFrame();
if (SFrame == null) return;
Graphics g = Graphics.FromImage(bmap);
Skeleton[] Skeletons = new Skeleton[SFrame.SkeletonArrayLength];
SFrame.CopySkeletonDataTo(Skeletons);
foreach (Skeleton S in Skeletons)
{
if (S.TrackingState == SkeletonTrackingState.Tracked)
{
//body
DrawBone(JointType.Head, JointType.ShoulderCenter, S, g);
DrawBone(JointType.ShoulderCenter, JointType.Spine, S, g);
DrawBone(JointType.Spine, JointType.HipCenter, S, g);
//left leg
DrawBone(JointType.HipCenter, JointType.HipLeft, S, g);
DrawBone(JointType.HipLeft, JointType.KneeLeft, S, g);
DrawBone(JointType.KneeLeft, JointType.AnkleLeft, S, g);
DrawBone(JointType.AnkleLeft, JointType.FootLeft, S, g);
//Right Leg
DrawBone(JointType.HipCenter, JointType.HipRight, S, g);
DrawBone(JointType.HipRight, JointType.KneeRight, S, g);
DrawBone(JointType.KneeRight, JointType.AnkleRight, S, g);
DrawBone(JointType.AnkleRight, JointType.FootRight, S, g);
//Left Arm
DrawBone(JointType.ShoulderCenter, JointType.ShoulderLeft, S, g);
DrawBone(JointType.ShoulderLeft, JointType.ElbowLeft, S, g);
DrawBone(JointType.ElbowLeft, JointType.WristLeft, S, g);
DrawBone(JointType.WristLeft, JointType.HandLeft, S, g);
//Right Arm
DrawBone(JointType.ShoulderCenter, JointType.ShoulderRight, S, g);
DrawBone(JointType.ShoulderRight, JointType.ElbowRight, S, g);
DrawBone(JointType.ElbowRight, JointType.WristRight, S, g);
DrawBone(JointType.WristRight, JointType.HandRight, S, g);
}
}
pictureBox1.Image = bmap;
}
void DrawBone(JointType j1, JointType j2, Skeleton S, Graphics g)
{
Point p1 = GetJoint(j1, S);
if(j1 == JointType.HandLeft){
this.label1.Text = S.Joints[j1].Position.X + " " + S.Joints[j1].Position.Y + " " + S.Joints[j1].Position.Z;
}
Point p2 = GetJoint(j2, S);
if (j2== JointType.HandLeft)
{
this.label1.Text = S.Joints[j2].Position.X + " " + S.Joints[j2].Position.Y + " " + S.Joints[j2].Position.Z;
}
g.DrawLine(Pens.Red, p1, p2);
}
Point GetJoint(JointType j, Skeleton S)
{
SkeletonPoint Sloc = S.Joints[j].Position;
ColorImagePoint Cloc = sensor.MapSkeletonPointToColor(Sloc, ColorImageFormat.RgbResolution640x480Fps30);
return new Point(Cloc.X, Cloc.Y);
}
Bitmap ImageToBitmap(ColorImageFrame Image)
{
byte[] pixelData =new byte[Image.PixelDataLength];
Image.CopyPixelDataTo(pixelData);
Bitmap bmap = new Bitmap( Image.Width,Image.Height,PixelFormat.Format32bppRgb);
BitmapData bmapS = bmap.LockBits(new Rectangle(0, 0,Image.Width, Image.Height),ImageLockMode.WriteOnly,bmap.PixelFormat);
IntPtr ptr = bmapS.Scan0;
Marshal.Copy(pixelData, 0, ptr,Image.PixelDataLength);
bmap.UnlockBits(bmapS);
return bmap;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
sensor.Stop();
}
}
}
----------------------------------------------------------------------------------------------------
Lo
que el anterior código hace es mostrar la imagen de video captada por la cámara
de la kinect y captar un esqueleto si es que lo hay, después en un label
muestra los tres puntos X-Y-Z que expresan la distancia en metros, tomando como
punto de referencia el centro de la kinect.
EXPLICACIÓN DE CÓDIGO:
- KinectSensor sensor;
sensor =
KinectSensor.KinectSensors[0];
Con esta pequeña parte del código se
toma la referencia del sensor kinect para poder utilizarlo.
- sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
sensor.DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
sensor.SkeletonStream.Enable();
Con esto se le dice al sensor que es
lo que se requiere usar, así los tendrá preparados para su uso.
- sensor.AllFramesReady
+= FramesReady;
sensor.Start();
Con esto se asigna el evento que se requerirá
y se inicia el sensor.
- void
FramesReady(object sender, AllFramesReadyEventArgs e)
Esta es la función que toma el
evento que se asignó anteriormente, aquí se hacen todos los pasos gráficos y la
representación de cada parte del cuerpo reconocida.
- void
DrawBone(JointType j1, JointType j2, Skeleton S, Graphics g)
Esta es la función que dibuja un
hueso del cuerpo, se necesita el puto de referencia de inicio, el de fin, el
arreglo desde donde se obtiene la posición de cada parte y el grafico para
dibujar el hueso (en este caso no se dibuja un uso exactamente, sino una line
que lo representa).
- Point GetJoint(JointType j,
Skeleton S)
Esta función obtiene la ubicación de
los puntos del cuerpo con respecto a la resolución de la imagen en la que se
quiere dibujar.
- Bitmap
ImageToBitmap(ColorImageFrame Image)
Esta función transforma la imagen que
se obtiene desde la cámara a una imagen Bitmap para poder utilizarla y
representarla en el cuadro de imagen.
OBTENCIÓN DE LA UBICACIÖN DE LAS PARTES DEL CUERPO:
Como se dice en el título de este artículo lo que se quiere probar con
este ejemplo es la ubicación en tiempo real de alguna parte del cuerpo, lo que
realiza esta tarea se encuentra en la función DrawBone() dentro de esta función
se encuentra este código:
S.Joints[j1].Position.X + " " + S.Joints[j1].Position.Y +
" " + S.Joints[j1].Position.Z;
el cual obtiene la posición de una parte del cuerpo, S representa el
esqueleto del que se quiere sacar las posiciones, en el caso de que hubiera una
sola persona este esqueleto seria el número 0 del arreglos de esqueletos que se
obtiene así:
SkeletonFrame SFrame =
e.OpenSkeletonFrame();
Skeleton[] Skeletons = new
Skeleton[SFrame.SkeletonArrayLength];
SFrame.CopySkeletonDataTo(Skeletons);
foreach (Skeleton S in
Skeletons)
{
Así se obtendría cada esqueleto dentro del arreglo de esqueletos que
crea el sensor de la kinect.
Joints[] es un arreglo en el cual están las partes del cuerpo y Position
es donde se guardan las posiciones X-Y_Z. si yo quisiera obtener la posición en
el eje x de la mano izquierda de un esqueleto seria así:
S.Joints[JointType.HandLeft].Position.X.
Entonces en el código lo que se hace es cada vez que se quiera dibujar
un hueso se revisa la condición de que si es una mano izquierda, si lo es lo es
se escribe su posición en un label (se puede visualizar esto en el código
ejemplo mostrado arriba en la función DrawBone() ).
Para finalizar mostramos una imagen que comprueba que este código
funciona correctamente, mi mano izquierda esta levantada a x= -0.29 mts, y=-0.08 mts y z=1.7mts de la kinect.