Git Product home page Git Product logo

py-c3d's Introduction

py-c3d

This is a small library for reading and writing C3D binary files. C3D files are a standard format for recording 3-dimensional time sequence data, especially data recorded by a 3D motion tracking apparatus.

Installing

Install with pip:

pip install c3d

Or if you'd like to use the bleeding-edge version, just clone the github repository and build and install using the normal Python setup process:

pip install git+https://github.com/EmbodiedCognition/py-c3d

Usage

Tools

This package includes a script for converting C3D motion data to CSV format (c3d2csv) and an OpenGL-based visualization tool for observing the motion described by a C3D file (c3d-viewer).

Note for the viewer you need to install pyglet. This can be done by installing the gui extra of py-c3d:

pip install "c3d[gui]"

Library

To use the C3D library, just import the package and create a Reader or Writer depending on your intended usage

import c3d

with open('data.c3d', 'rb') as handle:
    reader = c3d.Reader(handle)
    for i, (points, analog) in enumerate(reader.read_frames()):
        print('Frame {}: {}'.format(i, points.round(2)))

You can also get and set metadata fields using the library; see the package documentation for more details.

Developer Install

To work on c3d, first install poetry and then run:

git clone https://github.com/EmbodiedCognition/py-c3d
cd py-c3d
poetry install

This will create a new virtual environment with all the required dependency and c3d in develop mode.

Tests

To run tests available in the test folder, following command can be run from the root of the package directory:

python -m unittest discover .

Test scripts will automatically download test files from c3d.org.

Caveats

This library is minimally effective, in the sense that the only motion tracking system I have access to (for testing) is a Phasespace system. If you try out the library and find that it doesn't work with your motion tracking system, let me know. Pull requests are also welcome!

Also, if you're looking for more functionality than just reading and writing C3D files, there are a lot of better toolkits out there that support a lot more file formats and provide more functionality, perhaps at the cost of increased complexity. The biomechanical toolkit is a good package for analyzing motion data.

py-c3d's People

Contributors

akuederle avatar boyle avatar claudiohoffmann avatar extraymond avatar felixchenier avatar friggog avatar lmjohns3 avatar mattiasfredriksson avatar rvdijk avatar yochaytz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

py-c3d's Issues

write function fails at writer._frames[0]

The write function fails when calling 'self._frames[0] complaining that Writer has no attribute frame. Here are some details

Minimal example of the problem:

`import numpy as np
import c3d

inputFileName = 'P02_FL_Con_2FP_noGapFill.c3d'

reader = c3d.Reader(open(inputFileName, 'rb'))
writer = c3d.Writer( reader.point_rate,
reader.analog_rate,
reader.point_scale,
'mm',
1.0)

writer.add_frames(reader.read_frames())

with open('output.c3d','wb') as h:
writer.write(h,reader.point_labels)
h.close()`

Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
main, mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/mjhmilla/.vscode/extensions/ms-python.python-2021.5.842923320/pythonFiles/lib/python/debugpy/main.py", line 45, in
cli.main()
File "/home/mjhmilla/.vscode/extensions/ms-python.python-2021.5.842923320/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 444, in main
run()
File "/home/mjhmilla/.vscode/extensions/ms-python.python-2021.5.842923320/pythonFiles/lib/python/debugpy/../debugpy/server/cli.py", line 285, in run_file
runpy.run_path(target_as_str, run_name=compat.force_str("main"))
File "/usr/lib/python3.6/runpy.py", line 263, in run_path
pkg_name=pkg_name, script_name=fname)
File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/data/projects/AutomatedMocapGapFill/Gap_Fill_Python_code/minimalWorkingExample.py", line 15, in
points,analog=writer._frame[0]
AttributeError: 'Writer' object has no attribute '_frame'

Update to modern Python packaging structure

To simplify developing and maintaing the package, I would suggest to switch to a more modern package structure/tool chain.

My suggestioin would be to:

  • switch to poetry as dependency manager and build tool
  • add some github workflows to auto publish
  • Switch to pytest instead of nosetest

@friggog @lmjohns3 any opinions on that?

"TRIAL" not present in all c3d files

Running the example code:

