-
Notifications
You must be signed in to change notification settings - Fork 1
/
bvh.py
142 lines (119 loc) · 4.46 KB
/
bvh.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Edward Lee edl56@cornell.edu
# Classes:
# Node, Tree
# 2016-08-11
import pandas as pd
import numpy as np
def load(fname,includeDisplacement=False,removeBlank=True):
"""
Load data from BVD file. Euler angles are given as YXZ. Axis Neuron only keeps track of displacement for the hip.
Details about data files from Axis Neuron?
2016-08-11
Params:
-------
fname (str)
Name of file to load
includeDisplacement (bool=False)
If displacement data is included for everything including root.
removeBlank (bool=True)
Remove entries where nothing changes over the entire recording session. This should mean that there was nothing being recorded in that field.
Value:
------
df (dataFrame)
dt (float)
Frame rate.
"""
from itertools import chain
from pyparsing import nestedExpr
import string
# Parse skeleton.
# Find the line where data starts and get skeleton tree lines.
s = ''
lineix = 0
bodyParts = ['Hips'] # for keeping track of order of body parts
with open(fname) as f:
f.readline()
f.readline()
ln = f.readline()
lineix += 3
while not 'MOTION' in ln:
if 'JOINT' in ln:
bodyParts.append( ''.join(a for a in ln.lstrip(' ').split(' ')[1] if a.isalnum()) )
s += ln
ln = f.readline()
lineix += 1
# Read in the frame rate.
while 'Frame Time' not in ln:
ln = f.readline()
lineix += 1
dt = float( ln.split(' ')[-1] )
s = nestedExpr('{','}').parseString(s).asList()
nodes = []
def parse(parent,thisNode,skeleton):
"""
Keep track of traversed nodes in nodes list.
Params:
-------
parent (str)
skeleton (list)
As returned by pyparsing
"""
children = []
for i,ln in enumerate(skeleton):
if (not type(ln) is list) and 'JOINT' in ln:
children.append(skeleton[i+1])
elif type(ln) is list:
if len(children)>0:
parse(thisNode,children[-1],ln)
nodes.append( Node(thisNode,parents=[parent],children=children) )
# Parse skeleton.
parse('','Hips',s[0])
# Resort into order of motion data.
nodesNames = [n.name for n in nodes]
bodyPartsIx = [nodesNames.index(n) for n in bodyParts]
nodes = [nodes[i] for i in bodyPartsIx]
skeleton = Tree(nodes)
# Parse motion.
df = pd.read_csv(fname,skiprows=lineix+2,delimiter=' ',header=None)
df = df.iloc[:,:-1] # remove bad last col
if includeDisplacement:
df.columns = pd.MultiIndex.from_arrays([list(chain.from_iterable([[b]*6 for b in bodyParts])),
['xx','yy','zz','y','x','z']*len(bodyParts)])
else:
df.columns = pd.MultiIndex.from_arrays([[bodyParts[0]]*6 +
list(chain.from_iterable([[b]*3 for b in bodyParts[1:]])),
['xx','yy','zz']+['y','x','z']*len(bodyParts)])
# Filtering.
if removeBlank:
# Only keep entries that change at all.
df = df.iloc[:,np.diff(df,axis=0).sum(0)!=0]
# Units of radians and not degress.
df *= np.pi/180.
return df,dt,skeleton
class Node(object):
def __init__(self,name=None,parents=[],children=[]):
self.name = name
self.parents = parents
self.children = children
def add_child(self,child):
self.children.append(child)
class Tree(object):
def __init__(self,nodes):
self._nodes = nodes
self.nodes = [n.name for n in nodes]
names = [n.name for n in nodes]
if len(np.unique(names))<len(names):
raise Exception("Nodes have duplicate names.")
self.adjacency = np.zeros((len(nodes),len(nodes)))
for i,n in enumerate(nodes):
for c in n.children:
try:
self.adjacency[i,names.index(c)] = 1
# automatically insert missing nodes (these should all be dangling)
except ValueError:
self.adjacency = np.pad( self.adjacency, ((0,1),(0,1)), mode='constant', constant_values=0)
self._nodes.append( Node(c) )
names.append(c)
self.adjacency[i,names.index(c)] = 1
def print_tree(self):
print(self.adjacency)