Almacenar el movimiento v2.0 de Kinect en un archivo BVH

298

Me gustaría almacenar los datos de captura de movimiento de Kinect 2 como un archivo BVH. Encontré un código que lo hace para Kinect 1 que se puede encontrar aquí . Revisé el código y encontré varias cosas que no pude entender. Por ejemplo, en el código mencionado he tratado de comprender qué es exactamente el skelobjeto Skeleton , que se encuentra en varios lugares del código. Si no, ¿hay alguna aplicación conocida disponible para lograr el objetivo?

EDITAR: Traté de cambiar Skeleton skel a Body skel, que creo que es el objeto correspondiente para kinect SDK 2.0. Sin embargo, tengo un error cuando intento obtener la posición del cuerpo:

tempMotionVektor[0] = -Math.Round( skel.Position.X * 100,2);
tempMotionVektor[1] = Math.Round( skel.Position.Y * 100,2) + 120;
tempMotionVektor[2] = 300 - Math.Round( skel.Position.Z * 100,2);

Recibí errores al llamar a la función Posición para el skel del cuerpo. ¿Cómo puedo recuperar la X, Y, Z del esqueleto en SDK 2.0? Traté de cambiar las tres líneas anteriores a:

tempMotionVektor[0] = -Math.Round(skel.Joints[0].Position.X * 100, 2);
tempMotionVektor[1] = Math.Round(skel.Joints[0].Position.Y * 100, 2) + 120;
tempMotionVektor[2] = 300 - Math.Round(skel.Joints[0].Position.Z * 100, 2);

EDITAR: Básicamente logré almacenar el archivo bvh después de combinar bodyBasicsWPF y kinect2bvh. Sin embargo, parece que el esqueleto que estoy almacenando no es eficiente. Hay movimientos extraños en los codos. Estoy tratando de entender si tengo que cambiar algo en el archivo kinectSkeletonBVH.cp . Más específicamente, cuáles son los cambios en la orientación del eje de la articulación para la versión kinect 2. ¿Cómo puedo cambiar la siguiente línea skel.BoneOrientations[JointType.ShoulderCenter].AbsoluteRotation.Quaternion; ? Intenté cambiar esa línea con skel.JointOrientations[JointType.ShoulderCenter].Orientation. Estoy en lo cierto? Estoy usando el siguiente código para agregar la unión a los objetos BVHBone:

BVHBone hipCenter = new BVHBone(null, JointType.SpineBase.ToString(), 6, TransAxis.None, true);
BVHBone hipCenter2 = new BVHBone(hipCenter, "HipCenter2", 3, TransAxis.Y, false);
BVHBone spine = new BVHBone(hipCenter2, JointType.SpineMid.ToString(), 3, TransAxis.Y, true);
BVHBone shoulderCenter = new BVHBone(spine, JointType.SpineShoulder.ToString(), 3, TransAxis.Y, true);

BVHBone collarLeft = new BVHBone(shoulderCenter, "CollarLeft", 3, TransAxis.X, false);
BVHBone shoulderLeft = new BVHBone(collarLeft, JointType.ShoulderLeft.ToString(), 3, TransAxis.X, true);
BVHBone elbowLeft = new BVHBone(shoulderLeft, JointType.ElbowLeft.ToString(), 3, TransAxis.X, true);
BVHBone wristLeft = new BVHBone(elbowLeft, JointType.WristLeft.ToString(), 3, TransAxis.X, true);
BVHBone handLeft = new BVHBone(wristLeft, JointType.HandLeft.ToString(), 0, TransAxis.X, true);

BVHBone neck = new BVHBone(shoulderCenter, "Neck", 3, TransAxis.Y, false);
BVHBone head = new BVHBone(neck, JointType.Head.ToString(), 3, TransAxis.Y, true);
BVHBone headtop = new BVHBone(head, "Headtop", 0, TransAxis.None, false);

No puedo entender dónde the axis for every Jointse calcula el código interno .

Jose Ramon
fuente
99
Sin embargo, omití este problema. Si tiene alguna solución, es bueno publicarlo aquí no solo para mí, ya que noté que muchas personas buscan movimientos de almacenamiento en archivos bvh.
Jose Ramon
Puedo almacenar la información de Kinect v1 y v2 para un archivo txt. Este archivo BVH es algo que acabo de leer y será una característica que agregaré a nuestro software de adquisición. Si está interesado en los archivos * .txt, hágamelo saber. Todavía no tengo una solución BVH adecuada.
16 de
2
check: pterneas.com/2014/03/13/…
Khaled.K
1
Tengo una manera de escribir correctamente las transmisiones Kinect v1 y Kinect v2 en txt. Tal vez si lo usa, puede verificar dos veces los archivos de su bvh para el mismo experimento. Mira mi biografía para más información.
16per9
1
@ Khaled.K Si el enlace que publicó responde a esto, ¿le importaría convertirlo en una respuesta a esta pregunta (según esta metapregunta )
Matt Thomas

