In [1]:
import matplotlib.pyplot as plt
import user_data_loader as udl
import pandas as pd

Main Functions


In [2]:
# draw a vector
# retrieved from: http://stackoverflow.com/a/11156353/630598
from matplotlib.patches import FancyArrowPatch
from mpl_toolkits.mplot3d import proj3d

class Arrow3D(FancyArrowPatch):
    def __init__(self, xs, ys, zs, *args, **kwargs):
        FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
        self._verts3d = xs, ys, zs

    def draw(self, renderer):
        xs3d, ys3d, zs3d = self._verts3d
        xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
        self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
        FancyArrowPatch.draw(self, renderer)

In [3]:
# Specifying all the links between joints:
def get_links(data):
    ''' Returns a dict with all the links of the skeleton. 
        Arguments:
            data: a pd.DataFrame with columns with joint names'''
    return { 'head_neck': list(zip(data.T['head'].values, data.T['neck'].values)),
                'neck_lshoulder': zip(data.T['neck'].values, data.T['left_shoulder'].values),
                'lshoulder_lelbow': zip(data.T['left_shoulder'].values, data.T['left_elbow'].values),
                'lelbow_lhand': zip(data.T['left_elbow'].values, data.T['left_hand'].values),
                'neck_rshoulder': zip(data.T['neck'].values, data.T['right_shoulder'].values),
                'rshoulder_relbow': zip(data.T['right_shoulder'].values, data.T['right_elbow'].values),
                'relbow_rhand': zip(data.T['right_elbow'].values, data.T['right_hand'].values),
                'lshoulder_torso': zip(data.T['left_shoulder'].values, data.T['torso'].values),
                'rshoulder_torso': zip(data.T['right_shoulder'].values, data.T['torso'].values),
                'torso_lhip': zip(data.T['torso'].values, data.T['left_hip'].values),
                'lhip_rhip': zip(data.T['left_hip'].values, data.T['right_hip'].values),
                'lhip_lknee': zip(data.T['left_hip'].values, data.T['left_knee'].values),
                'lknee_lfoot': zip(data.T['left_knee'].values, data.T['left_foot'].values),
                'torso_rhip': zip(data.T['torso'].values, data.T['right_hip'].values),
                'rhip_rknee': zip(data.T['right_hip'].values, data.T['right_knee'].values),
                'rknee_rfoot': zip(data.T['right_knee'].values, data.T['right_foot'].values)
            }

In [4]:
def plot_skeleton(axis, datapoints, links=False, **kwargs):
    ''' Plots a skeleton in 3D'''
    axis.scatter(datapoints.x, datapoints.y, datapoints.z, **kwargs)
    if links:
        joint_links = get_links(datapoints)
        for jl in joint_links:                      # adding joint links to the plot:
            arrow = Arrow3D(joint_links[jl][0], joint_links[jl][1], joint_links[jl][2], lw=1, arrowstyle="-", **kwargs)
            axis.add_artist(arrow)

In [4]:


In [5]:
def to_xyz(series, colors=None):
    ''' converts series with index = head_pos_x, head_pos_y, etc... 
        to a dataframe with index=joints and columns = x, y z '''
    def_colors = {'STAND_POINTING_RIGHT':'red', 'STAND_POINTING_FORWARD':'green', 'STAND_POINTING_LEFT':'blue'}
    c = colors if colors else def_colors
    xyz = pd.DataFrame(index=udl.joints, columns=['x','y','z', 'color'])
    x = series[udl.ind_pos_x]
    y = series[udl.ind_pos_y]
    z = series[udl.ind_pos_z]
    for d in (x,y,z): # renaming index so it is the same as xyz
        d.index = udl.joints
    xyz.x, xyz.y, xyz.z = x, y, z
    xyz.color = c[series[-1]]
    return xyz
    
def irow_to_xyz(irow, **kwargs):
    ''' Helper function to pass the pd.iterrows tuple to the to_xyz function '''
    return to_xyz(irow[1], **kwargs)

