pyomeca / ezc3d

Easy to use C3D reader/writer for C++, Python and Matlab
https://pyomeca.github.io/Documentation/ezc3d/index.html
MIT License
141 stars 44 forks source link

Writing a c3d file originally read by ezc3d fails. #262

Closed felixchenier closed 2 months ago

felixchenier commented 2 years ago

Salut Benjamin

Here is a c3d file that loads well using data = ezc3d.c3d(filename1), but then fails to write back to c3d using data.write(filename2).

I think there are several gotchas in this files, such as weird characters, and an uneven number of analog frames compared to point frames. It was saved by Vicon Nexus. I use this file as a unit test and demo for the Kinetics Toolkit since it includes all kinds of things such as points, analogs and events, and it's been recorded recently by recent instrumentation. If you can have a look, it would be super nice.

Merci

>>> import ezc3d
>>> data = ezc3d.c3d('walk.c3d')
>>> data.write('test.c3d')

Traceback (most recent call last):

  File "/Users/felix/miniconda3/envs/mosa/lib/python3.8/site-packages/ezc3d/__init__.py", line 598, in write
    new_param.set(ezc3d.VecString(old_param["value"]), dim)

  File "/Users/felix/miniconda3/envs/mosa/lib/python3.8/site-packages/ezc3d/ezc3d.py", line 998, in __init__
    _ezc3d.VecString_swiginit(self, _ezc3d.new_VecString(*args))

TypeError: Wrong number or type of arguments for overloaded function 'new_VecString'.
  Possible C/C++ prototypes are:
    std::vector< std::string >::vector()
    std::vector< std::string >::vector(std::vector< std::string > const &)
    std::vector< std::string >::vector(std::vector< std::string >::size_type)
    std::vector< std::string >::vector(std::vector< std::string >::size_type,std::vector< std::string >::value_type const &)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "/var/folders/7v/g5_ntzfx35v4ck07wflynw640000gp/T/ipykernel_55334/2930717686.py", line 1, in <cell line: 1>
    reader.write('test.c3d')

  File "/Users/felix/miniconda3/envs/mosa/lib/python3.8/site-packages/ezc3d/__init__.py", line 600, in write
    raise ValueError(f"Value in parameters {group}:{param} could not be converted to string")

ValueError: Value in parameters ANALOG:UNITS could not be converted to string

walk.c3d.zip

felixchenier commented 2 years ago

Oh, now I remember that this file has been processed using obscure scripts and may be simply invalid. Please forget this issue, I will check with other files.

Kris5GJ commented 3 months ago

Hello, I'm having a problem that I think may be related to this issue. I'm using windows 10, python 3.7.13, ezc3d 1.5.0. I'm learning how to use ezc3d because I need to transform some biomechanical data; specifically, I have to rotate markers and force vectors 180 degrees around the vertical axis. I managed to read the c3d, I got the force data from data::analogs and the corresponding columns, I did the dot product with the rotational matrix, and I directly replaced the columns with the new values. When I try to write the c3d file again, I get the following error: "ValueError: Value in parameters ANALOG:UNITS could not be converted to string". I pasted here the whole information about the error. I may be ignoring something that I have to do before the writing. Can somebody please give me a hint? I appreciate it!


TypeError Traceback (most recent call last) C:\ProgramData\Miniconda3\lib\site-packages\ezc3d__init__.py in write(self, path) 597 try: --> 598 new_param.set(ezc3d.VecString(old_param["value"]), dim) 599 except:

C:\ProgramData\Miniconda3\lib\site-packages\ezc3d\ezc3d.py in init(self, args) 997 def init(self, args): --> 998 _ezc3d.VecString_swiginit(self, _ezc3d.new_VecString(*args)) 999

TypeError: Wrong number or type of arguments for overloaded function 'new_VecString'. Possible C/C++ prototypes are: std::vector< std::string >::vector() std::vector< std::string >::vector(std::vector< std::string > const &) std::vector< std::string >::vector(std::vector< std::string >::size_type) std::vector< std::string >::vector(std::vector< std::string >::size_type,std::vector< std::string >::value_type const &)

During handling of the above exception, another exception occurred:

ValueError Traceback (most recent call last) ~\AppData\Local\Temp\ipykernel_5452\4034714422.py in 46 47 # Save the modified data back to a C3D file ---> 48 c.write(r'C:\Users...\Downloads\ESTATICO_py.c3d')

C:\ProgramData\Miniconda3\lib\site-packages\ezc3d__init__.py in write(self, path) 598 new_param.set(ezc3d.VecString(old_param["value"]), dim) 599 except: --> 600 raise ValueError(f"Value in parameters {group}:{param} could not be converted to string") 601 else: 602 raise NotImplementedError("Parameter type not implemented yet")

ValueError: Value in parameters ANALOG:UNITS could not be converted to string

pariterre commented 3 months ago

Dear @Kris5GJ Can you post the print of the ANALOG:UNITS parameter please? :)

Kris5GJ commented 3 months ago

Hello @pariterre thank you for the quick response, here I attached the screenshot of what I understood you asked for:

issue_ezc3d

The columns I'm modifiying are the last 12.

pariterre commented 3 months ago

If I recall well, there are issues with the UTF-8 code, can you try to replace the \uXXXX by anything else? If it works, I will need a c3d containing such values so I try to fix that!

Kris5GJ commented 3 months ago

Hello @pariterre, thank you. I replaced the string with the corresponding unit, in this case was supposed to be °/s since is a gyroscope signal from a delsys sensor. It worked. When I open the original c3d in mokka, in the analog channel the units are correctly shown as °/s. I changed the string with deg/s. Here is the original static file. ESTATICO.zip

Kris5GJ commented 3 months ago

I have a question, I need to rotate the position of the forces' vectors so they match the new position of the markers, how is the right way to modify the values in the platforms objects (we have 2 force plates). I mean that I need to modify the following values:

rotated_force_data = np.dot(rotation_matrix_z, pf_1['force']) pf_1['force']= rotated_force_data

rotated_moment_data = np.dot(rotation_matrix_z, pf_1['moment']) pf_1['moment'] = rotated_moment_data

rotated_cop_data = np.dot(rotation_matrix_z, pf_1['center_of_pressure']) pf_1['center_of_pressure'] = rotated_cop_data

rotated_tz_data = np.dot(rotation_matrix_z, pf_1['Tz']) pf_1['Tz'] = rotated_tz_data

But I get an error when I try to assing the new values to force, or moment, etc._

pariterre commented 2 months ago

Hi there! You cannot modify the platform objects directly as this is not part of the data stored in a C3D. At load time, if you requested the platform, ezc3d actually performs some computation to transform the analog data into actual platform data.

If you want to modify the data, you have to change the raw analog data themselves, meaning that you have to change the force platform data as you please and then performs the opposite of the computation done to transform raw analog data into force platform data to get back in order to get raw analog data. Then you can override the raw analog data in the c3d['data']['analog'] and write a new C3D.

The computation you have to reverse is this one https://github.com/pyomeca/ezc3d/blob/dev/src/modules/ForcePlatforms.cpp#L327 . Please note that this computation highly depends on the force platform type declared in the PLATFORM:TYPE parameter.

Kris5GJ commented 2 months ago

Hi @pariterre, I understand, thank you for the explanation.