Skip to content

Commit

Permalink
Merge pull request #317 from pariterre/dev
Browse files Browse the repository at this point in the history
Fixed computation of number of frames of Rotations in Python
  • Loading branch information
pariterre authored Mar 12, 2024
2 parents 39c4e7c + 00bff6d commit 87c1062
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 39 deletions.
41 changes: 30 additions & 11 deletions binding/matlab/ezc3dRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}

// Preparer the first layer of the output structure
const char *globalFieldsNames[] = {"header", "parameters","data"};
const char *globalFieldsNames[] = {"header", "parameters", "data"};
int headerIdx = 0;
int parametersIdx = 1;
int dataIdx = 2;
Expand All @@ -70,7 +70,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])

// Fill the header
{
const char *headerFieldsNames[] = {"points", "analogs", "events"};
const char *headerFieldsNames[] = {"points", "analogs", "rotations", "events"};
mwSize headerFieldsDims[2] = {1, 1};
mxArray * headerStruct = mxCreateStructArray(
2, headerFieldsDims,
Expand All @@ -79,8 +79,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
mxSetFieldByNumber(plhs[0], 0, headerIdx, headerStruct);
// fill points
{
const char *pointsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *pointsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize pointFieldsDims[2] = {1, 1};
mxArray * pointsStruct = mxCreateStructArray(
2, pointFieldsDims,
Expand All @@ -97,8 +97,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}
// fill analogs
{
const char *analogsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *analogsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize analogsFieldsDims[2] = {1, 1};
mxArray * analogsStruct = mxCreateStructArray(
2, analogsFieldsDims,
Expand All @@ -118,20 +118,39 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
c3d->header().nbAnalogByFrame()
* (c3d->header().lastFrame()+1));
}
// fill rotations
{
const char *rotationsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize rotationsFieldsDims[2] = {1, 1};
mxArray * rotationsStruct = mxCreateStructArray(
2, rotationsFieldsDims,
sizeof(rotationsFieldsNames) / sizeof(*rotationsFieldsNames),
rotationsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, rotationsStruct);

ezc3d::DataNS::RotationNS::Info rotationsInfo(*c3d);
fillMatlabField(rotationsStruct, 0, rotationsInfo.used());
fillMatlabField(rotationsStruct, 1,
static_cast<mxDouble>(rotationsInfo.ratio() * c3d->header().frameRate()));
fillMatlabField(rotationsStruct, 2,
rotationsInfo.ratio() * c3d->header().firstFrame()+1);
fillMatlabField(rotationsStruct, 3,
rotationsInfo.ratio() * (c3d->header().lastFrame()+1));
}

// fill events
{
const char *eventsFieldsNames[] = {"size", "eventsTime",
"eventsLabel"};
const char *eventsFieldsNames[] = {
"size", "eventsTime", "eventsLabel"};
mwSize eventsFieldsDims[2] = {1, 1};
mxArray * eventsStruct = mxCreateStructArray(
2, eventsFieldsDims,
sizeof(eventsFieldsNames) / sizeof(*eventsFieldsNames),
eventsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, eventsStruct);
mxSetFieldByNumber(headerStruct, 0, 3, eventsStruct);

fillMatlabField(eventsStruct, 0, static_cast<int>(
c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 0, static_cast<int>(c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 1, c3d->header().eventsTime());
fillMatlabField(eventsStruct, 2, c3d->header().eventsLabel());
}
Expand Down
41 changes: 30 additions & 11 deletions binding/octave/ezc3dRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}

// Preparer the first layer of the output structure
const char *globalFieldsNames[] = {"header", "parameters","data"};
const char *globalFieldsNames[] = {"header", "parameters", "data"};
int headerIdx = 0;
int parametersIdx = 1;
int dataIdx = 2;
Expand All @@ -70,7 +70,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])

// Fill the header
{
const char *headerFieldsNames[] = {"points", "analogs", "events"};
const char *headerFieldsNames[] = {"points", "analogs", "rotations", "events"};
mwSize headerFieldsDims[2] = {1, 1};
mxArray * headerStruct = mxCreateStructArray(
2, headerFieldsDims,
Expand All @@ -79,8 +79,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
mxSetFieldByNumber(plhs[0], 0, headerIdx, headerStruct);
// fill points
{
const char *pointsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *pointsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize pointFieldsDims[2] = {1, 1};
mxArray * pointsStruct = mxCreateStructArray(
2, pointFieldsDims,
Expand All @@ -97,8 +97,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}
// fill analogs
{
const char *analogsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *analogsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize analogsFieldsDims[2] = {1, 1};
mxArray * analogsStruct = mxCreateStructArray(
2, analogsFieldsDims,
Expand All @@ -118,20 +118,39 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
c3d->header().nbAnalogByFrame()
* (c3d->header().lastFrame()+1));
}
// fill rotations
{
const char *rotationsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize rotationsFieldsDims[2] = {1, 1};
mxArray * rotationsStruct = mxCreateStructArray(
2, rotationsFieldsDims,
sizeof(rotationsFieldsNames) / sizeof(*rotationsFieldsNames),
rotationsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, rotationsStruct);

ezc3d::DataNS::RotationNS::Info rotationsInfo(*c3d);
fillMatlabField(rotationsStruct, 0, rotationsInfo.used());
fillMatlabField(rotationsStruct, 1,
static_cast<mxDouble>(rotationsInfo.ratio() * c3d->header().frameRate()));
fillMatlabField(rotationsStruct, 2,
rotationsInfo.ratio() * c3d->header().firstFrame()+1);
fillMatlabField(rotationsStruct, 3,
rotationsInfo.ratio() * (c3d->header().lastFrame()+1));
}

// fill events
{
const char *eventsFieldsNames[] = {"size", "eventsTime",
"eventsLabel"};
const char *eventsFieldsNames[] = {
"size", "eventsTime", "eventsLabel"};
mwSize eventsFieldsDims[2] = {1, 1};
mxArray * eventsStruct = mxCreateStructArray(
2, eventsFieldsDims,
sizeof(eventsFieldsNames) / sizeof(*eventsFieldsNames),
eventsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, eventsStruct);
mxSetFieldByNumber(headerStruct, 0, 3, eventsStruct);

fillMatlabField(eventsStruct, 0, static_cast<int>(
c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 0, static_cast<int>(c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 1, c3d->header().eventsTime());
fillMatlabField(eventsStruct, 2, c3d->header().eventsLabel());
}
Expand Down
66 changes: 52 additions & 14 deletions binding/python3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
from ._version import __version__


# This is a dummy class that is used as an interface for the group of the parameters
class _GroupParameter:
def __init__(self, data):
self.__dict__ = data


class C3dMapper(Mapping):
def __init__(self, *args, **kw):
self._storage = dict(*args, **kw)
Expand Down Expand Up @@ -132,8 +138,20 @@ def __init__(self, path="", extract_forceplat_data=False, ignore_bad_formatting=
self.extract_forceplat_data = extract_forceplat_data
self._storage["header"] = c3d.Header(self.c3d_swig.header(), rotations_info)
self._storage["parameters"] = c3d.Parameter(self.c3d_swig.parameters())
self._storage["data"] = c3d.Data(self.c3d_swig, rotations_info, self.extract_forceplat_data)
self._storage["data"] = c3d.Data(self.c3d_swig, self.extract_forceplat_data)
return

@property
def header(self):
return self._storage["header"]

@property
def parameters(self):
return self._storage["parameters"]

@property
def data(self):
return self._storage["data"]

def __deepcopy__(self, memodict=None):
if memodict is None:
Expand All @@ -145,7 +163,7 @@ def __deepcopy__(self, memodict=None):

new._storage["header"] = c3d.Header(new.c3d_swig.header(), rotations_info)
new._storage["parameters"] = c3d.Parameter(new.c3d_swig.parameters())
new._storage["data"] = c3d.Data(new.c3d_swig, rotations_info, new.extract_forceplat_data)
new._storage["data"] = c3d.Data(new.c3d_swig, new.extract_forceplat_data)

# Update the structure with a copy of all data
for header_key in self["header"]:
Expand Down Expand Up @@ -177,14 +195,12 @@ def __init__(self, swig_header, rotation_info):
"first_frame": self.header.nbAnalogByFrame() * self.header.firstFrame(),
"last_frame": self.header.nbAnalogByFrame() * (self.header.lastFrame() + 1) - 1,
}
if rotation_info.hasGroup():
rotation_frame_rate = self.header.frameRate() * rotation_info.ratio()
self._storage["rotations"] = {
"size": rotation_info.used(),
"frame_rate": rotation_frame_rate,
"first_frame": rotation_frame_rate * self.header.firstFrame(),
"last_frame": rotation_frame_rate * (self.header.lastFrame() + 1) - 1,
}
self._storage["rotations"] = {
"size": rotation_info.used(),
"frame_rate": self.header.frameRate() * rotation_info.ratio(),
"first_frame": rotation_info.ratio() * self.header.firstFrame(),
"last_frame": rotation_info.ratio() * (self.header.lastFrame() + 1) - 1,
}
self._storage["events"] = {
"size": len(self.header.eventsTime()),
"events_time": self.header.eventsTime(),
Expand All @@ -205,8 +221,15 @@ def __init__(self, swig_param):
self.create_group_if_needed(group_name)
self._storage[group_name]["__METADATA__"]["DESCRIPTION"] = group.description()
self._storage[group_name]["__METADATA__"]["IS_LOCKED"] = group.isLocked()

# Add easy accessor to the group
setattr(self, group_name, _GroupParameter(self._storage[group_name]))

for parameter in group.parameters():
self.add_parameter(group_name, parameter)

# There is no need to add an easy accessor to the parameter as it is implicit by the fact that it is added to the KEYS

return

def create_group_if_needed(self, group_name):
Expand Down Expand Up @@ -275,7 +298,7 @@ def __init__(self, swig_pf):
self._storage["Tz"][:, i] = Tz[i].to_array()[:, 0]

class Data(C3dMutableMapper):
def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
def __init__(self, swig_c3d, extract_forceplat_data):
super().__init__()

# Interface to swig pointers
Expand All @@ -288,8 +311,7 @@ def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
}
self._storage["analogs"] = swig_c3d.get_analogs()

if rotations_info.hasGroup():
self._storage["rotations"] = swig_c3d.get_rotations()
self._storage["rotations"] = swig_c3d.get_rotations()

# Add the platform filer if required
if extract_forceplat_data:
Expand All @@ -298,6 +320,22 @@ def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
all_pf.append(c3d.PlatForm(pf))
self._storage["platform"] = all_pf
return

@property
def points(self):
return self._storage["points"]

@property
def meta_points(self):
return self._storage["meta_points"]

@property
def analogs(self):
return self._storage["analogs"]

@property
def rotations(self):
return self._storage["rotations"]

def add_parameter(
self,
Expand Down Expand Up @@ -329,7 +367,7 @@ def add_parameter(

def add_event(
self,
time: (list, tuple),
time: list | tuple,
context: str = "",
label: str = "",
description: str = "",
Expand Down
2 changes: 1 addition & 1 deletion src/RotationsInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ezc3d::DataNS::RotationNS::Info::Info(
_hasGroup(false),
_dataStart(-1),
_used(0),
_ratio(1)
_ratio(0)
{
if (!c3d.parameters().isGroup("ROTATION")){
return;
Expand Down
27 changes: 25 additions & 2 deletions test/python3/test_binder_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,32 @@ def test_parse_and_rebuild_header(c3d_build_rebuild_all):


def test_parse_and_rebuild_parameters(c3d_build_rebuild_reduced):
# UNITS2 is not in the original file (Label2), but is required. Therefore, the parameters won't match
orig, rebuilt = c3d_build_rebuild_reduced
assert orig["parameters"] == rebuilt["parameters"]
for group_key in orig.parameters._storage:
for param_key in orig.parameters[group_key]:
if not isinstance(orig.parameters[group_key][param_key], dict):
# Only test the values that are actual parameters
continue
if "type" not in orig.parameters[group_key][param_key]:
# Only test the values that are actual parameters
continue

if param_key == "DATA_START":
# Skip DATA_START as it is an internal value
continue

try:
assert orig.parameters[group_key][param_key]['type'] == rebuilt.parameters[group_key][param_key]['type']
except:
# Type may differ for empty values
if not orig.parameters[group_key][param_key]['value'] and not rebuilt.parameters[group_key][param_key]['value']:
pass
else:
assert orig.parameters[group_key][param_key]['type'] == rebuilt.parameters[group_key][param_key]['type']
assert orig.parameters[group_key][param_key]['description'] == rebuilt.parameters[group_key][param_key]['description']
assert orig.parameters[group_key][param_key]['is_locked'] == rebuilt.parameters[group_key][param_key]['is_locked']
assert np.all(orig.parameters[group_key][param_key]['value'] == rebuilt.parameters[group_key][param_key]['value'])



def test_parse_and_rebuild_data(c3d_build_rebuild_all):
Expand Down

0 comments on commit 87c1062

Please sign in to comment.