Kinect Capture and Blender

2014/12/21 19:49

I have made a very quick and simple way of getting data captured from Kinect into blender. It’s perhaps not the best of solutions out there and I coded it all in one morning so it could definitely do with some more work. But if you are interested in doing something similar it might be of interest.

I used this guide to set up the drivers and software.

Using the NITE “Stick Figure” sample, I added the source below to write each bone’s location and orientation to a text file along with a frame number in the file name (i.e. test_0.txt, test_1.txt, test_2.txt…)

//in StickFigure.cpp
#define AnimName "H:/mocap/kinect/test" //location to write data to
void OPBoneInfo(XnUserID user, xn::UserGenerator userGenerator, XnSkeletonJoint joint, std::ofstream& fs)
{
    XnSkeletonJointPosition pos;
    XnSkeletonJointOrientation rot;
    userGenerator.GetSkeletonCap().GetSkeletonJointPosition(user, joint, pos);
    userGenerator.GetSkeletonCap().GetSkeletonJointOrientation(user, joint, rot);
    fs << pos.position.X << " " << pos.position.Y << " " << pos.position.Z << " ";
    fs << rot.orientation.elements[0] << " " << rot.orientation.elements[1] << " " << rot.orientation.elements[2] << " "
        << rot.orientation.elements[3] << " "<< rot.orientation.elements[4] << " " << rot.orientation.elements[5] << " "
        << rot.orientation.elements[6] << " "<< rot.orientation.elements[7] << " " << rot.orientation.elements[8] << " ";  
}

//in DrawSingleUser add
void DrawSingleUser(XnUserID user, xn::UserGenerator g_UserGenerator, const XnPoint3D& corner)
{
//existing source...
    {
        static int frame = 0;
        std::ofstream fs;
     
        std::string location(AnimName);
        std::string extension(".txt");
        std::stringstream oss;
        oss << location << "_" << frame << extension;
        fs.open (oss.str());
 
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_HAND,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_ELBOW,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_SHOULDER,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_TORSO,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_SHOULDER,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_ELBOW,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_HAND,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_NECK,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_HEAD,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_HIP,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_HIP,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_KNEE,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_KNEE,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_LEFT_FOOT,  fs);
        OPBoneInfo(user, g_UserGenerator, XN_SKEL_RIGHT_FOOT,  fs);
        fs.close();
        frame++;
    }
//existing source...
}

Using the program as you would normally it will output the data for 15 bones to the location you specify.

In Blender, I created this script

import bpy
import os, codecs, time
import mathutils
 
gBoneName = "KinectArma"
gRootName = ["LEFT_HAND","LEFT_ELBOW","LEFT_SHOULDER","TORSO","RIGHT_SHOULDER","RIGHT_ELBOW", "RIGHT_HAND","NECK","HEAD","LEFT_HIP","RIGHT_HIP", "LEFT_KNEE","RIGHT_KNEE","LEFT_FOOT","RIGHT_FOOT"]
gBoneStream = "H:/mocap/kinect/test"
 
 
def AddKinectArma(scene):
    armaData = bpy.data.armatures.new(gBoneName)
    arma = bpy.data.objects.new(gBoneName, armaData)
    scene.objects.link(arma)
    return arma
#enddef
     
def CheckSceneForArma(scene):
    if gBoneName in scene.objects:
        return scene.objects[gBoneName]
    return AddKinectArma(scene)
#endddef
 
def CheckArmaForBones(arma):
    armaObj = bpy.data.objects[gBoneName]
    armaDat = armaObj.data
     
    bpy.context.scene.objects.active = armaObj         
    bpy.ops.object.mode_set(mode='EDIT')   
    for bone in gRootName:
        if bone in armaDat.bones:
            print("Bone Present\n")
        else:          
            bone = bpy.ops.armature.bone_primitive_add(name=bone)
        #endif
    #endfor
    bpy.ops.object.mode_set(mode='OBJECT')
#endddef
 
def MainLoop():
         
    armaObj = bpy.data.objects[gBoneName]
    armaDat = armaObj.data
         
    scale = 0.01
    fileIdx = 0
    bpy.context.scene.frame_current = 0
    bpy.ops.object.mode_set(mode='POSE')
    mocapFile = gBoneStream + "_" + str(fileIdx) + ".txt"
    while(os.access(mocapFile, os.F_OK)):
        fs = open(mocapFile,"r")
        pos = [float(x)*scale for x in fs.read().split()]
        fs.close()
         
        idx = 0    
        for bone in gRootName:
            if idx + 12 <= len(pos):
                armaObj.pose.bones[bone].location.x = pos[idx+0]
                armaObj.pose.bones[bone].location.y = pos[idx+1]
                armaObj.pose.bones[bone].location.z = pos[idx+2]
                #create matrix, chnage it to a quat
                mtx = mathutils.Matrix((pos[idx+3],pos[idx+4],pos[idx+5],0),(pos[idx+6],pos[idx+7],pos[idx+8],0),(pos[idx+9],pos[idx+10],pos[idx+11],0),(0,0,0,1))                             
                armaObj.pose.bones[bone].rotation_mode = 'QUATERNION'
                mtx = mtx.rotation_part()
                #mtx = mtx.transpose()
                armaObj.pose.bones[bone].rotation_quaternion = mtx.to_quat()
            else:
                print("numbers are wrong : " + str(idx) + ", " + str(len(pos)) + armaObj.pose.bones[bone].name)
            #endif
            idx = idx + 12         
        #endfor
 
        #select all bones
        for bone in gRootName:
            armaObj.pose.bones[bone].bone.select = True
        #add a keyframe
        bpy.ops.anim.keyframe_insert(type='LocRotScale')       
        #update the anim counter
        bpy.context.scene.frame_current = bpy.context.scene.frame_current+1
        #get next file
        fileIdx = fileIdx + 1
        mocapFile = gBoneStream + "_" + str(fileIdx) + ".txt"
        if fileIdx > 1000: #JUST IN CASE
            break;
    #endwhile
    bpy.ops.object.mode_set(mode='OBJECT') 
    print("finished")
    #endwhile
#endddef
 
arma = CheckSceneForArma(bpy.context.scene)
CheckArmaForBones(arma)
MainLoop()

Which creates an Armature with the correct amount of bones, and then reads in the source files sequentially creating keyframes as it goes along. Here’s one I made earlier

It’s a bit of hack, and it’s not very user friendly but it’s a start :)