updated docstrings
This commit is contained in:
parent
6f7e883cd3
commit
27bdba282d
BIN
Content/Capture d’écran 2024-09-20 195347.jpg
Normal file
BIN
Content/Capture d’écran 2024-09-20 195347.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
@ -468,7 +468,6 @@ def markerAugmentation(config=None):
|
|||||||
def kinematics(config=None):
|
def kinematics(config=None):
|
||||||
'''
|
'''
|
||||||
Performing OpenSim scaling and inverse kinematics.
|
Performing OpenSim scaling and inverse kinematics.
|
||||||
Select the 10% slowest frames from trc for scaling
|
|
||||||
Save scaled model as .osim and output motion as .mot
|
Save scaled model as .osim and output motion as .mot
|
||||||
|
|
||||||
config can be a dictionary,
|
config can be a dictionary,
|
||||||
|
@ -7,22 +7,28 @@
|
|||||||
## KINEMATICS PROCESSING ##
|
## KINEMATICS PROCESSING ##
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
Process kinematic data using OpenSim tools.
|
Runs OpenSim scaling and inverse kinematics
|
||||||
|
|
||||||
This script performs scaling, inverse kinematics, and related processing
|
Scaling:
|
||||||
on 3D motion capture data (TRC files). The scaling process adjusts the
|
- No need for a static trial: scaling is done on the triangulated coordinates (trc file)
|
||||||
generic model to match the subject's physical dimensions, while inverse
|
- Remove 10% fastest frames (potential outliers)
|
||||||
kinematics computes the joint angles based on the motion data.
|
- Remove frames where coordinate speed is null (person probably out of frame)
|
||||||
|
- Remove 40% most extreme calculated segment values (potential outliers)
|
||||||
|
- For each segment, scale on the mean of the remaining segment values
|
||||||
|
|
||||||
Set your parameters in Config.toml.
|
Inverse Kinematics:
|
||||||
|
- Run on the scaled model with the same trc file
|
||||||
|
- Model markers follow the triangulated markers while respecting the model kinematic constraints
|
||||||
|
- Joint angles are computed
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- a directory containing TRC files
|
- config_dict (dict): Generated from a .toml calibration file
|
||||||
- kinematic processing parameters in Config.toml
|
|
||||||
|
|
||||||
OUTPUT:
|
OUTPUTS:
|
||||||
- scaled OpenSim model files (.osim)
|
- A scaled .osim model for each person
|
||||||
- joint angle data files (.mot)
|
- Joint angle data files (.mot) for each person
|
||||||
|
- Optionally, OpenSim scaling and IK setup files saved to the kinematics directory
|
||||||
|
- Pose2Sim and OpenSim logs saved to files
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +69,7 @@ def read_trc(trc_path):
|
|||||||
- trc_path (str): The path to the TRC file.
|
- trc_path (str): The path to the TRC file.
|
||||||
|
|
||||||
OUTPUTS:
|
OUTPUTS:
|
||||||
- tuple: A tuple containing the Q coordinates, frames column, time column, and header.
|
- tuple: A tuple containing the Q coordinates, frames column, time column, marker names, and header.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -85,6 +91,9 @@ def get_opensim_setup_dir():
|
|||||||
'''
|
'''
|
||||||
Locate the OpenSim setup directory within the Pose2Sim package.
|
Locate the OpenSim setup directory within the Pose2Sim package.
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- None
|
||||||
|
|
||||||
OUTPUTS:
|
OUTPUTS:
|
||||||
- Path: The path to the OpenSim setup directory.
|
- Path: The path to the OpenSim setup directory.
|
||||||
'''
|
'''
|
||||||
@ -130,12 +139,13 @@ def get_model_path(model_name, osim_setup_dir):
|
|||||||
raise ValueError(f"Pose model '{model_name}' not found.")
|
raise ValueError(f"Pose model '{model_name}' not found.")
|
||||||
|
|
||||||
unscaled_model_path = osim_setup_dir / pose_model_file
|
unscaled_model_path = osim_setup_dir / pose_model_file
|
||||||
|
|
||||||
return unscaled_model_path
|
return unscaled_model_path
|
||||||
|
|
||||||
|
|
||||||
def get_scaling_setup(model_name, osim_setup_dir):
|
def get_scaling_setup(model_name, osim_setup_dir):
|
||||||
'''
|
'''
|
||||||
Retrieve the OpenSim scaling setup file path.
|
Retrieve the path of the OpenSim scaling setup file.
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- model_name (str): Name of the model
|
- model_name (str): Name of the model
|
||||||
@ -169,12 +179,13 @@ def get_scaling_setup(model_name, osim_setup_dir):
|
|||||||
raise ValueError(f"Pose model '{model_name}' not found.")
|
raise ValueError(f"Pose model '{model_name}' not found.")
|
||||||
|
|
||||||
scaling_setup_path = osim_setup_dir / scaling_setup_file
|
scaling_setup_path = osim_setup_dir / scaling_setup_file
|
||||||
|
|
||||||
return scaling_setup_path
|
return scaling_setup_path
|
||||||
|
|
||||||
|
|
||||||
def get_IK_Setup(model_name, osim_setup_dir):
|
def get_IK_Setup(model_name, osim_setup_dir):
|
||||||
'''
|
'''
|
||||||
Retrieve the OpenSim inverse kinematics setup file path.
|
Retrieve the path of the OpenSim inverse kinematics setup file.
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- model_name (str): Name of the model
|
- model_name (str): Name of the model
|
||||||
@ -211,37 +222,11 @@ def get_IK_Setup(model_name, osim_setup_dir):
|
|||||||
return ik_setup_path
|
return ik_setup_path
|
||||||
|
|
||||||
|
|
||||||
# def get_output_dir(config_dir, person_id):
|
|
||||||
'''
|
|
||||||
Determines the correct output directory based on the configuration and the person identifier.
|
|
||||||
|
|
||||||
INPUTS:
|
|
||||||
- config_dir (Path): The root directory where the configuration file is located.
|
|
||||||
- person_id (str): Identifier for the person (e.g., 'SinglePerson', 'P1').
|
|
||||||
|
|
||||||
OUTPUTS:
|
|
||||||
- Path: The path where the output files should be stored.
|
|
||||||
'''
|
|
||||||
|
|
||||||
output_dir = config_dir / 'kinematics' # Assuming 'opensim' as the default output subdirectory
|
|
||||||
|
|
||||||
# Append the person_id to the output directory if it's a multi-person setup
|
|
||||||
if person_id != "SinglePerson":
|
|
||||||
output_dir = output_dir / person_id
|
|
||||||
|
|
||||||
logging.info(f"Output directory determined as: {output_dir}")
|
|
||||||
|
|
||||||
# Create the directory if it does not exist
|
|
||||||
if not output_dir.exists():
|
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
return output_dir
|
|
||||||
|
|
||||||
|
|
||||||
def get_kpt_pairs_from_tree(root_node):
|
def get_kpt_pairs_from_tree(root_node):
|
||||||
'''
|
'''
|
||||||
Get name pairs for all parent-child relationships in the tree.
|
Get marker pairs for all parent-child relationships in the tree.
|
||||||
# Excludes the root node.
|
# Excludes the root node.
|
||||||
|
# Not used in the current version.
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- root_node (Node): The root node of the tree.
|
- root_node (Node): The root node of the tree.
|
||||||
@ -262,17 +247,31 @@ def get_kpt_pairs_from_tree(root_node):
|
|||||||
|
|
||||||
def get_kpt_pairs_from_scaling(scaling_root):
|
def get_kpt_pairs_from_scaling(scaling_root):
|
||||||
'''
|
'''
|
||||||
Get name pairs for all marker pairs in the scaling setup file.
|
Get all marker pairs from the scaling setup file.
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- scaling_root (Element): The root element of the scaling setup file.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- pairs: A list of marker pairs.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
pairs = [pair.find('markers').text.strip().split(' ') for pair in scaling_root[0].findall(".//MarkerPair")]
|
pairs = [pair.find('markers').text.strip().split(' ')
|
||||||
|
for pair in scaling_root[0].findall(".//MarkerPair")]
|
||||||
|
|
||||||
return pairs
|
return pairs
|
||||||
|
|
||||||
|
|
||||||
def dict_segment_marker_pairs(scaling_root, right_left_symmetry=True):
|
def dict_segment_marker_pairs(scaling_root, right_left_symmetry=True):
|
||||||
'''
|
'''
|
||||||
|
Get a dictionary of segment names and their corresponding marker pairs.
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- scaling_root (Element): The root element of the scaling setup file.
|
||||||
|
- right_left_symmetry (bool): Whether to consider right and left side of equal size.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- segment_markers_dict: A dictionary of segment names and their corresponding marker pairs.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
segment_markers_dict = {}
|
segment_markers_dict = {}
|
||||||
@ -301,8 +300,21 @@ def dict_segment_marker_pairs(scaling_root, right_left_symmetry=True):
|
|||||||
return segment_markers_dict
|
return segment_markers_dict
|
||||||
|
|
||||||
|
|
||||||
def dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers, right_left_symmetry=True):
|
def dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers, trimmed_extrema_percent=0.5, right_left_symmetry=True):
|
||||||
'''
|
'''
|
||||||
|
Calculate the ratios between the size of the actual segment and the size of the model segment.
|
||||||
|
X, Y, and Z ratios are calculated separately if the original scaling setup file asks for it.
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- scaling_root (Element): The root element of the scaling setup file.
|
||||||
|
- unscaled_model (Model): The original OpenSim model before scaling.
|
||||||
|
- Q_coords_scaling (DataFrame): The triangulated coordinates of the markers.
|
||||||
|
- markers (list): The list of marker names.
|
||||||
|
- trimmed_extrema_percent (float): The proportion of the most extreme segment values to remove before calculating their mean.
|
||||||
|
- right_left_symmetry (bool): Whether to consider right and left side of equal size.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- segment_ratio_dict: A dictionary of segment names and their corresponding X, Y, and Z ratios.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# segment_pairs = get_kpt_pairs_from_tree(eval(model_name))
|
# segment_pairs = get_kpt_pairs_from_tree(eval(model_name))
|
||||||
@ -314,7 +326,7 @@ def dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers,
|
|||||||
for (pt1,pt2) in segment_pairs])
|
for (pt1,pt2) in segment_pairs])
|
||||||
# trc_segment_lengths = np.median(trc_segment_lengths, axis=1)
|
# trc_segment_lengths = np.median(trc_segment_lengths, axis=1)
|
||||||
# trc_segment_lengths = np.mean(trc_segment_lengths, axis=1)
|
# trc_segment_lengths = np.mean(trc_segment_lengths, axis=1)
|
||||||
trc_segment_lengths = np.array([trimmed_mean(arr, trimmed_percent=0.5) for arr in trc_segment_lengths])
|
trc_segment_lengths = np.array([trimmed_mean(arr, trimmed_extrema_percent=trimmed_extrema_percent) for arr in trc_segment_lengths])
|
||||||
|
|
||||||
# Get model segment lengths
|
# Get model segment lengths
|
||||||
model_markers = [marker for marker in markers if marker in [m.getName() for m in unscaled_model.getMarkerSet()]]
|
model_markers = [marker for marker in markers if marker in [m.getName() for m in unscaled_model.getMarkerSet()]]
|
||||||
@ -341,6 +353,14 @@ def dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers,
|
|||||||
|
|
||||||
def deactivate_measurements(scaling_root):
|
def deactivate_measurements(scaling_root):
|
||||||
'''
|
'''
|
||||||
|
Deactivate all scalings based on marker positions (called 'measurements' in OpenSim) in the scaling setup file.
|
||||||
|
(will use scaling based on segment sizes instead (called 'manual' in OpenSim))
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- scaling_root (Element): The root element of the scaling setup file.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- scaling_root with deactivated measurements.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
measurement_set = scaling_root.find(".//MeasurementSet/objects")
|
measurement_set = scaling_root.find(".//MeasurementSet/objects")
|
||||||
@ -351,6 +371,15 @@ def deactivate_measurements(scaling_root):
|
|||||||
|
|
||||||
def update_scale_values(scaling_root, segment_ratio_dict):
|
def update_scale_values(scaling_root, segment_ratio_dict):
|
||||||
'''
|
'''
|
||||||
|
Remove previous scaling values ('manual') and
|
||||||
|
add new scaling values based on calculated segment ratios.
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
- scaling_root (Element): The root element of the scaling setup file.
|
||||||
|
- segment_ratio_dict (dict): A dictionary of segment names and their corresponding X, Y, and Z ratios.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- scaling_root with updated scaling values.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Get the ScaleSet/objects element
|
# Get the ScaleSet/objects element
|
||||||
@ -379,17 +408,28 @@ def update_scale_values(scaling_root, segment_ratio_dict):
|
|||||||
def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_left_symmetry=True, subject_height=1.75, subject_mass=70, remove_scaling_setup=True):
|
def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_left_symmetry=True, subject_height=1.75, subject_mass=70, remove_scaling_setup=True):
|
||||||
'''
|
'''
|
||||||
Perform model scaling based on the (not necessarily static) TRC file:
|
Perform model scaling based on the (not necessarily static) TRC file:
|
||||||
- Retrieve the 80% slowest frames, excluding frames where the person is out of frame.
|
- Remove 10% fastest frames (potential outliers)
|
||||||
- From these frames, measure median segment lengths.
|
- Remove frames where coordinate speed is null (person probably out of frame)
|
||||||
- Calculate ratio between model and measured segment lengths -> OpenSim manual scaling.
|
- Remove 40% most extreme calculated segment values (potential outliers)
|
||||||
|
- For each segment, scale on the mean of the remaining segment values
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- config_dict (dict): The configuration dictionary.
|
- trc_file (Path): The path to the TRC file.
|
||||||
- person_id (str): The person identifier (e.g., 'SinglePerson', 'P1').
|
- kinematics_dir (Path): The directory where the kinematics files are saved.
|
||||||
- trc_files (list): List of TRC files to be processed.
|
- osim_setup_dir (Path): The directory where the OpenSim setup and model files are stored.
|
||||||
- output_dir (Path): The directory where the output files should be saved.
|
- model_name (str): The name of the model.
|
||||||
|
- right_left_symmetry (bool): Whether to consider right and left side of equal size.
|
||||||
|
- subject_height (float): The height of the subject.
|
||||||
|
- subject_mass (float): The mass of the subject.
|
||||||
|
- remove_scaling_setup (bool): Whether to remove the scaling setup file after scaling.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- A scaled OpenSim model file.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
fastest_frames_to_remove_percent = 0.1
|
||||||
|
trimmed_extrema_percent = 0.4 # proportion of the most extreme segment values to remove before calculating their mean
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Load model
|
# Load model
|
||||||
opensim.ModelVisualizer.addDirToGeometrySearchPaths(str(osim_setup_dir / 'Geometry'))
|
opensim.ModelVisualizer.addDirToGeometrySearchPaths(str(osim_setup_dir / 'Geometry'))
|
||||||
@ -412,11 +452,12 @@ def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_
|
|||||||
# Using 80% slowest frames for scaling, removing frames when person is out of frame
|
# Using 80% slowest frames for scaling, removing frames when person is out of frame
|
||||||
Q_diff = Q_coords.diff(axis=0).sum(axis=1)
|
Q_diff = Q_coords.diff(axis=0).sum(axis=1)
|
||||||
Q_diff = Q_diff[Q_diff != 0] # remove when speed is 0 (person out of frame)
|
Q_diff = Q_diff[Q_diff != 0] # remove when speed is 0 (person out of frame)
|
||||||
min_speed_indices = Q_diff.abs().nsmallest(int(len(Q_diff) * 0.8)).index
|
min_speed_indices = Q_diff.abs().nsmallest(int(len(Q_diff) * (1-fastest_frames_to_remove_percent))).index
|
||||||
Q_coords_scaling = Q_coords.iloc[min_speed_indices].reset_index(drop=True)
|
Q_coords_scaling = Q_coords.iloc[min_speed_indices].reset_index(drop=True)
|
||||||
|
|
||||||
# Get manual scale values (scale on trimmed mean of measured segments rather than on raw keypoints)
|
# Get manual scale values (scale on trimmed mean of measured segments rather than on raw keypoints)
|
||||||
segment_ratio_dict = dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers, right_left_symmetry=right_left_symmetry)
|
segment_ratio_dict = dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers,
|
||||||
|
trimmed_extrema_percent=trimmed_extrema_percent, right_left_symmetry=right_left_symmetry)
|
||||||
|
|
||||||
# Update scaling setup file
|
# Update scaling setup file
|
||||||
scaling_root[0].find('mass').text = str(subject_mass)
|
scaling_root[0].find('mass').text = str(subject_mass)
|
||||||
@ -445,13 +486,19 @@ def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_
|
|||||||
|
|
||||||
def perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_setup=True):
|
def perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_setup=True):
|
||||||
'''
|
'''
|
||||||
Perform inverse kinematics on the TRC files according to the OpenSim configuration.
|
Perform inverse kinematics based on a TRC file and a scaled OpenSim model:
|
||||||
|
- Model markers follow the triangulated markers while respecting the model kinematic constraints
|
||||||
|
- Joint angles are computed
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- config_dict (dict): The configuration dictionary.
|
- trc_file (Path): The path to the TRC file.
|
||||||
- person_id (str): The person identifier (e.g., 'SinglePerson', 'P1').
|
- kinematics_dir (Path): The directory where the kinematics files are saved.
|
||||||
- trc_files (list): List of TRC files to be processed.
|
- osim_setup_dir (Path): The directory where the OpenSim setup and model files are stored.
|
||||||
- output_dir (Path): The directory where the output files should be saved.
|
- model_name (str): The name of the model.
|
||||||
|
- remove_IK_setup (bool): Whether to remove the IK setup file after running IK.
|
||||||
|
|
||||||
|
OUTPUTS:
|
||||||
|
- A joint angle data file (.mot).
|
||||||
'''
|
'''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -488,14 +535,28 @@ def perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_s
|
|||||||
|
|
||||||
def kinematics(config_dict):
|
def kinematics(config_dict):
|
||||||
'''
|
'''
|
||||||
Runs OpenSim scaling and inverse kinematics on the trc files of triangulated coordinates.
|
Runs OpenSim scaling and inverse kinematics
|
||||||
|
|
||||||
|
Scaling:
|
||||||
|
- No need for a static trial: scaling is done on the triangulated coordinates (trc file)
|
||||||
|
- Remove 10% fastest frames (potential outliers)
|
||||||
|
- Remove frames where coordinate speed is null (person probably out of frame)
|
||||||
|
- Remove 40% most extreme calculated segment values (potential outliers)
|
||||||
|
- For each segment, scale on the mean of the remaining segment values
|
||||||
|
|
||||||
|
Inverse Kinematics:
|
||||||
|
- Run on the scaled model with the same trc file
|
||||||
|
- Model markers follow the triangulated markers while respecting the model kinematic constraints
|
||||||
|
- Joint angles are computed
|
||||||
|
|
||||||
INPUTS:
|
INPUTS:
|
||||||
- config_dict (dict): Generated from a .toml calibration file
|
- config_dict (dict): Generated from a .toml calibration file
|
||||||
|
|
||||||
OUTPUTS:
|
OUTPUTS:
|
||||||
- A scaled .osim model for each person.
|
- A scaled .osim model for each person
|
||||||
- Joint angle data files (.mot) for each person.
|
- Joint angle data files (.mot) for each person
|
||||||
|
- Optionally, OpenSim scaling and IK setup files saved to the kinematics directory
|
||||||
|
- Pose2Sim and OpenSim logs saved to files
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Read config_dict
|
# Read config_dict
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = pose2sim
|
name = pose2sim
|
||||||
version = 0.9.13
|
version = 0.10.0
|
||||||
author = David Pagnon
|
author = David Pagnon
|
||||||
author_email = contact@david-pagnon.com
|
author_email = contact@david-pagnon.com
|
||||||
description = Perform a markerless kinematic analysis from multiple calibrated views as a unified workflow from an OpenPose input to an OpenSim result.
|
description = Perform a markerless kinematic analysis from multiple calibrated views as a unified workflow from an OpenPose input to an OpenSim result.
|
||||||
|
Loading…
Reference in New Issue
Block a user