CS838 Project 2
1. General Description
The goal of the project is to build a tool for visualizing and
manipulating motion capture data. Screenshots of the tool are
shown in the following figures.
|
|
Tree view showing the joint
hierarchy
|
GL view showing the skeleton and the
bounding volume
|
The tool provides the user with two views of the data. The first
view uses the motion data to draw and animate a simple
skeleton; this gives an immediate and intuitive
impression of the motion. The second view provides a more
detailed presentation of the motion: the skeleton is visualized
as an hierarchy of joints, and the user can examine the rotation
and translation channels of any joint by selecting it. In this
manner, the user is able to examine the motion in two different
levels of semantic "zoom". The interface also provides controls to replay the motion
interactively, to control the camera of the graphical view, and
to set interpolators for any range of frames.
In general, the implemented functionality of the tool
can be broken down in the following six points ('*' indicates a bonus feature):
In the sections that follow, we first present the general implementation
details of the tool, and proceed to discuss in more detail each
of the previous points.
2. Implementation
2.1 Overview
The tool is implemented in Java (jdk 1.1.7). Apart from the Java
runtime, the following packages are required:
- Swing
: This package is used for the interface of the tool.
- Vecmath (from Java3d):
This is used for the different internal representations of
rotations.
- Magician: This package
provides Java bindings for OpenGL.
The implementation of the tool is based heavily on the
Model-View-Controller (MVC) design pattern.
Motion capture data represent the "model", which is
visualized by a number of different "views". In our case two
views are provided: the animated graphical view, and the more
detailed joint hierarchy view. The visual controls for time and
interpolators represent the "controller" that alter the state of
the model, and therefore the state of the visualizations.
Figure 1. UML class diagram
Figure 1 shows a simplified class diagram of the tool. The
"model" is implemented in terms of the AnimationModel
class. This class controls the time of the model and acts as a
mediator for the following classes:
- Joint. This class is used to build
up the representation of the joint
hierarchy. AnimationModel holds a reference to the
root joint.
- DataManager. This class is responsible for
providing the motion data to the views. This includes both the
original frame data and the data produced by interpolation.
- DriverFactory. This class abstracts the different
internal representations for rotations. DataManager
uses the factory to instantiate a default driver that stores the
original motion data and then multiple drivers that are used
for interpolation.
Currently, three factories are implemented:
- EulerAnglesDriverFactory uses Euler angles to
represent rotations. The factory provides independent linear
interpolation on the three axes and supports a special kind of
interpolator that implements a 3-way median cut filter. This
non-linear filter can be used on noisy motion data in
order to dampen the high frequency components.
- QuartenionDriverFactory uses unit quartenions to
represent rotations and supports SLERP interpolation.
- MapDriverFactory uses the exponential map to
represents rotations and implements standard linear interpolation
between parameterized quartenions
The "view" of this model is abstracted by the
ModelView class. This class is realized in two
subclasses:
- TreeModelView. This class provides a tree
visualization of the joint hierarchy, and allows the user to
examine the rotation and translation channels of any joint.
- GLModelView. This class uses OpenGL to draw and
animate a 3D model of the skeleton.
Given the model, GLModelView positions its camera coordinates with
Camera and contains a mapping from joints and joint pairs (segments) to
Shape for drawing the skeleton. Currently, several of each are implemented.
For Camera the broad distinction is manual or automatic.
The automatic Camera uses one of several Director strategies
that implement the decision making process.
The "glue" between the model and the view is provided by
ViewFrame. This class represents an association between
an instance of AnimationModel and an instance of
ModelView. This association is one to many, which means
that different views can share the same model. This class also
provides the visual controls that are common among all views.
At this point we conclude our short overview of the
implementation. In the following sections, we will focus on the
different functions of the tool and we will provide more
implementation details as needed. In addition, the interested
reader can find more information in the generated javadoc documentation of the source
code.
The tool reads in motion capture data in the Biovision format
(.bvh files). The parser for the data was implemented using the
JavaCC parser
generator. The tool makes no assumptions on the format of the
input and can read arbitrary skeleton hierarchies. The only
assumption is that translation channels will be named with
Translate{X,Y,Z} and rotation channels with
Rotate{X,Y,Z}. The tool honors the order of the
rotation channels for each joint and the rotation matrix is
calculated based on the specified order.
As we mentioned in section 2.1, the tool currently supports two
views of the motion data. The first view uses OpenGL to draw and
animate a 3d skeleton on screen, while the second view presents
visualizes the joint hierarchy and the raw motion data.
The GLModelView does the work of setting up the scene and rendering the
model into the scene. For rendering the model, the task is broken down into
mapping shapes onto the model and choosing where to position the eye and look at
of the camera. Mapping shapes is done by the class ShapeManager. It maps
joints and joint pairs(segments) to a set of shapes. Drawing some shapes but not
all, as in the case of Tracers, is accomplished by having several ShapeManagers.
To make shape drawing more generic, all model data whether its explicit as in the
case of joints or implicit as in the case of segments is converted to a
ShapeAdapter that defines a bounding volume. The task of Shape
is then to fill the ShapeAdapter volume.
The camera eye and look at points are determined by Camera. Two
primary types of implementation are:
- ManualCamera: user specified positions.
- AutoCamera: a Director specifies the positions.
Due to the subjective nature of cinematography, it is difficult to say which
automated solution will prove to be the best if at all possible. Thus we provide a
framework for exploring what makes a good camera motion and ultimately the most
interesting story. The heart of the framework is the Director that
is responsible for providing camera positioning for a given frame. Every director
knows something about a given frame of an animation and makes a decision based on
this information. This knowledge is partitioned relative to the current frame
in terms of prior and future knowledge. Specific to each director is what information
they collect for their knowledge and how it effects the final decision. Directors
for local and global are implemented. Local uses only the current frame's model information
to calculate a fixed offset from a given joint. Global uses all frames to pick interesting
camera positions and interpolated (linear) between them. Interesting is defined as breaking
a bounding volume that serves as a threshold. Currently, only local has been plugged in.
2.3.2 TreeView
The second view presents the user with a tree visualization of
the joint hierarchy. The user can select any joint or end
effector in the hierarchy and view the values of the translation
and rotation channels.
The visualization of the rotation channels uses a text-based
view that shows the actual data, and an OpenGL view that
depicts an axis-angle representation. In this manner, the user
can browse the raw motion data with the text-view, and at the
same time get a more intuitive representation of the rotation
through the OpenGL view. The latter shows the axis as a vector
on the unit sphere and animates the orientation of the axis
based on the rotation data.
It is important to note here that both main views share the same
animation model. Apart from the obvious save in resource
consumption, this also means that the two views are completely
in sync: whatever change is applied to the model, it will appear
in both views. Therefore, they will always display the same
frame and the same interpolators will apply in both of them.
The user can control the replay of the motion using the "video"
buttons that each window contains. The tool supports replay of the
motion in normal and fast speed, and also in both directions
(backwards and forward). There is also a looping option, which
wraps the frame number around when the end or start of frames is
reached. Apart from the automated replay of frames, the user can also move manually
to the next or previous frame, or directly set the value of the text field that
shows the current frame. As we noted earlier, since the two main
views share the same animation model, the replay of frames can
be controlled from either of them and they will always be in sync.
Internally, the model is traversed in a depth first manner to produce the
expected matrix stack manipulations.
The tool supports three different internal representations of
rotations: Euler angles, unit quartenions and parameterized
quartenions (exponential maps).
2.5.1 Euler Angles
In this representation, the rotation of the joint is represented
with three rotation angles around the axes of the local coordinate
system. The order that the rotations are applied is dictated by
the order that the rotation channels appear in the input
data. We make no assumption of a "global" rotation order, but
instead store the rotation order on a per joint basis.
2.5.2 Unit Quartenions
We have used the javax.vecmath
package for the implementation of unit quartenions. The package
provides all the necessary code to create a unit quartenion and
convert it to a rotation matrix. The quartenion for a specific
joint is constructed by multiplying the individual quartenions that
represent the rotation around the Cartesian axes. Again, the order
of multiplication is dictated by the order that the rotation
channels appear in the input file.
We have implemented exponential maps on top of unit
quartenions. More specifically, we first construct the unit
quartenion that represents a specific rotation, and then use the
logarithmic map to obtain the parameterized form. This is
necessary, since there is no obvious manner in which we can
compose the rotations of two parameterized quartenions.
We have implemented interpolators as a special case of rotation
drivers. An interpolator is initialized with a range of frames and
a rotation driver. It then uses the rotation driver to retrieve
the rotations of the joints for the starting and ending frame, and
calculates the rotations for the in-between frames. Interpolators
need not use only the frame driver in order to access the rotation
data; instead, the tool allows interpolators to be stacked in an
hierarchy, where one interpolator is using the previous one as the
rotation driver. This feature allows the creation of virtual
"streams" of drivers, and can be used to combine filters and
interpolators on the same motion. In addition, there may be an
arbitrary number of such interpolator intervals defined over the total
set of frames.
The interface
allows the user to select a range of frames and the kind of
interpolation that will be used (if there is actually a
choice). The current version of the tool supports the following
interpolators: linear interpolation for Euler angles, median
filter for Euler angles, SLERP interpolation for quartenions and
linear interpolation for exponential maps.
2.6.1 Euler Angles Interpolation
The Euler interpolator uses simple independent linear interpolation
on the three angles to calculate the rotation of a joint. This of
course does not always provide a smooth transition between the
starting and ending frame, since the interpolation scheme does not
take in account the interplay between the three axes.
We have also used the interpolator framework to implement a
median cut filter, suitable for smoothing out the high frequency
components of the motion. This filter
acts much like an interpolator, but calculates the value for
each rotation channel by examining the values in the previous
and next frames.
Since the framework allows interpolators to be stacked, one can
imagine layering the median cut filter on top of the original
motion data, and then applying linear interpolation on top of
that; as a result, interpolation will be applied after the
filter has smoothed out the high frequency "noisy"
components. In this manner, users "mix & match" these simple
interpolators and filters until they arrive at the desired
visual result. Although we could potentially include the
filtering mechanism in the interpolator, we believe that the
direct manipulation of the interpolation stack provides a more
intuitive and flexible interaction paradigm.
2.6.2 Quartenion Interpolation
The quartenion interpolator uses the javax.vecmath
package which already provides for SLERP interpolation. Overall,
quartenion interpolation has produced the best results compared
to interpolation for the other two representations.
2.6.3 Map Interpolation
The map interpolator implements linear interpolation between the
starting and ending parameterized quartenion. It uses
linear interpolation independently on each coordinate of the quartenion, and
produces aesthetically pleasing results, as longs as the two
quartenions are close in Euclidean space. We noticed, however, that
in some cases, the interpolation would produce spurious results
even though the start and end keys represented similar
rotations.
To illustrate this, imagine a starting rotation of -(pi-epsilon) around the
positive y-axis, and an ending rotation of (pi-epsilon) around
the positive y-axis. Although the two rotations are actually
2*epsilon radians apart, linear interpolation will travel
counter clockwise around the circle in order to move between the
keys.
We were able, however, to discover the source of the problem by
examining the axis-angle view of the rotation. Since
parameterized quartenions are constructed with the logarithmic
map, this causes all angles to lie in [0,2*pi]; therefore, the
negative rotation around the positive y-axis is transformed to a
positive rotation around the negative y-axis. This means that
interpolation has to work its way from a starting negative axis
to an ending positive axis, thus causing the trip around the circle.
In order to remedy this, we have implemented a second version of
the linear interpolator that aligns the start and end keys. More
specifically, the interpolator computes the inner product of the
keys, and if negative it reverses axis and angle of the start
key. This has the effect of bringing the two keys closer
together in Euclidean space, and decreases the chances of
producing spurious rotations.
Comparison
In this section, we present one characteristic example that
illustrates the operation of the different interpolation
schemes. More specifically, we use the Thief data set and focus on
the rotation of the hips joints. The starting rotation of the hips
is approximately -3*PI around the y axis, and the ending rotation
is close to 3*PI, again around the y axis (note the change of
sign). These two rotations are actually very close in rotation
space, and this is why we have chosen our example. In the
following figures, we show the hips rotation that each algorithm
calculates using screenshots of the axis-angle
representation. The screenshots refer to frames
100,110,120,130,140,150.
Euler Linear Interpolation
Linear interpolation in Euler space results in a trip in the
wrong direction. Not only that, but the rotation is not smooth:
it accelerates in the middle, and decelerates close to the
start and the end.
Quartenion SLERP Interpolation
SLERP interpolation provides a very smooth rotation, since it
masks out the change of sign.
Map Linear Interpolation
Plain linear interpolation in the exponential map gives poor
results, since the two rotations are far apart in Euclidean
space. We basically get the same results as the Euler angles
interpolation, with an increased acceleration in the middle.
Map Aligned Interpolation
Aligned map interpolation applies a simple heuristic to bring
the start and end rotation close in Euclidean space.
This simple heuristic produces
very good results in our case, since the initial rotation is
"mirrored" and the axis reversed. Essentially, we get results
that look similar to SLERP interpolation.
Marker export is implemented in the model-view framework that we
have used for the visual manipulation of motion data. More
specifically, we have implemented marker export as a special type
of view that simply outputs the marker positions in a file.
Running
- Get all resources and unpack
- Set your LD_LIBRARY_PATH to ${magician_home}/lib
- Edit the runtest script to reflect local JVM and class files.
- Running the motion capture: execute runtest
- Running marker export: execute Java ExportMarkers
Last modified: Tue Apr 18 13:46:37 XXX 2000