fixed multi_person + differenciates X,Y,Z in scaling

This commit is contained in:
davidpagnon 2024-09-20 14:42:50 +02:00
parent b793ad1c7c
commit 6f7e883cd3
9 changed files with 48 additions and 38 deletions

View File

@ -179,7 +179,7 @@ make_c3d = true # also save triangulated data in c3d format
make_c3d = true # save triangulated data in c3d format in addition to trc make_c3d = true # save triangulated data in c3d format in addition to trc
[opensim] [kinematics]
use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers
right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb) right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering

View File

@ -179,7 +179,7 @@
# 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] # [kinematics]
# use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers # use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers
# right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb) # right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
# remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering # remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering

View File

@ -179,7 +179,7 @@ keypoints_to_consider = 'all' # 'all' if all points should be considered, for ex
# 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] # [kinematics]
# use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers # use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers
# right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb) # right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
# remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering # remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering

View File

@ -179,7 +179,7 @@ make_c3d = false # also save triangulated data in c3d format
make_c3d = true # save triangulated data in c3d format in addition to trc make_c3d = true # save triangulated data in c3d format in addition to trc
[opensim] [kinematics]
use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers use_augmentation = true # true or false (lowercase) # Set to true if you want to use the model with augmented markers
right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb) right_left_symmetry = true # true or false (lowercase) # Set to false only if you have good reasons to think the participant is not symmetrical (e.g. prosthetic limb)
remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering remove_individual_scaling_setup = true # true or false (lowercase) # If true, the individual scaling setup files are removed to avoid cluttering

View File

@ -217,7 +217,7 @@ def poseEstimation(config=None):
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
# Set up logging # Set up logging
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Batch process all trials # Batch process all trials
@ -264,7 +264,7 @@ def synchronization(config=None):
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
# Set up logging # Set up logging
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Batch process all trials # Batch process all trials
@ -296,7 +296,7 @@ def personAssociation(config=None):
or the function can be called without an argument, in which case it the config directory is the current one. or the function can be called without an argument, in which case it the config directory is the current one.
''' '''
from Pose2Sim.personAssociation import track_2d_all from Pose2Sim.personAssociation import associate_all
# Determine the level at which the function is called (root:2, trial:1) # Determine the level at which the function is called (root:2, trial:1)
level, config_dicts = read_config_files(config) level, config_dicts = read_config_files(config)
@ -308,7 +308,7 @@ def personAssociation(config=None):
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
# Set up logging # Set up logging
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Batch process all trials # Batch process all trials
@ -326,7 +326,7 @@ def personAssociation(config=None):
logging.info(f"Project directory: {project_dir}") logging.info(f"Project directory: {project_dir}")
logging.info("---------------------------------------------------------------------\n") logging.info("---------------------------------------------------------------------\n")
track_2d_all(config_dict) associate_all(config_dict)
end = time.time() end = time.time()
elapsed = end-start elapsed = end-start
@ -354,7 +354,7 @@ def triangulation(config=None):
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
# Set up logging # Set up logging
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Batch process all trials # Batch process all trials
@ -400,7 +400,7 @@ def filtering(config=None):
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
# Set up logging # Set up logging
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Batch process all trials # Batch process all trials
@ -441,7 +441,7 @@ def markerAugmentation(config=None):
raise ValueError('Please specify the project directory in config_dict:\n \ raise ValueError('Please specify the project directory in config_dict:\n \
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
for config_dict in config_dicts: for config_dict in config_dicts:
@ -485,7 +485,7 @@ def kinematics(config=None):
raise ValueError('Please specify the project directory in config_dict:\n \ raise ValueError('Please specify the project directory in config_dict:\n \
config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})') config_dict.get("project").update({"project_dir":"<YOUR_TRIAL_DIRECTORY>"})')
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
# Process each configuration dictionary # Process each configuration dictionary
@ -519,7 +519,7 @@ def runAll(config=None, do_calibration=True, do_poseEstimation=True, do_synchron
# Set up logging # Set up logging
level, config_dicts = read_config_files(config) level, config_dicts = read_config_files(config)
session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '..')) session_dir = os.path.realpath(os.path.join(config_dicts[0].get('project').get('project_dir'), '.'))
setup_logging(session_dir) setup_logging(session_dir)
currentDateAndTime = datetime.now() currentDateAndTime = datetime.now()

View File

@ -211,7 +211,7 @@ def reprojection(P_all, Q):
y_calc.append(P_cam[1] @ Q / (P_cam[2] @ Q)) y_calc.append(P_cam[1] @ Q / (P_cam[2] @ Q))
return x_calc, y_calc return x_calc, y_calc
def euclidean_distance(q1, q2): def euclidean_distance(q1, q2):
''' '''

View File

