config can be dict, dir, None (from current dir)

This commit is contained in:
davidpagnon 2023-12-09 22:06:57 +01:00
parent f23f82c6eb
commit 9dff5d73be
3 changed files with 229 additions and 176 deletions

View File

@ -68,18 +68,6 @@ def setup_logging(session_dir):
handlers = [logging.handlers.TimedRotatingFileHandler(os.path.join(session_dir, 'logs.txt'), when='D', interval=7), logging.StreamHandler()]) handlers = [logging.handlers.TimedRotatingFileHandler(os.path.join(session_dir, 'logs.txt'), when='D', interval=7), logging.StreamHandler()])
def determine_level():
'''
Determine the level at which the function is called.
Level = 1: called from a Trial folder
Level = 2: called from a Participant folder
Level = 3: called from a Session folder
'''
level = max([len(root.split(os.sep)) for root,dirs,files in os.walk('.') if 'Config.toml' in files])
return level
def recursive_update(dict_to_update, dict_with_new_values): def recursive_update(dict_to_update, dict_with_new_values):
''' '''
Update nested dictionaries without overwriting existing keys in any level of nesting Update nested dictionaries without overwriting existing keys in any level of nesting
@ -102,62 +90,86 @@ def recursive_update(dict_to_update, dict_with_new_values):
return dict_to_update return dict_to_update
def read_config_files(level): def determine_level(config_dir):
'''
Determine the level at which the function is called.
Level = 1: Trial folder
Level = 2: Participant folder
Level = 3: Session folder
'''
len_paths = [len(root.split(os.sep)) for root,dirs,files in os.walk(config_dir) if 'Config.toml' in files]
level = max(len_paths) - min(len_paths) + 1
return level
def read_config_files(config):
''' '''
Read Session, Participant, and Trial configuration files, Read Session, Participant, and Trial configuration files,
and output a dictionary with all the parameters. and output a dictionary with all the parameters.
''' '''
# Trial level if type(config)==dict:
if level == 1: level = 3 # log_dir = os.getcwd()
session_config_dict = toml.load(os.path.join('..','..','Config.toml')) config_dicts = [config]
participant_config_dict = toml.load(os.path.join('..','Config.toml')) if config_dicts[0].get('project').get('project_dir') == None:
trial_config_dict = toml.load('Config.toml') raise ValueError('Please specify the project directory in config_dict:\n \
config_dict.get("project").update({"project_dir":"<YOUR_PROJECT_DIRECTORY>"})')
session_config_dict = recursive_update(session_config_dict,participant_config_dict) else:
session_config_dict = recursive_update(session_config_dict,trial_config_dict) # if launched without an argument, config == None, else it is the path to the config directory
session_config_dict.get("project").update({"project_dir":os.getcwd()}) config_dir = ['.' if config == None else config][0]
config_dicts = [session_config_dict] level = determine_level(config_dir)
# Participant level # Trial level
if level == 2: if level == 1:
session_config_dict = toml.load(os.path.join('..','Config.toml')) session_config_dict = toml.load(os.path.join(config_dir, '..','..','Config.toml'))
participant_config_dict = toml.load('Config.toml') participant_config_dict = toml.load(os.path.join(config_dir, '..','Config.toml'))
config_dicts = [] trial_config_dict = toml.load(os.path.join(config_dir, 'Config.toml'))
# Create config dictionaries for all trials of the participant
for (root,dirs,files) in os.walk('.'): session_config_dict = recursive_update(session_config_dict,participant_config_dict)
if 'Config.toml' in files and root != '.': session_config_dict = recursive_update(session_config_dict,trial_config_dict)
trial_config_dict = toml.load(os.path.join(root, files[0])) session_config_dict.get("project").update({"project_dir":config_dir})
# deep copy, otherwise session_config_dict is modified at each iteration within the config_dicts list config_dicts = [session_config_dict]
temp_dict = deepcopy(session_config_dict)
temp_dict = recursive_update(temp_dict,participant_config_dict) # Participant level
temp_dict = recursive_update(temp_dict,trial_config_dict) if level == 2:
temp_dict.get("project").update({"project_dir":os.path.join(os.getcwd(), os.path.relpath(root))}) session_config_dict = toml.load(os.path.join(config_dir, '..','Config.toml'))
if not os.path.basename(root) in temp_dict.get("project").get('exclude_from_batch'): participant_config_dict = toml.load(os.path.join(config_dir, 'Config.toml'))
config_dicts.append(temp_dict) config_dicts = []
# Create config dictionaries for all trials of the participant
# Session level for (root,dirs,files) in os.walk(config_dir):
if level == 3: if 'Config.toml' in files and root != config_dir:
session_config_dict = toml.load('Config.toml')
config_dicts = []
# Create config dictionaries for all trials of all participants of the session
for (root,dirs,files) in os.walk('.'):
if 'Config.toml' in files and root != '.':
# participant
if len(root.split(os.sep)) == 2:
participant_config_dict = toml.load(os.path.join(root, files[0]))
# trial
elif len(root.split(os.sep)) == 3:
trial_config_dict = toml.load(os.path.join(root, files[0])) trial_config_dict = toml.load(os.path.join(root, files[0]))
# deep copy, otherwise session_config_dict is modified at each iteration within the config_dicts list # deep copy, otherwise session_config_dict is modified at each iteration within the config_dicts list
temp_dict = deepcopy(session_config_dict) temp_dict = deepcopy(session_config_dict)
temp_dict = recursive_update(temp_dict,participant_config_dict) temp_dict = recursive_update(temp_dict,participant_config_dict)
temp_dict = recursive_update(temp_dict,trial_config_dict) temp_dict = recursive_update(temp_dict,trial_config_dict)
temp_dict.get("project").update({"project_dir":os.path.join(os.getcwd(), os.path.relpath(root))}) temp_dict.get("project").update({"project_dir":os.path.join(config_dir, os.path.relpath(root))})
if not os.path.relpath(root) in [os.path.relpath(p) for p in temp_dict.get("project").get('exclude_from_batch')]: if not os.path.basename(root) in temp_dict.get("project").get('exclude_from_batch'):
config_dicts.append(temp_dict) config_dicts.append(temp_dict)
return config_dicts # Session level
if level == 3:
session_config_dict = toml.load(os.path.join(config_dir, 'Config.toml'))
config_dicts = []
# Create config dictionaries for all trials of all participants of the session
for (root,dirs,files) in os.walk(config_dir):
if 'Config.toml' in files and root != config_dir:
# participant
if determine_level(root) == 2:
participant_config_dict = toml.load(os.path.join(root, files[0]))
# trial
elif determine_level(root) == 1:
trial_config_dict = toml.load(os.path.join(root, files[0]))
# deep copy, otherwise session_config_dict is modified at each iteration within the config_dicts list
temp_dict = deepcopy(session_config_dict)
temp_dict = recursive_update(temp_dict,participant_config_dict)
temp_dict = recursive_update(temp_dict,trial_config_dict)
temp_dict.get("project").update({"project_dir":os.path.join(config_dir, os.path.relpath(root))})
if not os.path.relpath(root) in [os.path.relpath(p) for p in temp_dict.get("project").get('exclude_from_batch')]:
config_dicts.append(temp_dict)
return level, config_dicts
def base_params(config_dict, level): def base_params(config_dict, level):
@ -182,23 +194,15 @@ def calibration(config=None):
''' '''
Cameras calibration from checkerboards or from qualisys files. Cameras calibration from checkerboards or from qualisys files.
config is usually deduced from the path the function is called from config can be a dictionary,
(see read_config_files(level) function) but it can also be a dictionary or a trial, participant, or session directory path,
or the function can be called without an argument, in which case it is the current directory.
''' '''
from Pose2Sim.calibration import calibrate_cams_all from Pose2Sim.calibration import calibrate_cams_all
if type(config)==dict:
level = 3 # log_dir = os.getcwd()
config_dict = config
if config_dict.get('project').get('project_dir') == None:
raise ValueError('Please specify the project directory in config_dict:\n \
config_dict.get("project").update({"project_dir":"<YOUR_PROJECT_DIRECTORY>"})')
else:
# Determine the level at which the function is called (session:3, participant:2, trial:1)
level = determine_level()
config_dict = read_config_files(level)[0]
level, config_dicts = read_config_files(config)
config_dict = config_dicts[0]
session_dir = os.path.realpath([os.getcwd() if level==3 else os.path.join(os.getcwd(), '..') if level==2 else os.path.join(os.getcwd(), '..', '..')][0]) session_dir = os.path.realpath([os.getcwd() if level==3 else os.path.join(os.getcwd(), '..') if level==2 else os.path.join(os.getcwd(), '..', '..')][0])
config_dict.get("project").update({"project_dir":session_dir}) config_dict.get("project").update({"project_dir":session_dir})

View File

@ -1,107 +0,0 @@
[2023-12-08 12:52:35.337] [info] Loaded model model from file D:\softs\github_david\Openpose-to-Opensim\Pose_from_simu_draft\Model_Pose2Sim_Body25b_scaled.osim
[2023-12-08 12:52:35.338] [warning] Couldn't find file 'r_pelvis.vtp'.
[2023-12-08 12:52:35.338] [warning] Couldn't find file 'l_pelvis.vtp'.
[2023-12-08 12:52:35.339] [warning] Couldn't find file 'sacrum.vtp'.
[2023-12-08 12:52:35.339] [warning] Couldn't find file 'femur_r.vtp'.
[2023-12-08 12:52:35.339] [warning] Couldn't find file 'r_patella.vtp'.
[2023-12-08 12:52:35.339] [warning] Couldn't find file 'tibia_r.vtp'.
[2023-12-08 12:52:35.339] [warning] Couldn't find file 'fibula_r.vtp'.
[2023-12-08 12:52:35.340] [warning] Couldn't find file 'talus_rv.vtp'.
[2023-12-08 12:52:35.341] [warning] Couldn't find file 'foot.vtp'.
[2023-12-08 12:52:35.341] [warning] Couldn't find file 'bofoot.vtp'.
[2023-12-08 12:52:35.341] [warning] Couldn't find file 'femur_l.vtp'.
[2023-12-08 12:52:35.341] [warning] Couldn't find file 'l_patella.vtp'.
[2023-12-08 12:52:35.342] [warning] Couldn't find file 'tibia_l.vtp'.
[2023-12-08 12:52:35.342] [warning] Couldn't find file 'fibula_l.vtp'.
[2023-12-08 12:52:35.342] [warning] Couldn't find file 'talus_lv.vtp'.
[2023-12-08 12:52:35.342] [warning] Couldn't find file 'l_foot.vtp'.
[2023-12-08 12:52:35.343] [warning] Couldn't find file 'l_bofoot.vtp'.
[2023-12-08 12:52:35.343] [warning] Couldn't find file 'lumbar5.vtp'.
[2023-12-08 12:52:35.343] [warning] Couldn't find file 'lumbar4.vtp'.
[2023-12-08 12:52:35.343] [warning] Couldn't find file 'lumbar3.vtp'.
[2023-12-08 12:52:35.343] [warning] Couldn't find file 'lumbar2.vtp'.
[2023-12-08 12:52:35.344] [warning] Couldn't find file 'lumbar1.vtp'.
[2023-12-08 12:52:35.344] [warning] Couldn't find file 'cerv1sm.vtp'.
[2023-12-08 12:52:35.344] [warning] Couldn't find file 'cerv2sm.vtp'.
[2023-12-08 12:52:35.344] [warning] Couldn't find file 'cerv3sm.vtp'.
[2023-12-08 12:52:35.344] [warning] Couldn't find file 'cerv4sm.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'cerv5sm.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'cerv6sm.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'cerv7.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'thoracic12_s.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'thoracic11_s.vtp'.
[2023-12-08 12:52:35.345] [warning] Couldn't find file 'thoracic10_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic9_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic8_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic7_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic6_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic5_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic4_s.vtp'.
[2023-12-08 12:52:35.346] [warning] Couldn't find file 'thoracic3_s.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'thoracic2_s.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'thoracic1_s.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'hat_ribs_scap.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'hat_jaw.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'hat_skull.vtp'.
[2023-12-08 12:52:35.347] [warning] Couldn't find file 'humerus_rv.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'ulna_rv.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'radius_rv.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'pisiform_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'lunate_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'scaphoid_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'triquetrum_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'hamate_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'capitate_rvs.vtp'.
[2023-12-08 12:52:35.348] [warning] Couldn't find file 'trapezoid_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'trapezium_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'metacarpal2_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'index_proximal_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'index_medial_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'index_distal_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'metacarpal3_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'middle_proximal_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'middle_medial_rvs.vtp'.
[2023-12-08 12:52:35.349] [warning] Couldn't find file 'middle_distal_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'metacarpal4_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'ring_proximal_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'ring_medial_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'ring_distal_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'metacarpal5_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'little_proximal_rvs.vtp'.
[2023-12-08 12:52:35.350] [warning] Couldn't find file 'little_medial_rvs.vtp'.
[2023-12-08 12:52:35.351] [warning] Couldn't find file 'little_distal_rvs.vtp'.
[2023-12-08 12:52:35.351] [warning] Couldn't find file 'metacarpal1_rvs.vtp'.
[2023-12-08 12:52:35.351] [warning] Couldn't find file 'thumb_proximal_rvs.vtp'.
[2023-12-08 12:52:35.351] [warning] Couldn't find file 'thumb_distal_rvs.vtp'.
[2023-12-08 12:52:35.351] [warning] Couldn't find file 'humerus_lv.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'ulna_lv.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'radius_lv.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'pisiform_lvs.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'lunate_lvs.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'scaphoid_lvs.vtp'.
[2023-12-08 12:52:35.352] [warning] Couldn't find file 'triquetrum_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'hamate_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'capitate_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'trapezoid_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'trapezium_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'metacarpal2_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'index_proximal_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'index_medial_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'index_distal_lvs.vtp'.
[2023-12-08 12:52:35.353] [warning] Couldn't find file 'metacarpal3_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'middle_proximal_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'middle_medial_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'middle_distal_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'metacarpal4_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'ring_proximal_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'ring_medial_lvs.vtp'.
[2023-12-08 12:52:35.354] [warning] Couldn't find file 'ring_distal_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'metacarpal5_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'little_proximal_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'little_medial_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'little_distal_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'metacarpal1_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'thumb_proximal_lvs.vtp'.
[2023-12-08 12:52:35.355] [warning] Couldn't find file 'thumb_distal_lvs.vtp'.
[2023-12-08 12:52:35.680] [warning] Coordinate.setValue: coordinate pro_sup_r is locked. Unable to change its value.
[2023-12-08 12:52:35.758] [warning] Coordinate.setValue: coordinate pro_sup_l is locked. Unable to change its value.