>>> r = c3d.Reader(open('capture.c3d', 'rb'))
>>> for frame_no, points, analog in r.read_frames():
...     print('{0.shape} points in this frame'.format(points))

Can yield the following error if "TRIAL" is not present in the C3D file:

/Users/XXX/Python/c3d-0.2.1/c3d.py in __getitem__(self, group)
    531         if '.' in group:
    532             group, param = group.split('.', 1)
--> 533         group = super(Manager, self).__getitem__(group)
    534         if param:
    535             return group[param]

KeyError: 'TRIAL'

AttributeError: 'str' object has no attribute 'decode'

The read method in the Param class contains the following:

class Param(object):
    ...
    def read(self, handle):
        ...
        self.bytes = ''
        if self.total_bytes:
            self.bytes = handle.read(self.total_bytes)
        ...

If self.total_bytes is 0, then self.bytes is of type str not bytes.

A subsequent call to Param.string_value will raise an AttributeError:

File "lib/python3.6/site-packages/c3d.py", line 500, in get_string return self.params[key.upper()].string_value
File "lib/python3.6/site-packages/c3d.py", line 323, in string_value return self.bytes.decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'

AttributeError: module 'climate' has no attribute 'add_arg'

HI there I am trying to run scripts/cd3-viewer on my Macbook Pro and get the following output error:

2019-10-31 14:54:46.867 Python[61527:6696186] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to (null)
Traceback (most recent call last):
  File "./c3d-viewer", line 14, in <module>
    climate.add_arg('inputs', nargs='+', metavar='FILE', help='show these c3d files')
AttributeError: module 'climate' has no attribute 'add_arg'

Any idea what this could be?

Upload to newer version (climate specificly)

Hi! I've been enjoying this library for some time, it would be nice to update this module to a newer version where argparse replaced climate.

Since climate is not available for pypy, functionalities in scripts/ folder is unable to be launched.

Installation is broken due to renamed c3d2csv script

At the moment it is not possible to install that package, because the script scripts/c3d2csv does not exist. The file is now named c3d2csv.py.

Either the .py extension must be removed again or the setup.py file should be adjusted.

force plate data not aligned

Hi,

I tried to parse a c3d file from Kistler force plate and get the Fz1 data from it. In V3D, Fz1 is the 3rd channel (starting from 1) and has 5 sub channels. But when parsing with py-c3d, the 5 sub channels are analog[0, 2], [3, 0], [5, 3], [8, 1], [10, 4]. I had to reshape analog to (5, -1), so that I can get the 5 sub channels of Fz1 by analog[:, 2].

Is it a bug or did I do something wrong? The c3d file attached. The 13th channel is for the sync with EMG by the way.

Thanks.
Kaiyuan
FP_CMJ_6.zip

Not clear how to write a file

The documentation shows the following usage example for the write functionality:

import c3d
import numpy as np


writer = c3d.Writer()
for _ in range(100):
    writer.add_frame(np.random.randn(30, 5))
with open('random-points.c3d', 'wb') as h:
    writer.write(h)

This has two issues. First, it should be add_frames but not add_frame. That's easy to fix. But the second issue is hard to fix, since write.write expects a second argument, labels, and it is not clear what it should be

TypeError: write() missing 1 required positional argument: 'labels'

Problem writing C3D file

Hi,

I'm trying to read, process, and write C3D files with py-c3d (0.3.0). The input C3D files are generated with the (old) Vicon Workstation software and I'm using Sypder (Python 3.6.3) on a Win 64-bit machine.

Your reading example works just fine.
However, the writing examples of both the stable version as well as the read/process/write example of the latest version result in the following error

Traceback (most recent call last):

  File "<ipython-input-27-45556bdd732f>", line 9, in <module>
    writer.write(h)

  File "C:\ProgramData\Anaconda3\lib\site-packages\c3d.py", line 1046, in write
    points, analog = self._frames[0]

ValueError: too many values to unpack (expected 2)

Do you have any idea why? Thanks in advance!

Get marker coordinates using marker name

Hi,

I've installed your library and I'm able to read my c3d files from Vicon using your example script, however I would like to know if it is possible to get the coordinates of a specific marker based on its name, namely 'marker1'.

I appreciate any help.
Thank you very much
Iroupa

