Reading C3D files

8.1. Reading C3D files#

Let’s first download a sample C3D file. This file is provided by https://www.c3d.org as a test suite for C3D software development:

import kineticstoolkit.lab as ktk

filename = ktk.doc.download("c3d_test_suite_sample.c3d")

We read it using ktk.read_c3d:

c3d_contents = ktk.read_c3d(filename)

c3d_contents
UserWarning [/Users/felix/Documents/git/kineticstoolkit_doc/src/kineticstoolkit/files.py:519] In the specified file, points are expressed in mm. They were automatically converted to meters by scaling them by 0.001. Please note that if this file also contains calculated values such as angles, powers, etc., they were also (wrongly) scaled by 0.001. Consult https://kineticstoolkit.uqam.ca/doc/api/ktk.read_c3d.html for more information. You can mute this warning by explicitely setting `convert_point_unit` to either True or False.
{
     'Points': TimeSeries with attributes: time: <array of shape (450,)> dat...
    'Analogs': TimeSeries with attributes: time: array([0. , 0.005, 0.01 , ....
}

Caution

As indicated in the warning above, the points in this file are expressed in millimeters; however Kinetics Toolkit uses SI units and therefore meters by default. The point positions have been converted to meters by default, and therefore you can ignore this warning, or suppress it all the way using:

c3d_contents = ktk.read_c3d(filename, convert_point_unit=True)

You can generally ignore this warning if you read unprocessed marker positions and analog signals.

You should not ignore this warning if you are reading a c3d file that has been processed by some software or pipeline to calculate joint kinetics, powers, etc., and if these values are expressed as “points” in the c3d. Consult the help of ktk.read_c3d for more details.

The content of the file is expressed as a dictionary with two keys:

  • Points: A TimeSeries that contains the point data (markers)

  • Analogs (only when applicable): A TimeSeries that contains the raw analog data from force platforms, EMG or other analog signals recorded into the c3d file.

8.1.1. Points#

Each data key of the Points TimeSeries corresponds to one point (marker):

c3d_contents["Points"].data
{
    'RFT1': <array of shape (450, 4)>
    'RFT2': <array of shape (450, 4)>
    'RFT3': <array of shape (450, 4)>
    'LFT1': <array of shape (450, 4)>
    'LFT2': <array of shape (450, 4)>
    'LFT3': <array of shape (450, 4)>
    'RSK1': <array of shape (450, 4)>
    'RSK2': <array of shape (450, 4)>
    'RSK3': <array of shape (450, 4)>
    'RSK4': <array of shape (450, 4)>
    'LSK1': <array of shape (450, 4)>
    'LSK2': <array of shape (450, 4)>
    'LSK3': <array of shape (450, 4)>
    'LSK4': <array of shape (450, 4)>
    'RTH1': <array of shape (450, 4)>
    'RTH2': <array of shape (450, 4)>
    'RTH3': <array of shape (450, 4)>
    'RTH4': <array of shape (450, 4)>
    'LTH1': <array of shape (450, 4)>
    'LTH2': <array of shape (450, 4)>
    'LTH3': <array of shape (450, 4)>
    'LTH4': <array of shape (450, 4)>
     'PV1': <array of shape (450, 4)>
     'PV2': <array of shape (450, 4)>
     'PV3': <array of shape (450, 4)>
     'pv4': <array of shape (450, 4)>
}

We will learn how to visualize these markers using an interactive player in Visualizing 3D points and frames. For now, let’s plot some of them:

c3d_contents["Points"].plot(["LFT1", "RFT1"])
_images/46911b14a350c35fcf79a24ff618c276350323cdf7649a274e1be0872bad0bbe.png

8.1.2. Analogs#

Each data key of the Analogs TimeSeries corresponds to one analog signal:

c3d_contents["Analogs"].data
{
     'FX1': <array of shape (1800,)>
     'FY1': array([-0., -0., -0., ..., -0., -0., -0.])
     'FZ1': <array of shape (1800,)>
     'MX1': <array of shape (1800,)>
     'MY1': <array of shape (1800,)>
     'MZ1': <array of shape (1800,)>
     'CH7': array([-12. , -7. , -6. , ..., -7. , -10. , -8.5])
     'CH8': array([ -3. , -2. , -6.5, ..., -29. , -31. , -31. ])
     'FX2': <array of shape (1800,)>
     'FY2': array([-0. , -0. , -0. , ..., -0. , -0. , -0.442])
     'FZ2': <array of shape (1800,)>
     'MX2': <array of shape (1800,)>
     'MY2': <array of shape (1800,)>
     'MZ2': <array of shape (1800,)>
    'CH15': array([-69.5, -31.5, -15. , ..., 0. , -2.5, -1. ])
    'CH16': array([-110.5, -73. , -53.5, ..., -21. , -24. , -24. ])
}

Let’s plot some of them:

c3d_contents["Analogs"].plot(["FZ1", "FZ2"])
_images/b3d1e10fd4431f5282672afc149550fed27de53d7e88229104b33e1ef73f7bc1.png