def df_to_xyz(df):
    ''' converts a a pd.Dataframe with user data to a '3D-plottable' dataframe '''
    return pd.concat(map(irow_to_xyz, df.iterrows()))

Load a file


In [6]:
user_file = '../data/exp03-user13.arff'
#user_file = 'data-torso-relative/exp03-user13.arff'
#user_file = 'data-torso-relative/exp03-user20.arff'
e03u01 = udl.load_user_file(user_file)

In [7]:
e03u01.head()


Out[7]:
h_seqNum h_stamp user_id head_confidence head_pos_x head_pos_y head_pos_z head_orient_x head_orient_y head_orient_z head_orient_w neck_confidence neck_pos_x neck_pos_y neck_pos_z neck_orient_x neck_orient_y neck_orient_z neck_orient_w torso_confidence
0 0.0 1324392042.71 3.0 1 -0.510045 0.236679 3.291093 0.089709 0.831996 0.139070 0.529523 1 -0.510480 0.030301 3.219823 0.089709 0.831996 0.139070 0.529523 1 ...
1 0.0 1324392042.76 3.0 0 -0.502725 0.219912 3.290291 0.101461 0.835196 0.133464 0.523775 1 -0.509226 0.013109 3.218151 0.101461 0.835196 0.133464 0.523775 1 ...
2 0.0 1324392042.84 3.0 0 -0.510593 0.198147 3.279210 0.090080 0.843601 0.145246 0.509045 1 -0.511495 -0.008547 3.205283 0.090080 0.843601 0.145246 0.509045 1 ...
3 0.0 1324392042.92 3.0 0 -0.516177 0.178483 3.262746 0.082526 0.857139 0.149674 0.485902 1 -0.515302 -0.028464 3.188726 0.082526 0.857139 0.149674 0.485902 1 ...
4 0.0 1324392043.0 3.0 0 -0.523917 0.160048 3.259767 0.070075 0.860676 0.161180 0.477857 1 -0.516571 -0.046199 3.184054 0.070075 0.860676 0.161180 0.477857 1 ...

5 rows × 124 columns

Plotting the Skeleton

Plotting an unnormalized skeleton


In [8]:
%pylab


Using matplotlib backend: MacOSX
Populating the interactive namespace from numpy and matplotlib

Making the DataFrame plottable


In [9]:
e03u01.columns = udl.index

In [10]:
xyz_03u01 = df_to_xyz(e03u01)

Grouping the DataFrame points by color so later is easier to plot them


In [11]:
clouds = xyz_03u01.groupby('color')

Separating by pose and calculating the pose means so we can plot the skeleton links


In [12]:
means = e03u01.groupby('pose').mean()
means.insert(len(means.columns), 'pose', means.index )
# Prepare means to be printed:
m_groups = [to_xyz(means.ix[i]) for i,ind in enumerate(means.index)]

Plotting the skeleton cloud and its means


In [13]:
# Plot skeleton joints and links
ax = plt.axes(projection='3d')

for c, values in clouds:
    ax.scatter(values.x, values.y, values.z, color=c, alpha=0.2, marker='o')
    
for m in m_groups:
    col = m['color'][0] # Just need the 1st one
    plot_skeleton(ax, m, links=True, color=col)

ax.view_init(-90,90)
#plt.savefig('/Users/almudenasanz/Downloads/skeleton.pdf', format='pdf')

Plotting Normalized Skeleton


In [14]:
udl = reload(udl)

In [15]:
user_file = '../data/exp03-user13.arff'
#user_file = 'data-torso-relative/exp03-user13.arff'
#user_file = 'data-torso-relative/exp03-user20.arff'
e03u01 = udl.load_user_file(user_file)

In [16]:
e03u01.head()


