├── regular ├── readme.txt └── Aim Pistol.fbx ├── json2npy └── readme.txt ├── fbx2json └── readme.txt ├── README.md ├── visualise_frames.py └── fbx2npy.py /regular/readme.txt: -------------------------------------------------------------------------------- 1 | Add your .fbx files here. This is your Source directory to start with. 2 | -------------------------------------------------------------------------------- /regular/Aim Pistol.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RahhulDd/FbxToNpyConverter/HEAD/regular/Aim Pistol.fbx -------------------------------------------------------------------------------- /json2npy/readme.txt: -------------------------------------------------------------------------------- 1 | All the JSON file will be processed and converted to NPY files which will move here. 2 | 3 | Directory format: 4 | 5 | json2npy => Motion Type => one NPY file per motion type -------------------------------------------------------------------------------- /fbx2json/readme.txt: -------------------------------------------------------------------------------- 1 | All the .fbx file will be processed and converted to JSON files which will move here. 2 | 3 | Directory format: 4 | 5 | fbs2json => Motion Type => jointDict => Key frame JSON files -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FBX-to-NPY Converter 2 | Script to process and convert .fbx file format to .npy file format only for human motion analysis. 3 | 4 | The script needs the Blender to run. If you don't have Blender, then you can download it from the link below: 5 | 6 | https://www.blender.org/download/ 7 | 8 | For Mixamo Data, use this link: 9 | 10 | https://www.mixamo.com/#/?page=1&type=Motion%2CMotionPack&limit=96 11 | 12 | For a larger dataset, I recommed using the download script provided in the link below: 13 | 14 | https://forums.unrealengine.com/community/community-content-tools-and-tutorials/1376068-script-mixamo-download-script 15 | 16 | Once the Blender is downloaded, install it and add it's directory PATH to the ENVIRONMENT VARIABLES. 17 | 18 | A). To run the script: 19 | 1. Open CMD prompt with administrator permisson. 20 | 2. Change to the directory where 'fbx2npy.py' is located. 21 | 3. Type the command: 'blender --background -P fbx2npy.py' 22 | 4. If you installed and add Blender directory PATH correctly, then above command won't be a problem. 23 | 24 | B). To visualise the .npy generated files, use 'visualise_frames.py'. This only works with single .npy file so be careful to mention PATH to .npy file. 25 | 26 | Voila!! you have prepared the .npy files for your ML/DL project or anything else. 27 | -------------------------------------------------------------------------------- /visualise_frames.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import os 4 | import matplotlib.pyplot as plt 5 | from mpl_toolkits import mplot3d 6 | 7 | def visualise_frames(pathtonpy='json2np/Aim Pistol/Aim Pistol.npy',up_view=0,side_view=90,x_lim=[-2,2],y_lim=[-2,2],z_lim=[-2,2]): 8 | #MIXAMO DATASET 9 | """ 10 | Each POSE follow the given index joints below: 11 | 12 | 0 - Head 13 | 1 - Neck 14 | 2 - Lshoulder 15 | 3 - Lelbow 16 | 4 - Lwrist 17 | 5 - Rshoulder 18 | 6 - Relbow 19 | 7 - Rwrist 20 | 8 - Pelvis 21 | 9 - Lhip 22 | 10 - Lknee 23 | 11 - Lankle 24 | 12 - Rhip 25 | 13 - Rknee 26 | 14 - Rankle 27 | """ 28 | #Load .npy file 29 | motion3d = np.load(pathtonpy) 30 | 31 | #Index for bone links 32 | bones = [[0,1],[1,2],[2,3],[3,4],[1,5],[5,6],[6,7],[1,8],[8,9],[9,10],[10,11],[8,12],[12,13],[13,14]] 33 | 34 | #Set offset for few frame visualisation 35 | offset = int(motion3d.shape[2]/5) 36 | offset_sum = 0 37 | 38 | #Output frames 39 | for i in range(5): 40 | fig = plt.figure() 41 | ax = plt.axes(projection='3d') 42 | ax.scatter(motion3d[:,0,offset_sum],motion3d[:,1,offset_sum],motion3d[:,2,offset_sum]) 43 | ax.set_xlim(x_lim) 44 | ax.set_ylim(y_lim) 45 | ax.set_zlim(z_lim) 46 | for i in range(len(bones)): 47 | ax.plot3D([motion3d[bones[i][0],0,offset_sum],motion3d[bones[i][1],0,offset_sum]],[motion3d[bones[i][0],1,offset_sum],motion3d[bones[i][1],1,offset_sum]],[motion3d[bones[i][0],2,offset_sum],motion3d[bones[i][1],2,offset_sum]]) 48 | 49 | ax.view_init(up_view,side_view) 50 | print(offset_sum) 51 | ax.set_title('Frame: %d'%(offset_sum)) 52 | offset_sum = offset_sum+offset 53 | 54 | if __name__='__main__': 55 | visualise_frames() 56 | -------------------------------------------------------------------------------- /fbx2npy.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | import time 4 | import sys 5 | import json 6 | from mathutils import Vector 7 | import numpy as np 8 | 9 | HOME_FILE_PATH = os.path.abspath('homefile.blend') 10 | MIN_NR_FRAMES = 64 11 | RESOLUTION = (512, 512) 12 | 13 | #Crucial joints sufficient for visualisation #FIX ME - Add more joints if desirable for MixamRig 14 | BASE_JOINT_NAMES = ['Head', 'Neck', 15 | 'RightArm', 'RightForeArm', 'RightHand', 'LeftArm', 'LeftForeArm', 'LeftHand', 16 | 'Hips', 'RightUpLeg', 'RightLeg', 'RightFoot', 'LeftUpLeg', 'LeftLeg', 'LeftFoot', 17 | ] 18 | #Source directory where .fbx exist 19 | SRC_DATA_DIR ='regular' 20 | 21 | #Ouput directory where .fbx to JSON dict will be stored 22 | OUT_DATA_DIR ='fbx2json' 23 | 24 | #Final directory where NPY files will ve stored 25 | FINAL_DIR_PATH ='json2npy' 26 | 27 | #Number of joints to be used from MixamoRig 28 | joint_names = ['mixamorig:' + x for x in BASE_JOINT_NAMES] 29 | 30 | def fbx2jointDict(): 31 | 32 | #Remove 'Cube' object if exists in the scene 33 | if bpy.data.objects.get('Cube') is not None: 34 | cube = bpy.data.objects['Cube'] 35 | bpy.data.objects.remove(cube) 36 | 37 | #Intensify Light Point in the scene 38 | if bpy.data.objects.get('Light') is not None: 39 | bpy.data.objects['Light'].data.energy = 2 40 | bpy.data.objects['Light'].data.type = 'POINT' 41 | 42 | #Set resolution and it's rendering percentage 43 | bpy.data.scenes['Scene'].render.resolution_x = RESOLUTION[0] 44 | bpy.data.scenes['Scene'].render.resolution_y = RESOLUTION[1] 45 | bpy.data.scenes['Scene'].render.resolution_percentage = 100 46 | 47 | #Base file for blender 48 | bpy.ops.wm.save_as_mainfile(filepath=HOME_FILE_PATH) 49 | 50 | #Get animation(.fbx) file paths 51 | anims_path = os.listdir(SRC_DATA_DIR) 52 | 53 | #Make OUT_DATA_DIR 54 | if not os.path.exists(OUT_DATA_DIR): 55 | os.makedirs(OUT_DATA_DIR) 56 | 57 | for anim_name in anims_path: 58 | 59 | anim_file_path = os.path.join(SRC_DATA_DIR,anim_name) 60 | save_dir = os.path.join(OUT_DATA_DIR,anim_name.split('.')[0],'JointDict') 61 | 62 | #Make save_dir 63 | if not os.path.exists(save_dir): 64 | os.makedirs(save_dir) 65 | 66 | #Load HOME_FILE and .fbx file 67 | bpy.ops.wm.read_homefile(filepath=HOME_FILE_PATH) 68 | bpy.ops.import_scene.fbx(filepath=anim_file_path) 69 | 70 | #End Frame Index for .fbx file 71 | frame_end = bpy.data.actions[0].frame_range[1] 72 | 73 | for i in range(int(frame_end)+1): 74 | 75 | bpy.context.scene.frame_set(i) 76 | 77 | bone_struct = bpy.data.objects['Armature'].pose.bones 78 | armature = bpy.data.objects['Armature'] 79 | 80 | out_dict = {'pose_keypoints_3d': []} 81 | 82 | for name in joint_names: 83 | global_location = armature.matrix_world @ bone_struct[name].matrix @ Vector((0, 0, 0)) 84 | l = [global_location[0], global_location[1], global_location[2]] 85 | out_dict['pose_keypoints_3d'].extend(l) 86 | 87 | save_path = os.path.join(save_dir,'%04d_keypoints.json'%i) 88 | with open(save_path,'w') as f: 89 | json.dump(out_dict, f) 90 | 91 | def jointDict2npy(): 92 | 93 | json_dir = OUT_DATA_DIR 94 | npy_dir = FINAL_DIR_PATH 95 | if not os.path.exists(npy_dir): 96 | os.makedirs(npy_dir) 97 | 98 | anim_names = os.listdir(json_dir) 99 | 100 | for anim_name in anim_names: 101 | files_path = os.path.join(json_dir,anim_name,'jointDict') 102 | frame_files = os.listdir(files_path) 103 | 104 | motion = [] 105 | 106 | for frame_file in frame_files: 107 | file_path = os.path.join(files_path,frame_file) 108 | 109 | with open(file_path) as f: 110 | info = json.load(f) 111 | joint = np.array(info['pose_keypoints_3d']).reshape((-1, 3)) 112 | motion.append(joint[:15,:]) 113 | 114 | motion = np.stack(motion,axis=2) 115 | save_path = os.path.join(npy_dir,anim_name) 116 | if not os.path.exists(save_path): 117 | os.makedirs(save_path) 118 | 119 | print(save_path) 120 | 121 | np.save(save_path+'/'+'{i}.npy'.format(i=anim_name),motion) 122 | 123 | if __name__ == '__main__': 124 | 125 | #Convert .fbx files to JSON dict 126 | fbx2jointDict() 127 | 128 | #Convert JSON dict to NPY 129 | jointDict2npy() 130 | 131 | 132 | --------------------------------------------------------------------------------