After Ethan’s great work with Python scripting to allow the
.csv data to be turned in to animation streams and spline curves in Maya we faced the
next two new challenges;
Challenge 1 - Finding
True Time: Although the ‘ecs_CsvToMaya.py’ script allowed the data into Maya it
had no multiplication factor to make it match ‘true time’. In essence over two hours of data was being compressed in to fifty frames (two seconds).
Challenge 2– Different
lengths of Data: Each data file (.csv) ,Choir Heart Rate, Conductors Feet
Movement, etc had a different length (recording time) due to the complexities
of setting up devices whilst on location. The Cellist for example was recorded
for over two hours but the performance only lasts for one and half hours. This meant
that each data stream needed to be aligned and then ‘top and tailed’ to find
the concert performance.
Solution - The
solution for both problems relied upon finding an anchor point to match each
stream. Alongside each
data stream we also recorded sepereate audio/video files as reference which meant that we were able
to 'stack & achor' each on a timeline in Premiere. To do this we used the end of the performance
(last notes) to identify a consistant ‘time based clipping guide’. For example,
Aligned Audio /
Visual in Premiere
Choir
File Length: 2hrs 15m
27s - Concert Start Time at: 40m 12s - Concert End Time at: 2hrs 5mins 04s
Conductor
File Length: 2hrs 00m
08s - Concert Start Time at: 26m 24s - Concert End Time at: 1hrs 51mins 28s
Cellist
File Length: 2hrs 09m
29s - Concert Start Time at: 30m 19s - Concert End Time at: 1hrs 55mins 25s
ZDepth
File Length: 1hrs 25m
39s - Concert Start Time 7s - Concert End Time at: 1hrs 24mins 49s
Once we had this information we were able to
calculate that the multiplication factor to unlock true time (per file),
was a factor of ‘2.4’. Once this was added to the script we could be
sure that anything created in Maya would be a true visual representation of time
vs data. Finally, by using the ‘start/end time of the concert’ and Maya’s
timeline we were then able to set the time range to the concert only ensuring that each curve or animation generate was clipped to the correct length (‘ecs_CsvToMaya.py’uses the range of the timeline to calculate).
Cellist Arm Data
Stream Created Clipped in Maya
Success! - The next step, chopping the concert into sections…
As anyone whose following this blog knows, we have a collection of files containing data that was captured at a performance of Verdi's 'Requiem', using various sensors connected to several participants. The purpose of capturing this data was so that we could use it to create something in 3D that derives from that performance.
The files in question contain Comma Separated Values, simply known as .csv files. Since Autodesk Maya cannot read these files and no obvious solution (script/plug-in) could be found on the internet, it would seem like an impossible task to be able to make use of them in any meaningful way, but it's not 'game over' just yet!
My brief was to use the Python programming language to bridge the gap between the files and Maya. I would need to find a way to read the files, interpret the data and then find ways to represent that data in Maya.
In this post I will be outlining my progress on the script, covering my research and thinking behind it, and finishing with an overview of how it works and how to use it.
The CSV File
The first thing to understand about the .csv file type is that there are no strict rules on the format of the file; only that it is written in plain text (so it is humanly readable if opened in a text editor), that each line is a record containing fields separated by a delimiter (usually a comma), and that each line contains the same sequence of fields. While there are no requirements to label the fields, it is also common to use the first line to contain headings for each field. In our case all the .csv files we have use headings on the first line. Here is one of our examples:
What the Script Does
The script does several things:
The first function of the script is specifically for reading the data from any given file.
getCsvData("Path/to/a/file.csv")
This takes a file path, reads a file and it then returns a Python object containing the data in a format that can easily be used by other Python functions. It does this by reading the file line by line, using the commas to split each line of text into their individual values.
It assumes the first line contains the headings, so it will use that information to create a Python Dictionary Object, using each heading to contain a list for its values. As the script continues to read each line, the values are appended to the corresponding list in the dictionary. It is this dictionary object that is then returned by this function. Here is an example of what a Python dictionary looks like:
Each dictionary entry is a key:value pair. 'timestamp' being the key to access a Python List (the square brackets). The list would look something like this:
['0.031250', '0.062500', '0.093750', '0.125000']
Once a file has been read by this function and its output stored in a variable, it is possible to use the data within Python without needing to re-read the file.
As said previously, .csv files can contain any kind of data, some of which is not necessarily going to be useful to Maya. While the above function is designed to read any .csv file into Python, what we do next with this data is going to be specific for each file.
The next few functions written for the script are written specifically for the files that we are working with.
I shall use the heart rate file as an example of how I use the data.
This function creates an empty group node in Maya with the following animated attributes.
speed pace heartRate averageSpeed averagePace averageHeartRate latitude longitude distance
This .csv data contains a "Workout Time (secs)" column. The function accesses the values in that column, (in this case, the current time in seconds), and for each one, the corresponding attribute values are accessed and a key frame is set at that time, and that value.
The script also uses a helper function to convert the time from seconds to frames, taking the scene frame rate into account automatically. Here is what the extracted animation data looks like:
Writing a function to read the csv data object is fairy straight forward as shown in this Python code snippet.
def printCsvColumn(csvData, columnName)
for i in range(len(csvData[columnName])):
print csvData[columnName][i]
This code takes a csvData object and the name of a column, and prints out the values of that column.
Basic Usage of the Script
For all team mates on project Requiem, here is a quick overview on how to use the script.
To install the script you need to put the .py file inside one of the 3 script folders in your local maya settings folder. As an example:
Windows: <drive>:\Documents and Settings\<username>\My Documents\maya\<Version>\scripts
Mac OS X: ~/Library/Preferences/Autodesk/maya/<version>/scripts
Linux: ~/maya/<version>/scripts
Once this is done, run Maya and open up the script editor. Then inside a Python tab, run the following:
First we need to 'import' the script's functions. We use the namespace of 'csv' so we don't have to type out the full name.
import ecs_CsvToMaya as csv
Next we run the function 'getCsvData' and parse in the file path to a csv file as a string. The data is returned and stored in the variable 'csvData':
csvData = csv.getCsvData("Path/to/a/file.csv")
Note the quotation marks around the string, and the 'csv.' before the function name. If we didn't use the namespace when importing the module, we would have to write it like this:
ecs_CsvToMaya.getCsvData("Path/to/a/file.csv")
Now that we have the data available in Python we can use the other functions in the script to represent that data as keyframes. As each file is different we need to make sure we use the right function for the right file.
For the heart rate file, we need to use this function:
csv.createCsvHeartRateData(csvData)
This will create an empty group with all the relevant data animated on some custom attributes.
However for the rest of the files we can use this function:
csv.createCsvSensorData(csvData)
This will create a locator with the translate X, Y, and Z attributes animated.
So the completed code to run in Maya should look something like this:
So for each file we need to read in the data, then use the correct function to get the data into Maya.
In this case the heart rate file is the only exception, and for the rest we can use the other function.
On last set of functions have also been included, that generate Nurbs Curves from the animation data.
To use these you can run either of the following commands in Python (replacing with correct node and attribute names):
You can use the Maya time slider to select a time range from which to generate the curve from.
create3DCurve() specifically works on the translate X, Y and Z attributes to generate a full 3D curve, while the create2DCurve() will generate a flat curve on any other attribute. Here is an example of what the 'pace' attribute generated from the heart rate file:
Taking the Script Further
Currently the script will not be made publicly available, as the script is very much geared towards this project only. However it is my intention to continue developing this script so that it can be used on other projects, and by other people. I'm thinking that the tool would need to be generalised so that it could be possible to analyse any csv file from within Maya and choose how to interpret the data, rather than needing to write specific functions on a per-file basis.
Perhaps it would be possible to provide a set of nodes that are designed to interpret the data in different ways, which could then be plugged into existing Maya nodes to animate objects, create effects or generate meshes on the fly etc; and of course, some kind of graphical user interface.
So there we have it! I hope that readers have found it interesting, and informative.