Out[16]:
h_seqNum h_stamp user_id head_confidence head_pos_x head_pos_y head_pos_z head_orient_x head_orient_y head_orient_z head_orient_w neck_confidence neck_pos_x neck_pos_y neck_pos_z neck_orient_x neck_orient_y neck_orient_z neck_orient_w torso_confidence
0 0.0 1324392042.71 3.0 1 -0.510045 0.236679 3.291093 0.089709 0.831996 0.139070 0.529523 1 -0.510480 0.030301 3.219823 0.089709 0.831996 0.139070 0.529523 1 ...
1 0.0 1324392042.76 3.0 0 -0.502725 0.219912 3.290291 0.101461 0.835196 0.133464 0.523775 1 -0.509226 0.013109 3.218151 0.101461 0.835196 0.133464 0.523775 1 ...
2 0.0 1324392042.84 3.0 0 -0.510593 0.198147 3.279210 0.090080 0.843601 0.145246 0.509045 1 -0.511495 -0.008547 3.205283 0.090080 0.843601 0.145246 0.509045 1 ...
3 0.0 1324392042.92 3.0 0 -0.516177 0.178483 3.262746 0.082526 0.857139 0.149674 0.485902 1 -0.515302 -0.028464 3.188726 0.082526 0.857139 0.149674 0.485902 1 ...
4 0.0 1324392043.0 3.0 0 -0.523917 0.160048 3.259767 0.070075 0.860676 0.161180 0.477857 1 -0.516571 -0.046199 3.184054 0.070075 0.860676 0.161180 0.477857 1 ...

5 rows × 124 columns


In [21]:
e03u01.columns = udl.make_multiindex(udl.joints, udl.attribs)

In [22]:
e03u01.head()


Out[22]:
joint header head neck torso
attrib h_seqNum h_stamp user_id confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence
0 0.0 1324392042.71 3.0 1 -0.510045 0.236679 3.291093 0.089709 0.831996 0.139070 0.529523 1 -0.510480 0.030301 3.219823 0.089709 0.831996 0.139070 0.529523 1 ...
1 0.0 1324392042.76 3.0 0 -0.502725 0.219912 3.290291 0.101461 0.835196 0.133464 0.523775 1 -0.509226 0.013109 3.218151 0.101461 0.835196 0.133464 0.523775 1 ...
2 0.0 1324392042.84 3.0 0 -0.510593 0.198147 3.279210 0.090080 0.843601 0.145246 0.509045 1 -0.511495 -0.008547 3.205283 0.090080 0.843601 0.145246 0.509045 1 ...
3 0.0 1324392042.92 3.0 0 -0.516177 0.178483 3.262746 0.082526 0.857139 0.149674 0.485902 1 -0.515302 -0.028464 3.188726 0.082526 0.857139 0.149674 0.485902 1 ...
4 0.0 1324392043.0 3.0 0 -0.523917 0.160048 3.259767 0.070075 0.860676 0.161180 0.477857 1 -0.516571 -0.046199 3.184054 0.070075 0.860676 0.161180 0.477857 1 ...

5 rows × 124 columns


In [25]:
# Removing foots
#e03u01_norm.drop(['left_foot', 'right_foot'], axis=1, level='joint')

Normalize data. Note that we keep the original data of the torso in orig_torso


In [26]:
orig_torso, df_normalized = udl.normalize_joints(e03u01, 'torso')

In [27]:
df_normalized.head()


Out[27]:
joint head neck left_shoulder
attrib confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence pos_x pos_y pos_z
0 0 0.002246 0.427557 0.145069 0.000355 -0.000968 0.005861 -0.000044 0 0.001811 0.221179 0.073799 0.000355 -0.000968 0.005861 -0.000044 0 0.071439 0.173471 0.215013 ...
1 -1 0.014066 0.427201 0.147727 0.000206 -0.000482 0.002851 0.000011 0 0.007565 0.220398 0.075587 0.000206 -0.000482 0.002851 0.000011 0 0.078430 0.170023 0.215264 ...
2 -1 0.002196 0.426360 0.151841 0.000271 -0.000280 0.001617 -0.000043 0 0.001294 0.219666 0.077914 0.000271 -0.000280 0.001617 -0.000043 0 0.077832 0.170767 0.214997 ...
3 -1 -0.001633 0.426574 0.152250 0.000185 -0.000156 0.000908 -0.000034 0 -0.000758 0.219627 0.078230 0.000185 -0.000156 0.000908 -0.000034 0 0.083698 0.172759 0.211098 ...
4 -1 -0.015047 0.424978 0.155844 0.000205 -0.000100 0.000583 -0.000046 0 -0.007701 0.218731 0.080131 0.000205 -0.000100 0.000583 -0.000046 0 0.079975 0.173812 0.211732 ...

