pose2sim/Pose2Sim/Utilities/trc_from_easymocap.py

169 lines
6.1 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
#####################################################
## Convert EasyMocap results to TRC ##
#####################################################
Converts EasyMocap keypoints3D results to TRC files.
If several people are detected, writes one TRC file per person.
N.B.: If you run EasyMocap with a different model, edit KEYPOINT_NAMES
accordingly (names and order of keypoints).
N.B.: EasyMocap currently attributes even IDs to the detected people,
so the trc files with odd numbers may be empty. Left this way for it
to still work the day they fix it.
N.B.: Trc framerate is set to 1 by default.
Usage:
python -m trc_from_easymocap -i input_keypoint_dir
python -m trc_from_easymocap -i input_keypoint_dir -o output_trc_dir
import trc_from_easymocap; trc_from_easymocap.trc_from_easymocap_func(input_keypoint_dir=r'<input_keypoint_dir>', output_trc_dir=r'<output_trc_dir>')
'''
## CONSTANTS
KEYPOINT_NAMES = ['Nose', 'Neck','RShoulder','RElbow','RWrist','LShoulder','LElbow','LWrist','CHip','RHip','RKnee','RAnkle','LHip','LKnee','LAnkle','REye','LEye','REar','LEar','LBigToe','LSmallToe','LHeel','RBigToe','RSmallToe','RHeel']
## INIT
import argparse
import os
import glob
import json
import numpy as np
import pandas as pd
## AUTHORSHIP INFORMATION
__author__ = "David Pagnon"
__copyright__ = "Copyright 2021, Pose2Sim"
__credits__ = ["David Pagnon"]
__license__ = "BSD 3-Clause License"
__version__ = "0.8.2"
__maintainer__ = "David Pagnon"
__email__ = "contact@david-pagnon.com"
__status__ = "Development"
## FUNCTIONS
def zup2yup(Q):
'''
X->Y, Y->Z, Z->X
'''
cols = list(Q.columns)
cols = np.array([[cols[i*3+1],cols[i*3+2],cols[i*3]] for i in range(int(len(cols)/3))]).flatten()
Q = Q[cols]
return Q
def max_persons(keypoint_files):
'''
Max number of persons in easymocap results
'''
max_id = 0
for f in keypoint_files:
with open(f, 'r') as json_f:
js = json.load(json_f)
for p in js:
if p['id'] > max_id:
max_id = p['id']
max_id += 1
return max_id
def df_from_easymocap(keypoint_files, max_id):
'''
Stores keypoint_files data in a list of dataframes with the IDs given by EasyMocap.
'''
Q = [[] for n in range(max_id)]
for f in keypoint_files:
with open(f, 'r') as json_f:
js = json.load(json_f)
idx_persons_in_frame = [p['id'] for p in js]
for idx in range(max_id):
if idx in idx_persons_in_frame:
x = [p['id'] for p in js].index(idx)
keypoints3d_frame = np.array(js[x]['keypoints3d'])[:, :3].flatten().tolist()
else:
keypoints3d_frame = [np.nan]*len(KEYPOINT_NAMES)*3
Q[idx].append(keypoints3d_frame)
Q_df = [pd.DataFrame(Q[idx]) for idx in range(max_id)]
return Q_df
def write_trc(Q_df, output_trc_dir, trc_root_name):
'''
Writes a list of dataframes in a directory with a given root name.
'''
for idx, Q in enumerate(Q_df):
DataRate = CameraRate = OrigDataRate = 1
NumFrames = len(Q)
NumMarkers = len(KEYPOINT_NAMES)
header_trc = ['PathFileType\t4\t(X/Y/Z)\t'+trc_root_name+str(idx),
'DataRate\tCameraRate\tNumFrames\tNumMarkers\tUnits\tOrigDataRate\tOrigDataStartFrame\tOrigNumFrames',
'\t'.join(map(str,[DataRate, CameraRate, NumFrames, NumMarkers, 'm', OrigDataRate, 0, NumFrames])),
'Frame#\tTime\t' + '\t\t\t'.join(KEYPOINT_NAMES) + '\t\t',
'\t\t'+'\t'.join([f'X{i+1}\tY{i+1}\tZ{i+1}' for i in range(len(KEYPOINT_NAMES))])]
Q = zup2yup(Q_df[idx])
Q.index = np.array(range(NumFrames)) + 1
Q.insert(0, 't', Q.index / DataRate)
trc_path = os.path.realpath(os.path.join(output_trc_dir, trc_root_name+str(idx)+'.trc'))
with open(trc_path, 'w') as trc_o:
[trc_o.write(line+'\n') for line in header_trc]
Q.to_csv(trc_o, sep='\t', index=True, header=None, lineterminator='\n')
def trc_from_easymocap_func(**kwargs):
'''
Converts EasyMocap keypoints3D results to TRC files.
If several people are detected, writes one TRC file per person.
N.B.: If you run EasyMocap with a different model, edit KEYPOINT_NAMES
accordingly (names and order of keypoints).
N.B.: EasyMocap currently attributes even IDs to the detected people,
so the trc files with odd numbers may be empty. Left this way for it
to still work the day they fix it.
N.B.: Trc framerate is set to 1 by default.
Usage:
trc_from_easymocap -i input_keypoint_dir
trc_from_easymocap -i input_keypoint_dir -o output_trc_dir
import trc_from_easymocap; trc_from_easymocap.trc_from_easymocap_func(input_keypoint_dir=r'<input_keypoint_dir>', output_trc_dir=r'<output_trc_dir>')
'''
input_keypoint_dir = kwargs.get('input_keypoint_dir')
output_trc_dir = kwargs.get('output_trc_dir')
input_keypoint_dir = os.path.abspath(input_keypoint_dir)
output_trc_dir = os.path.abspath(output_trc_dir) if output_trc_dir else os.path.dirname(input_keypoint_dir)
if not os.path.exists(output_trc_dir): os.makedirs(output_trc_dir)
trc_root_name = os.path.basename(os.path.dirname(os.path.dirname(input_keypoint_dir)))
keypoint_files = sorted(glob.glob(input_keypoint_dir+'/*.json'))
max_id = max_persons(keypoint_files)
Q_df = df_from_easymocap(keypoint_files, max_id)
write_trc(Q_df, output_trc_dir=output_trc_dir, trc_root_name=trc_root_name)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input_keypoint_dir', required = True, help='directory on input keypoints3D folder with EasyMocap json files')
parser.add_argument('-o', '--output_trc_dir', required = False, help='direction of the gait. If negative, you need to include an equal sign in the argument, eg -d=-Z')
kwargs = vars(parser.parse_args())
trc_from_easymocap_func(**kwargs)