Respuestas:

2

El código que usó para Kinect 1.0 para obtener un archivo BVH utiliza la información de las articulaciones para construir vectores óseos leyendo el Esqueleto .

public static double[] getBoneVectorOutofJointPosition(BVHBone bvhBone, Skeleton skel)
{
    double[] boneVector = new double[3] { 0, 0, 0 };
    double[] boneVectorParent = new double[3] { 0, 0, 0 };
    string boneName = bvhBone.Name;

    JointType Joint;
    if (bvhBone.Root == true)
    {
        boneVector = new double[3] { 0, 0, 0 };
    }
    else
    {
        if (bvhBone.IsKinectJoint == true)
        {
            Joint = KinectSkeletonBVH.String2JointType(boneName);

            boneVector[0] = skel.Joints[Joint].Position.X;
            boneVector[1] = skel.Joints[Joint].Position.Y;
            boneVector[2] = skel.Joints[Joint].Position.Z;
..

Fuente: Nguyên Lê Đặng - Kinect2BVH.V2

Excepto en Kinect 2.0 , la clase Esqueleto ha sido reemplazada por la clase Cuerpo , por lo que debe cambiarla para tratar con un Cuerpo y obtener las articulaciones siguiendo los pasos que se detallan a continuación.

// Kinect namespace
using Microsoft.Kinect;

// ...

// Kinect sensor and Kinect stream reader objects
KinectSensor _sensor;
MultiSourceFrameReader _reader;
IList<Body> _bodies;

// Kinect sensor initialization
_sensor = KinectSensor.GetDefault();

if (_sensor != null)
{
    _sensor.Open();
}

También agregamos una lista de cuerpos, donde se guardarán todos los datos relacionados con el cuerpo / esqueleto. Si se ha desarrollado para Kinect versión 1, observará que la clase Skeleton ha sido reemplazada por la clase Body. ¿Recuerdas el MultiSourceFrameReader? ¡Esta clase nos da acceso en cada transmisión, incluida la transmisión del cuerpo! Simplemente necesitamos que el sensor sepa que necesitamos la funcionalidad de seguimiento del cuerpo agregando un parámetro adicional al inicializar el lector:

_reader = _sensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color |
                                             FrameSourceTypes.Depth |
                                             FrameSourceTypes.Infrared |
                                             FrameSourceTypes.Body);

_reader.MultiSourceFrameArrived += Reader_MultiSourceFrameArrived;

Se llamará al método Reader_MultiSourceFrameArrived siempre que haya un nuevo marco disponible. Especifiquemos lo que sucederá en términos de los datos del cuerpo:

  1. Obtenga una referencia al marco del cuerpo
  2. Compruebe si el marco del cuerpo es nulo; esto es crucial
  3. Inicializar la lista _bodies
  4. Llame al método GetAndRefreshBodyData para copiar los datos del cuerpo en la lista
  5. ¡Recorre la lista de cuerpos y haz cosas increíbles!

Siempre recuerde verificar los valores nulos. Kinect le proporciona aproximadamente 30 fotogramas por segundo, ¡cualquier cosa puede ser nula o faltante! Aquí está el código hasta ahora:

void Reader_MultiSourceFrameArrived(object sender,
            MultiSourceFrameArrivedEventArgs e)
{
    var reference = e.FrameReference.AcquireFrame();

    // Color
    // ...

    // Depth
    // ...

    // Infrared
    // ...

    // Body
    using (var frame = reference.BodyFrameReference.AcquireFrame())
    {
        if (frame != null)
        {
            _bodies = new Body[frame.BodyFrameSource.BodyCount];

            frame.GetAndRefreshBodyData(_bodies);

            foreach (var body in _bodies)
            {
                if (body != null)
                {
                    // Do something with the body...
                }
            }
        }
    }
}

¡Eso es todo! Ahora tenemos acceso a los cuerpos que Kinect identifica. El siguiente paso es mostrar la información del esqueleto en la pantalla. Cada cuerpo consta de 25 articulaciones. El sensor nos proporciona la posición (X, Y, Z) y la información de rotación para cada uno de ellos. Además, Kinect nos permite saber si las articulaciones se rastrean, se hipotizan o no. Es una buena práctica verificar si se rastrea un cuerpo antes de realizar cualquier función crítica.

El siguiente código ilustra cómo podemos acceder a las diferentes articulaciones del cuerpo:

if (body != null)
{
    if (body.IsTracked)
    {
        Joint head = body.Joints[JointType.Head];

        float x = head.Position.X;
        float y = head.Position.Y;
        float z = head.Position.Z;

        // Draw the joints...
    }
}

Fuente: Blog de Vangos Pterneas - KINECT PARA WINDOWS VERSIÓN 2: SEGUIMIENTO CORPORAL

Khaled.K
fuente