c3d2csv with headers

Hi, how to use thec3d2csvscript to convert a .c3d file to .csv file with headers? Currently, when I use the script, all the header information are missing in the.csvfile.

Thanks

latest commit breaks c3d2csv scripts

commit 0aee81 will raise an error "SyntaxError: Generator expression must be parenthesized if not sole argument" in c3d.py line 1059, rollback one version make this issue fixed (did not check whether adding an extra parenthesis could fix)

Can't read_frames

Hi, I have a problem reading frames on win 64 machine with latest version. It worked on Linux though.

This is the error. Any clue? Could be the c3d file or numpy version maybe? Thanks in advance

for i, (_, points, analog) in enumerate(C.read_frames()):

  File "build\bdist.win-amd64\egg\c3d.py", line 869, in read_frames
    count=n).reshape((self.point_used, 4))

ValueError: string is smaller than requested size

reading Xsens file

I have a c3d file exported from MVNX studio of xsens. When I try to read the file using your library. I get the following error:

`

import c3d
f = open('test.c3d','rb')
data = c3d.Reader(f)
Traceback (most recent call last):
File "", line 1, in
File "/is/ps2/mhassan/.virtualenvs/frankengeist/local/lib/python2.7/site-packages/c3d.py", line 796, in init
self.check_metadata()
File "/is/ps2/mhassan/.virtualenvs/frankengeist/local/lib/python2.7/site-packages/c3d.py", line 534, in check_metadata
assert self.header.frame_rate == self.point_rate, (
File "/is/ps2/mhassan/.virtualenvs/frankengeist/local/lib/python2.7/site-packages/c3d.py", line 674, in point_rate
return self.get_float('POINT:RATE')
File "/is/ps2/mhassan/.virtualenvs/frankengeist/local/lib/python2.7/site-packages/c3d.py", line 657, in get_float
return self.get(key).float_value
AttributeError: 'NoneType' object has no attribute 'float_value'
`

Creating a new c3d file with labels

the current test file shows how to write a c3d with labels yet the method seems to have changed. So the question is how to write a c3d file with point_labels?

ValueError: we only read Intel C3D files

I read some c3d files successfully, but some files when I tried to read can't be read. The error is
"ValueError: we only read Intel C3D files ", how can I solve it? thx
I had a screenshot here
default

latest commit breaks write function

In c3d.py line 1070f something ugly happened, so I get a syntax error when trying to write the file.
Also, it would be great to have the custom label argument being optional.
Please update the PyPi package.

Fails to read files with screen axis metadata

When the c3d file contains screen axis metadata the file import fails with the following error:

Traceback (most recent call last):
  File "D:\Git\Blender\addons\io_anim_c3d\__init__.py", line 254, in execute
    msg = c3d_importer.load(self, context, filepath=path, **keywords)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Git\Blender\addons\io_anim_c3d\c3d_importer.py", line 97, in load
    global_orient, parsed_screen_param = parser.axis_interpretation([0, 0, 1], [0, 1, 0])
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\Git\Blender\addons\io_anim_c3d\c3d_parse_dictionary.py", line 189, in axis_interpretation
    O_data[:, 0] = axis_x
    ~~~~~~^^^^^^
ValueError: could not convert string to float: '+Z'

The axis_interpretation method expects get_screen_axis to return vectors but get_screen_axis and set_screen_axis read and write the axis in string representation ("+Z", "-Y", etc.)

I am writing an exporter and used set_screen_axis to write the axis data to the file.
When reimporting the file I encountered this error.

Transpose bug with analog channels

I think there is a little bug when reading the analog channels.
Here you use:

raw = np.fromstring(self._handle.read(n * analog_bytes),  dtype=analog_dtype, count=n).reshape((self.analog_used, -1))

This will result in the analog channels being in the wrong order, because you reshape the wrong way around.

Let me show you what I mean with a little example:

Let's say we have 20 analog channels, which are sampled at 5 times the frequency of the other points.
Than the information for 1 frame would look like that:

np.array(list(range(20))*5)

>>> array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13,
       14, 15, 16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
       11, 12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,  4,  5,  6,  7,
        8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,  1,  2,  3,  4,
        5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])