5 rows × 112 columns

Update e03u01 with the normalized data


In [28]:
e03u01.update(df_normalized)

In [29]:
e03u01.head()


Out[29]:
joint header head neck torso
attrib h_seqNum h_stamp user_id confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w confidence
0 0.0 1324392042.71 3.0 0 0.002246 0.427557 0.145069 0.000355 -0.000968 0.005861 -0.000044 0 0.001811 0.221179 0.073799 0.000355 -0.000968 0.005861 -0.000044 1 ...
1 0.0 1324392042.76 3.0 -1 0.014066 0.427201 0.147727 0.000206 -0.000482 0.002851 0.000011 0 0.007565 0.220398 0.075587 0.000206 -0.000482 0.002851 0.000011 1 ...
2 0.0 1324392042.84 3.0 -1 0.002196 0.426360 0.151841 0.000271 -0.000280 0.001617 -0.000043 0 0.001294 0.219666 0.077914 0.000271 -0.000280 0.001617 -0.000043 1 ...
3 0.0 1324392042.92 3.0 -1 -0.001633 0.426574 0.152250 0.000185 -0.000156 0.000908 -0.000034 0 -0.000758 0.219627 0.078230 0.000185 -0.000156 0.000908 -0.000034 1 ...
4 0.0 1324392043.0 3.0 -1 -0.015047 0.424978 0.155844 0.000205 -0.000100 0.000583 -0.000046 0 -0.007701 0.218731 0.080131 0.000205 -0.000100 0.000583 -0.000046 1 ...

5 rows × 124 columns

Note that torso values are not set to 0. If we want to make torso the origin of coordinates, we need to substract torso from itself.


In [30]:
e03u01['torso'].head()


Out[30]:
attrib confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w
0 1 -0.512291 -0.190878 3.146024 0.089354 0.832964 0.133209 0.529567
1 1 -0.516791 -0.207289 3.142564 0.101255 0.835678 0.130613 0.523764
2 1 -0.512789 -0.228213 3.127369 0.089809 0.843881 0.143629 0.509088
3 1 -0.514544 -0.248091 3.110496 0.082341 0.857295 0.148766 0.485936
4 1 -0.508870 -0.264930 3.103923 0.069870 0.860776 0.160597 0.477903

5 rows × 8 columns


In [31]:
## Set torso values to 0 (the origing of coordinates)
e03u01.torso = e03u01.torso - e03u01.torso
e03u01.torso.head()


Out[31]:
attrib confidence pos_x pos_y pos_z orient_x orient_y orient_z orient_w
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0
3 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0

5 rows × 8 columns

Now, let's plot the skeletons


In [32]:
e03u01.columns = udl.index
xyz_03u01 = df_to_xyz(e03u01)

clouds = xyz_03u01.groupby('color')

means = e03u01.groupby('pose').mean()
means.insert(len(means.columns), 'pose', means.index )
# Prepare means to be printed:
m_groups = [to_xyz(means.ix[i]) for i,ind in enumerate(means.index)]

# Plot skeleton joints and links
ax = plt.axes(projection='3d')

for c, values in clouds:
    ax.scatter(values.x, values.y, values.z, color=c, alpha=0.2, marker='o')
    
for m in m_groups:
    col = m['color'][0] # Just need the 1st one
    plot_skeleton(ax, m, links=True, color=col)

ax.view_init(-90,90)
#plt.savefig('/Users/almudenasanz/Downloads/skeleton.pdf', format='pdf')

In [73]:


In [ ]: