Skip to content

Commit

Permalink
few changes to the conversion to c3d
Browse files Browse the repository at this point in the history
  • Loading branch information
davidpagnon committed Apr 16, 2024
1 parent 46652a8 commit db8014c
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 170 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dist/
# **/*.mp4
**/*.trc
**/*.sto
**/*.c3d

**/Calib_qualisys.toml
**/pose-3d/
Expand Down
1 change: 1 addition & 0 deletions Pose2Sim/S00_Demo_BatchSession/Config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ make_c3d = false # also save triangulated data in c3d format
## Only works on BODY_25 and BODY_25B models
participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
participant_mass = 70.0 # kg
make_c3d = false # save triangulated data in c3d format in addition to trc


[opensim]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@
## Only works on BODY_25 and BODY_25B models
# participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
# participant_mass = 70.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@
## Only works on BODY_25 and BODY_25B models
# participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
# participant_mass = 70.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@ display_figures = false # true or false (lowercase)
## Only works on BODY_25 and BODY_25B models
# participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
# participant_mass = 70.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,12 @@
## Only works on BODY_25 and BODY_25B models
# participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
# participant_mass = 70.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,11 @@
## Only works on BODY_25 and BODY_25B models
participant_height = 1.21 # m # float if single person, list of float if multi-person (same order as the Static trials)
participant_mass = 25.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,11 @@
## Only works on BODY_25 and BODY_25B models
participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
participant_mass = 70.0 # kg
# make_c3d = false # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ reorder_trc = true # only checked if multi_person analysis
# show_interp_indices = true # true or false (lowercase). For each keypoint, return the frames that need to be interpolated
# handle_LR_swap = false # Better if few cameras (eg less than 4) with risk of limb swapping (eg camera facing sagittal plane), otherwise slightly less accurate and slower
# undistort_points = false # Better if distorted image (parallel lines curvy on the edge or at least one param > 10^-2), but unnecessary (and slightly slower) if distortions are low
# make_c3d = false # save triangulated data in c3d format in addition to trc
make_c3d = true # save triangulated data in c3d format in addition to trc


# [filtering]
[filtering]
# type = 'butterworth' # butterworth, kalman, gaussian, LOESS, median, butterworth_on_speed
# display_figures = false # true or false (lowercase)
# make_c3d = false # save triangulated data in c3d format in addition to trc
make_c3d = true # save triangulated data in c3d format in addition to trc

# [filtering.butterworth]
# order = 4
Expand All @@ -159,10 +159,11 @@ reorder_trc = true # only checked if multi_person analysis
## Only works on BODY_25 and BODY_25B models
participant_height = [1.21, 1.72] # m # float if single person, list of float if multi-person (same order as the Static trials)
participant_mass = [25.0, 70.0] # kg
make_c3d = true # save triangulated data in c3d format in addition to trc


# [opensim]
# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# static_trial = [# static_trial = ['S00_P00_Participant/S00_P00_T00_StaticTrial']
# # If this Config.toml file is at the Trial level, set to true or false (lowercase);
# # At the Participant level, specify the name of the static trial folder name, e.g. ['S00_P00_T00_StaticTrial'];
# # At the Session level, add participant subdirectory, e.g. ['S00_P00_Participant/S00_P00_T00_StaticTrial', 'S00_P01_Participant/S00_P00_T00_StaticTrial']
Expand Down
1 change: 1 addition & 0 deletions Pose2Sim/S01_Demo_SingleTrial/Config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ make_c3d = false # save triangulated data in c3d format in addition to trc
## Only works on BODY_25 and BODY_25B models
participant_height = 1.72 # m # float if single person, list of float if multi-person (same order as the Static trials)
participant_mass = 70.0 # kg
make_c3d = false # save triangulated data in c3d format in addition to trc


[opensim]
Expand Down
2 changes: 0 additions & 2 deletions Pose2Sim/Utilities/c3d_to_trc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
Converts c3d files to trc files.
Beware that it only allows you to retrieve 3D points, you won't get analog data nor computed data such as angles or powers with this code.
N.B.: First install c3d: `pip install c3d`
Usage:
from Pose2Sim.Utilities import c3d_to_trc; c3d_to_trc.c3d_to_trc_func(r'<input_c3d_file>')
python -m c3d_to_trc -i input_c3d_file
Expand Down
147 changes: 100 additions & 47 deletions Pose2Sim/Utilities/trc_to_c3d.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,126 @@
"""
Extracts marker data from a TRC file and creates a corresponding C3D file.
#! /usr/bin/env python
# -*- coding: utf-8 -*-

Usage:
python trc_to_c3d.py --trc_path <path_to_trc_file> --f <frame_rate>

--trc_path: Path to the TRC file.
--f: Frame rate of the 3D data.
c3d file will be saved in the same directory as the TRC file with the same name.
'''
##################################################
## Convert trc files to c3d ##
##################################################
Converts trc files to c3d files.
Usage:
from Pose2Sim.Utilities import trc_to_c3d; trc_to_c3d.trc_to_c3d_func(r'<input_trc_file>')
python -m trc_to_c3d -t <path_to_trc_path>
python -m trc_to_c3d --trc_path <path_to_trc_path> --c3d_path <output_c3d_file>
'''

"""

import os
## INIT
import argparse
import numpy as np
import pandas as pd
import c3d

def extract_marker_data(trc_file):
with open(trc_file, 'r') as file:

## AUTHORSHIP INFORMATION
__author__ = "HunMin Kim, David Pagnon"
__copyright__ = "Copyright 2021, Pose2Sim"
__credits__ = ["HuMin Kim, David Pagnon"]
__license__ = "BSD 3-Clause License"
__version__ = '0.8'
__maintainer__ = "David Pagnon"
__email__ = "[email protected]"
__status__ = "Development"


## FUNCTIONS
def extract_marker_data(trc_path):
'''
Extract marker names and coordinates from a trc file.
INPUTS:
- trc_path: Path to the trc file
OUTPUTS:
- marker_names: List of marker names
- marker_coords: Array of marker coordinates (n_frames, t+3*n_markers)
'''

# marker names
with open(trc_path, 'r') as file:
lines = file.readlines()
marker_names_line = lines[3]
marker_names = marker_names_line.strip().split('\t')[2::3]

trc_data = pd.read_csv(trc_file, sep='\t', skiprows=5)
marker_coords = trc_data.iloc[:, 2:].to_numpy().reshape(-1, len(marker_names), 3)
# marker_coords = np.nan_to_num(marker_coords, nan=0.0)
marker_coords *= 1000 # Convert from meters to millimeters
# time and marker coordinates
trc_data_np = np.genfromtxt(trc_path, skip_header=5, delimiter = '\t')[:,1:]

return marker_names, trc_data_np


def create_c3d_file(c3d_path, marker_names, trc_data_np):
'''
Create a c3d file from the data extracted from a trc file.
return marker_names, marker_coords
INPUTS:
- c3d_path: Path to the c3d file
- marker_names: List of marker names
- trc_data_np: Array of marker coordinates (n_frames, t+3*n_markers)
def create_c3d_file(trc_file, marker_names, marker_coords, frame_rate):
OUTPUTS:
- c3d file
'''

# retrieve frame rate
times = trc_data_np[:,0]
frame_rate = round((len(times)-1) / (times[-1] - times[0]))

# write c3d file
writer = c3d.Writer(point_rate=frame_rate, analog_rate=0, point_scale=1.0, point_units='mm', gen_scale=-1.0)
writer.set_point_labels(marker_names)

markers_group = writer.point_group

for frame in marker_coords:
residuals = np.full((frame.shape[0], 1), 0.0)
cameras = np.zeros((frame.shape[0], 1))
points = np.hstack((frame, residuals, cameras))
for frame in trc_data_np:
residuals = np.full((len(marker_names), 1), 0.0)
cameras = np.zeros((len(marker_names), 1))
coords = frame[1:].reshape(-1,3)*1000
points = np.hstack((coords, residuals, cameras))
writer.add_frames([(points, np.array([]))])

writer.set_start_frame(1)
writer._set_last_frame(len(marker_coords))
writer.set_start_frame(0)
writer._set_last_frame(len(trc_data_np)-1)

c3d_file_path = trc_file.replace('.trc', '.c3d')
with open(c3d_file_path, 'wb') as handle:
with open(c3d_path, 'wb') as handle:
writer.write(handle)
print(f"Successfully created c3d file.")

def trc_to_c3d(trc_file, frame_rate):
marker_names, marker_coords = extract_marker_data(trc_file)
create_c3d_file(trc_file, marker_names, marker_coords, frame_rate)

def main():
parser = argparse.ArgumentParser(description='Convert TRC files to C3D files.')
parser.add_argument('--trc_path', type=str, required=True, help='Path to the TRC file')
parser.add_argument('--f', type=int, required=True, help='Frame rate')

args = parser.parse_args()

trc_file = args.trc_path
frame_rate = args.f

if not os.path.isfile(trc_file):
print(f"Error: {trc_file} does not exist.")
return
def trc_to_c3d_func(*args):
'''
Converts trc files to c3d files.
Usage:
from Pose2Sim.Utilities import trc_to_c3d; trc_to_c3d.trc_to_c3d_func(r'<input_trc_file>')
python -m trc_to_c3d -t <path_to_trc_path>
python -m trc_to_c3d --trc_path <path_to_trc_path> --c3d_path <output_c3d_file>
'''

try:
trc_path = args[0]['trc_path'] # invoked with argparse
if args[0]['c3d_path'] == None:
c3d_path = trc_path.replace('.trc', '.c3d')
else:
c3d_path = args[0]['c3d_path']
except:
trc_path = args[0] # invoked as a function
c3d_path = trc_path.replace('.trc', '.c3d')

marker_names, trc_data_np = extract_marker_data(trc_path)
create_c3d_file(c3d_path, marker_names, trc_data_np)

trc_to_c3d(trc_file, frame_rate)

if __name__ == '__main__':
main()
parser = argparse.ArgumentParser(description='Convert TRC files to C3D files.')
parser.add_argument('-t', '--trc_path', type=str, required=True, help='trc input file path')
parser.add_argument('-c', '--c3d_path', type=str, required=False, help='c3d output file path')
args = vars(parser.parse_args())

trc_to_c3d_func(args)
Loading

0 comments on commit db8014c

Please sign in to comment.