Now we want to extract the 5 values belonging to each channel:

With the current implementation, that happens:

np.array(list(range(20))*5).reshape((20,-1))

>>> array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

But what we actually want is:

np.array(list(range(20))*5).reshape((-1,20)).T

>>>array([[ 0,  0,  0,  0,  0],
       [ 1,  1,  1,  1,  1],
       [ 2,  2,  2,  2,  2],
       [ 3,  3,  3,  3,  3],
       [ 4,  4,  4,  4,  4],
       [ 5,  5,  5,  5,  5],
       [ 6,  6,  6,  6,  6],
       [ 7,  7,  7,  7,  7],
       [ 8,  8,  8,  8,  8],
       [ 9,  9,  9,  9,  9],
       [10, 10, 10, 10, 10],
       [11, 11, 11, 11, 11],
       [12, 12, 12, 12, 12],
       [13, 13, 13, 13, 13],
       [14, 14, 14, 14, 14],
       [15, 15, 15, 15, 15],
       [16, 16, 16, 16, 16],
       [17, 17, 17, 17, 17],
       [18, 18, 18, 18, 18],
       [19, 19, 19, 19, 19]])

Correct me, if I am mistaken, but i think that a little bug in the module. If you agree I will prepare a tiny pull request to fix it :)

Best regards

Parsing valid c3d file fails

p2s1.zip
While trying to parse this file I get
KeyError: 'PROCESSING'
It seems that the group named PROCESSISNG appears twice in the header. Is the invalid according to the c3d format?

Reader functions outside of loop but not inside

Reader functions perfectly well within simple function code block with paths of .c3d files as input. However, when ITERATING through list of paths of .c3d file locations, and passing those paths to the reader fails and states that: "We only read Intel 3D files (got processor 85)". The loop that the reader is failing with (but not outside of) is part of a h5py dataset creation block.

I am really struggling to find a solution. Any general insight / suggestions?

Update the PyPi Version

The pypi version is out of date and still has the analog transpose bug (#7). It would be great to have the latest on there (or at least that bug fixed)

Install error

Hitting this on install of latest version:

Pip subprocess error:
ERROR: Exception:
Traceback (most recent call last):
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2857, in get_entry_map
    ep_map = self._ep_map
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2815, in __getattr__
    raise AttributeError(attr)
AttributeError: _ep_map

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/cli/base_command.py", line 228, in _main
    status = self.run(options, args)
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/cli/req_command.py", line 182, in wrapper
    return func(self, options, args)
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/commands/install.py", line 406, in run
    pycompile=options.compile,
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/req/__init__.py", line 90, in install_given_reqs
    pycompile=pycompile,
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/req/req_install.py", line 822, in install
    requested=self.user_supplied,
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/operations/install/wheel.py", line 860, in install_wheel
    requested=requested,
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/operations/install/wheel.py", line 662, in _install_wheel
    console, gui = get_entrypoints(distribution)
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_internal/operations/install/wheel.py", line 156, in get_entrypoints
    console = distribution.get_entry_map('console_scripts')
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2860, in get_entry_map
    self._get_metadata('entry_points.txt'), self
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2535, in parse_map
    maps[group] = cls.parse_group(group, lines, dist)
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2513, in parse_group
    ep = cls.parse(line, dist)
  File "/azureml-envs/azureml_05625119c63857e91b504d23cd268824/lib/python3.7/site-packages/pip/_vendor/pkg_resources/__init__.py", line 2491, in parse
    raise ValueError(msg, src)
ValueError: ("EntryPoint must be in 'name=module:attrs [extras]' format", 'c3d-metatdata=scripts.c3d_metatdata:main()')

Does not work under python >=3.9

It would be nice to update the pip version of the package :)

As it is, reader.read_frames() does not work under python >=3.9, which makes the package a tad useless.

In the pip version, _as_array uses the method fromstring which has been removed from python 3.9.
It's been replaced by frombuffer in the github version, but you can't access it from pip (which makes it impossible to install from a requirement.txt file)

Thanks for the good work!

KeyError: 'ANALOG'

I get KeyError: 'ANALOG' (line 622) when trying to use a c3d file generated from Optitrack Motion after downloading the zip file and running the set.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.