diff --git a/README.md b/README.md index cb3dfa1..94fb091 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ A library for using motion capture data for machine learning * Convert data representations * [Euler angles to positions](#convert-to-positions) * Euler angles to exponential maps + * Euler angles to axis angles * Exponential maps to euler angles * Body-oriented global translation and rotation calculation with inverse tranform * Root-centric position normalizer with inverse tranform diff --git a/pymo/preprocessing.py b/pymo/preprocessing.py index a7e12e6..fdea825 100644 --- a/pymo/preprocessing.py +++ b/pymo/preprocessing.py @@ -31,6 +31,8 @@ def transform(self, X, y=None): return X elif self.param_type == 'position': return self._to_pos(X) + elif self.param_type == 'axis_angle': + return self._to_axis_angle(X) else: raise UnsupportedParamError('Unsupported param: %s. Valid param types are: euler, quat, expmap, position' % self.param_type) # return X @@ -153,6 +155,80 @@ def _to_pos(self, X): return Q + def _to_axis_angle(self, X): + '''Converts joints rotations in Euler angles to axis angle rotations''' + + Q = [] + for track in X: + # fix track names + # adapt joint name so that it's equal for either male or female + channels = [] + titles = [] + euler_df = track.values + + # Create a new DataFrame to store the axis angle values + axis_anlge_df = pd.DataFrame(index=euler_df.index) + + # List the columns that contain rotation channels + rot_cols = [c for c in euler_df.columns if ('rotation' in c)] + + # List the columns that contain position channels + pos_cols = [c for c in euler_df.columns if ('position' in c)] + + # List the joints that are not end sites, i.e., have channels + joints = (joint for joint in track.skeleton) + + tree_data = {} + + for joint in track.traverse(): + parent = track.skeleton[joint]['parent'] + + # Get the rotation columns that belong to this joint + rc = euler_df[[c for c in rot_cols if joint in c]] + + # Get the position columns that belong to this joint + pc = euler_df[[c for c in pos_cols if joint in c]] + + # Make sure the columns are organized in xyz order + if rc.shape[1] < 3: + euler_values = [[0,0,0] for f in rc.iterrows()] + else: + euler_values = [[f[1]['%s_Xrotation'%joint], + f[1]['%s_Yrotation'%joint], + f[1]['%s_Zrotation'%joint]] for f in rc.iterrows()] + + ################# in euler angle, the order of rotation axis is very important ##################### + rotation_order = rc.columns[0][rc.columns[0].find('rotation') - 1] + rc.columns[1][rc.columns[1].find('rotation') - 1] + rc.columns[2][rc.columns[2].find('rotation') - 1] #rotation_order is string : 'XYZ' or'ZYX' or ... + #################################################################################################### + + if pc.shape[1] < 3: + pos_values = [[0,0,0] for f in pc.iterrows()] + else: + pos_values =[[f[1]['%s_Xposition'%joint], + f[1]['%s_Yposition'%joint], + f[1]['%s_Zposition'%joint]] for f in pc.iterrows()] + + # Convert the eulers to axis angles + ############################ input rotation order as Rotation class's argument ######################### + axis_angles = np.asarray([Rotation([f[0], f[1], f[2]], 'euler', rotation_order, from_deg=True).to_axis_angle() for f in euler_values]) + ######################################################################################################## + + # Create the corresponding columns in the new DataFrame + axis_anlge_df['%s_Xposition'%joint] = pd.Series(data=[e[0] for e in pos_values], index=axis_anlge_df.index) + axis_anlge_df['%s_Yposition'%joint] = pd.Series(data=[e[1] for e in pos_values], index=axis_anlge_df.index) + axis_anlge_df['%s_Zposition'%joint] = pd.Series(data=[e[2] for e in pos_values], index=axis_anlge_df.index) + + axis_anlge_df['%s_Xrotation'%joint] = pd.Series(data=[e[0] for e in axis_angles], index=axis_anlge_df.index) + axis_anlge_df['%s_Yrotation'%joint] = pd.Series(data=[e[1] for e in axis_angles], index=axis_anlge_df.index) + axis_anlge_df['%s_Zrotation'%joint] = pd.Series(data=[e[2] for e in axis_angles], index=axis_anlge_df.index) + + + new_track = track.clone() + new_track.values = axis_anlge_df + Q.append(new_track) + return Q + + def _to_expmap(self, X): '''Converts Euler angles to Exponential Maps''' diff --git a/pymo/rotation_tools.py b/pymo/rotation_tools.py index ba208a9..3137f32 100644 --- a/pymo/rotation_tools.py +++ b/pymo/rotation_tools.py @@ -9,6 +9,7 @@ import math import numpy as np +from scipy.spatial.transform import Rotation as SpRotation def deg2rad(x): return x/180*math.pi @@ -99,6 +100,15 @@ def get_euler_axis(self): axis = axis/(2*math.sin(theta)) return theta, axis + def to_axis_angle(self): + # transpose matrix because we're building the rotation matrix in a different way + transposed_rot_matrix = np.matrix.transpose(self.rotmat) + + sp_rot = SpRotation.from_matrix(transposed_rot_matrix) + rot_vec = sp_rot.as_rotvec() + + return rot_vec + def to_expmap(self): theta, axis = self.get_euler_axis() rot_arr = theta * axis diff --git a/setup.py b/setup.py index 3e5bbf2..256fca3 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ 'numpy', 'pandas', 'matplotlib', + 'scipy', 'scikit-learn' ] -) \ No newline at end of file +)