@ -275,7 +275,7 @@ def dict_segment_marker_pairs(scaling_root, right_left_symmetry=True):
''' '''
measurement_dict = {} segment_markers_dict = {}
for measurement in scaling_root.findall(".//Measurement"): for measurement in scaling_root.findall(".//Measurement"):
# Collect all marker pairs for this measurement # Collect all marker pairs for this measurement
marker_pairs = [pair.find('markers').text.strip().split() for pair in measurement.findall(".//MarkerPair")] marker_pairs = [pair.find('markers').text.strip().split() for pair in measurement.findall(".//MarkerPair")]
@ -283,17 +283,22 @@ def dict_segment_marker_pairs(scaling_root, right_left_symmetry=True):
# Collect all body scales for this measurement # Collect all body scales for this measurement
for body_scale in measurement.findall(".//BodyScale"): for body_scale in measurement.findall(".//BodyScale"):
body_name = body_scale.get('name') body_name = body_scale.get('name')
if right_left_symmetry: axes = body_scale.find('axes').text.strip().split()
measurement_dict[body_name] = marker_pairs for axis in axes:
else: body_name_axis = f"{body_name}_{axis}"
if body_name.endswith('_r'): if right_left_symmetry:
marker_pairs_r = [pair for pair in marker_pairs if any([pair[0].startswith('R'), pair[1].startswith('R')])] segment_markers_dict.setdefault(body_name_axis, []).extend(marker_pairs)
measurement_dict[body_name] = marker_pairs_r else:
elif body_name.endswith('_l'): if body_name.endswith('_r'):
marker_pairs_l = [pair for pair in marker_pairs if any([pair[0].startswith('L'), pair[1].startswith('L')])] marker_pairs_r = [pair for pair in marker_pairs if any([pair[0].upper().startswith('R'), pair[1].upper().startswith('R')])]
measurement_dict[body_name] = marker_pairs_l segment_markers_dict.setdefault(body_name_axis, []).extend(marker_pairs_r)
elif body_name.endswith('_l'):
marker_pairs_l = [pair for pair in marker_pairs if any([pair[0].upper().startswith('L'), pair[1].upper().startswith('L')])]
segment_markers_dict.setdefault(body_name_axis, []).extend(marker_pairs_l)
else:
segment_markers_dict.setdefault(body_name_axis, []).extend(marker_pairs)
return measurement_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, right_left_symmetry=True):
@ -321,10 +326,15 @@ def dict_segment_ratio(scaling_root, unscaled_model, Q_coords_scaling, markers,
# Calculate ratio for each segment # Calculate ratio for each segment
segment_ratios = trc_segment_lengths / model_segment_lengths segment_ratios = trc_segment_lengths / model_segment_lengths
segment_markers_dict = dict_segment_marker_pairs(scaling_root, right_left_symmetry=right_left_symmetry) segment_markers_dict = dict_segment_marker_pairs(scaling_root, right_left_symmetry=right_left_symmetry)
segment_ratio_dict = segment_markers_dict.copy() segment_ratio_dict_temp = segment_markers_dict.copy()
segment_ratio_dict.update({key: np.mean([segment_ratios[segment_pairs.index(k)] segment_ratio_dict_temp.update({key: np.mean([segment_ratios[segment_pairs.index(k)]
for k in segment_markers_dict[key]]) for k in segment_markers_dict[key]])
for key in segment_markers_dict.keys()}) for key in segment_markers_dict.keys()})
# Merge X, Y, Z ratios into single key
segment_ratio_dict={}
xyz_keys = list(set([key[:-2] for key in segment_ratio_dict_temp.keys()]))
for key in xyz_keys:
segment_ratio_dict[key] = [segment_ratio_dict_temp[key+'_X'], segment_ratio_dict_temp[key+'_Y'], segment_ratio_dict_temp[key+'_Z']]
return segment_ratio_dict return segment_ratio_dict
@ -351,11 +361,11 @@ def update_scale_values(scaling_root, segment_ratio_dict):
scale_set.remove(scale) scale_set.remove(scale)
# Add new Scale elements based on scale_dict # Add new Scale elements based on scale_dict
for segment, scale in segment_ratio_dict.items(): for segment, scales in segment_ratio_dict.items():
new_scale = etree.Element('Scale') new_scale = etree.Element('Scale')
# scales # scales
scales_elem = etree.SubElement(new_scale, 'scales') scales_elem = etree.SubElement(new_scale, 'scales')
scales_elem.text = ' '.join([str(scale)]*3) scales_elem.text = ' '.join(map(str, scales))
# segment name # segment name
segment_elem = etree.SubElement(new_scale, 'segment') segment_elem = etree.SubElement(new_scale, 'segment')
segment_elem.text = segment segment_elem.text = segment
@ -394,8 +404,8 @@ def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_
scaling_path = get_scaling_setup(model_name, osim_setup_dir) scaling_path = get_scaling_setup(model_name, osim_setup_dir)
scaling_tree = etree.parse(scaling_path) scaling_tree = etree.parse(scaling_path)
scaling_root = scaling_tree.getroot() scaling_root = scaling_tree.getroot()
scaling_path_temp = str(kinematics_dir / Path(scaling_path).name) scaling_path_temp = str(kinematics_dir / (trc_file.stem + '_scaling_setup.xml'))
# Read trc file # Read trc file
Q_coords, _, _, markers, _ = read_trc(trc_file) Q_coords, _, _, markers, _ = read_trc(trc_file)
@ -415,8 +425,7 @@ def perform_scaling(trc_file, kinematics_dir, osim_setup_dir, model_name, right_
scaling_root[0].find(".//scaling_order").text = ' manualScale measurements' scaling_root[0].find(".//scaling_order").text = ' manualScale measurements'
deactivate_measurements(scaling_root) deactivate_measurements(scaling_root)
update_scale_values(scaling_root, segment_ratio_dict) update_scale_values(scaling_root, segment_ratio_dict)
for mk_f in scaling_root[0].findall(".//marker_file"): for mk_f in scaling_root[0].findall(".//marker_file"): mk_f.text = "Unassigned"
mk_f.text = "Unassigned"
scaling_root[0].find('ModelScaler').find('output_model_file').text = str(scaled_model_path) scaling_root[0].find('ModelScaler').find('output_model_file').text = str(scaled_model_path)
etree.indent(scaling_tree, space='\t', level=0) etree.indent(scaling_tree, space='\t', level=0)
@ -448,7 +457,7 @@ def perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_s
try: try:
# Retrieve data # Retrieve data
ik_path = get_IK_Setup(model_name, osim_setup_dir) ik_path = get_IK_Setup(model_name, osim_setup_dir)
ik_path_temp = str(kinematics_dir / Path(ik_path).name) ik_path_temp = str(kinematics_dir / (trc_file.stem + '_ik_setup.xml'))
scaled_model_path = (kinematics_dir / (trc_file.stem + '.osim')).resolve() scaled_model_path = (kinematics_dir / (trc_file.stem + '.osim')).resolve()
output_motion_file = Path(kinematics_dir, trc_file.stem + '.mot').resolve() output_motion_file = Path(kinematics_dir, trc_file.stem + '.mot').resolve()
if not trc_file.exists(): if not trc_file.exists():
@ -546,7 +555,7 @@ def kinematics(config_dict):
elif not type(subject_mass) == list: elif not type(subject_mass) == list:
subject_mass = [subject_mass] subject_mass = [subject_mass]
elif len(subject_mass) < len(trc_files): elif len(subject_mass) < len(trc_files):
logging.warning("Number of subject masses does not match number of TRC files. Missing masses are set to 70kg.") logging.warning("Number of subject masses does not match number of TRC files. Missing masses are set to 70kg.\n")
subject_mass += [70] * (len(trc_files) - len(subject_mass)) subject_mass += [70] * (len(trc_files) - len(subject_mass))
# Perform scaling and IK for each trc file # Perform scaling and IK for each trc file
@ -558,7 +567,7 @@ def kinematics(config_dict):
logging.info(f"\tDone. OpenSim logs saved to {opensim_logs_file.resolve()}.") logging.info(f"\tDone. OpenSim logs saved to {opensim_logs_file.resolve()}.")
logging.info(f"\tScaled model saved to {(kinematics_dir / (trc_file.stem + '_scaled.osim')).resolve()}") logging.info(f"\tScaled model saved to {(kinematics_dir / (trc_file.stem + '_scaled.osim')).resolve()}")
logging.info("\nInverse Kinematics...") logging.info("Inverse Kinematics...")
perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_setup=remove_IK_setup) perform_IK(trc_file, kinematics_dir, osim_setup_dir, model_name, remove_IK_setup=remove_IK_setup)
logging.info(f"\tDone. OpenSim logs saved to {opensim_logs_file.resolve()}.") logging.info(f"\tDone. OpenSim logs saved to {opensim_logs_file.resolve()}.")
logging.info(f"\tJoint angle data saved to {(kinematics_dir / (trc_file.stem + '.mot')).resolve()}\n") logging.info(f"\tJoint angle data saved to {(kinematics_dir / (trc_file.stem + '.mot')).resolve()}\n")

View File

@ -604,7 +604,7 @@ def recap_tracking(config_dict, error=0, nb_cams_excluded=0):
logging.info(f'\nTracked json files are stored in {os.path.realpath(poseTracked_dir)}.') logging.info(f'\nTracked json files are stored in {os.path.realpath(poseTracked_dir)}.')
def track_2d_all(config_dict): def associate_all(config_dict):
''' '''
For each frame, For each frame,
- Find all possible combinations of detected persons - Find all possible combinations of detected persons

View File

@ -191,6 +191,7 @@ def sort_people(Q_kpt_old, Q_kpt):
frame_by_frame_dist = [] frame_by_frame_dist = []
for comb in personsIDs_comb: for comb in personsIDs_comb:
frame_by_frame_dist += [euclidean_distance(Q_kpt_old[comb[0]],Q_kpt[comb[1]])] frame_by_frame_dist += [euclidean_distance(Q_kpt_old[comb[0]],Q_kpt[comb[1]])]
frame_by_frame_dist = np.mean(frame_by_frame_dist, axis=1)
# sort correspondences by distance # sort correspondences by distance
minL, _, associated_tuples = min_with_single_indices(frame_by_frame_dist, personsIDs_comb) minL, _, associated_tuples = min_with_single_indices(frame_by_frame_dist, personsIDs_comb)