View File

@ -0,0 +1,156 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
##################################################
## Reproject 3D points on camera planes ##
##################################################
Build a trc file which stores all real and virtual markers
calculated from a .mot motion file and a .osim model file.
Default: DeepLabCut
Usage:
from Pose2Sim.Utilities import reproj_from_trc_calib; reproj_from_trc_calib.reproj_from_trc_calib_func(r'<input_trc_file>', r'<input_calib_file>', r'<openpose_or_deeplabcut_format>')
python -m reproj_from_trc_calib -t input_trc_file -c input_calib_file
python -m reproj_from_trc_calib -t input_trc_file -c input_calib_file -o 'openpose'
'''
## INIT
import os
import pandas as pd
import numpy as np
import opensim as osim
import argparse
## AUTHORSHIP INFORMATION
__author__ = "David Pagnon"
__copyright__ = "Copyright 2021, Pose2Sim"
__credits__ = ["David Pagnon"]
__license__ = "BSD 3-Clause License"
__version__ = "0.5"
__maintainer__ = "David Pagnon"
__email__ = "contact@david-pagnon.com"
__status__ = "Development"
## FUNCTIONS
def get_marker_positions(motion_data, model):
'''
Get dataframe of marker positions
INPUTS:
- motion_data: .mot file opened with osim.TimeSeriesTable
- model: .osim file opened with osim.Model
OUTPUT:
- marker_positions_pd: DataFrame of marker positions
'''
# Markerset
marker_set = model.getMarkerSet()
marker_set_names = [mk.getName() for mk in list(marker_set)]
marker_set_names_xyz = np.array([[m+'_x', m+'_y', m+'_z'] for m in marker_set_names]).flatten()
# Data
times = motion_data.getIndependentColumn()
joint_angle_set_names = motion_data.getColumnLabels() # or [c.getName() for c in model.getCoordinateSet()]
joint_angle_set_names = [j for j in joint_angle_set_names if not j.endswith('activation')]
motion_data_pd = pd.DataFrame(motion_data.getMatrix().to_numpy()[:,:len(joint_angle_set_names)], columns=joint_angle_set_names)
# Get marker positions at each state
state = model.initSystem()
marker_positions = []
for n,t in enumerate(times):
# put the model in the right position
for coord in joint_angle_set_names:
if not coord.endswith('_tx') and not coord.endswith('_ty') and not coord.endswith('_tz'):
value = motion_data_pd.loc[n,coord]*np.pi/180
else:
value = motion_data_pd.loc[n,coord]
model.getCoordinateSet().get(coord).setValue(state,value)
# get marker positions
marker_positions += [np.array([marker_set.get(mk_name).findLocationInFrame(state, model.getGround()).to_numpy() for mk_name in marker_set_names]).flatten()]
marker_positions_pd = pd.DataFrame(marker_positions, columns=marker_set_names_xyz)
marker_positions_pd.insert(0, 'time', times)
marker_positions_pd.insert(0, 'frame', np.arange(len(times)))
return marker_positions_pd
def trc_from_mot_osim_func(*args):
'''
Build a trc file which stores all real and virtual markers
calculated from a .mot motion file and a .osim model file.
Usage:
from Pose2Sim.Utilities import trc_from_mot_osim; trc_from_mot_osim.trc_from_mot_osim_func(r'<input_mot_file>', r'<output_osim_file>', r'<trc_output_file>')
python -m trc_from_mot_osim -m input_mot_file -o input_osim_file
python -m trc_from_mot_osim -m input_mot_file -o input_osim_file -t trc_output_file
'''
try:
motion_path = args[0]['input_mot_file'] # invoked with argparse
osim_path = args[0]['input_osim_file']
if args[0]['trc_output_file'] == None:
trc_path = motion_path.replace('.mot', '.trc')
else:
trc_path = args[0]['trc_output_file']
except:
motion_path = args[0] # invoked as a function
osim_path = args[1]
try:
trc_path = args[2]
except:
trc_path = motion_path.replace('.mot', '.trc')
# Create dataframe with marker positions
model = osim.Model(osim_path)
motion_data = osim.TimeSeriesTable(motion_path)
marker_positions_pd = get_marker_positions(motion_data, model)
# Trc header
times = motion_data.getIndependentColumn()
marker_set = model.getMarkerSet()
marker_set_names = [mk.getName() for mk in list(marker_set)]
fps = str(int( 1/ (times[1]-times[0]) / (len(times)-1) ))
nb_frames = str(len(times))
nb_markers = str(len(marker_set_names))
header0_str = 'PathFileType\t4\t(X/Y/Z)\t' + os.path.basename(trc_path)
header1 = {}
header1['DataRate'] = fps
header1['CameraRate'] = fps
header1['NumFrames'] = nb_frames
header1['NumMarkers'] = nb_markers
header1['Units'] = 'm'
header1['OrigDataRate'] = fps
header1['OrigDataStartFrame'] = '0'
header1['OrigNumFrames'] = nb_frames
header1_str1 = '\t'.join(header1.keys())
header1_str2 = '\t'.join(header1.values())
header2_str1 = 'Frame#\tTime\t' + '\t\t\t'.join([mk.strip() for mk in marker_set_names]) + '\t\t'
header2_str2 = '\t\t'+'\t'.join(['X{i}\tY{i}\tZ{i}'.format(i=i+1) for i in range(int(header1['NumMarkers']))])
header_trc = '\n'.join([header0_str, header1_str1, header1_str2, header2_str1, header2_str2])
# write data
with open(trc_path, 'w') as trc_o:
trc_o.write(header_trc+'\n')
marker_positions_pd.to_csv(trc_path, header=False, sep = '\t', mode='a', index=False)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--input_mot_file', required = True, help='input mot file')
parser.add_argument('-o', '--input_osim_file', required = True, help='input osim file')
parser.add_argument('-t', '--trc_output_file', required=False, help='trc output file')
args = vars(parser.parse_args())
trc_from_mot_osim_func(args)