examples/model_creation/two_side_lower_limbs.py

Summary

Maintainability
A
1 hr
Test Coverage
import os
from pyomeca import Markers

from bionc import (
    AxisTemplate,
    AxisFunctionTemplate,
    BiomechanicalModelTemplate,
    MarkerTemplate,
    SegmentTemplate,
    NaturalSegmentTemplate,
    C3dData,
    BiomechanicalModel,
    JointType,
)
from tests.utils import TestUtils

# special load to test the script
# otherwise one could have use standard import
bionc = TestUtils.bionc_folder()
module = TestUtils.load_module(bionc + "/examples/model_creation/right_side_lower_limb.py")


def model_creation_from_measured_data(c3d_filename: str = "statref.c3d") -> BiomechanicalModel:
    """
    Create a model from a data file and we build the biomechanical model as a template using the marker names
    """

    # Fill the kinematic chain model
    model = BiomechanicalModelTemplate()
    # de_leva = DeLevaTable(total_mass=100, sex="female")

    right_hip_joint = lambda m, bio: module.harrington2007(m["RFWT"], m["LFWT"], m["RBWT"], m["LBWT"])[0]
    left_hip_joint = lambda m, bio: module.harrington2007(m["RFWT"], m["LFWT"], m["RBWT"], m["LBWT"])[1]
    right_knee_joint = lambda m, bio: MarkerTemplate.middle_of(m, bio, "RKNI", "RKNE")
    right_ankle_joint = lambda m, bio: MarkerTemplate.middle_of(m, bio, "RANE", "RANI")
    left_knee_joint = lambda m, bio: MarkerTemplate.middle_of(m, bio, "LKNI", "LKNE")
    left_ankle_joint = lambda m, bio: MarkerTemplate.middle_of(m, bio, "LANE", "LANI")

    model["PELVIS"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            u_axis=AxisTemplate(
                # from the middle of posterior illiac spine to the middle of anterior illiac spine
                start=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RBWT", "LBWT"),
                end=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RFWT", "LFWT"),
            ),
            # middle of the right and left posterior superior iliac spine
            # or sacroiliac joint
            proximal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RBWT", "LBWT"),
            # Hip joint center projected in the sagittal plane of the pelvis
            # middle of the right and left hip joint center
            distal_point=lambda m, bio: right_hip_joint(m, bio),
            # normal to the sagittal plane of the pelvis
            w_axis=AxisTemplate(start=left_hip_joint, end=right_hip_joint),
        )
    )

    model["PELVIS"].add_marker(MarkerTemplate(name="RFWT", parent_name="PELVIS", is_technical=True))
    model["PELVIS"].add_marker(MarkerTemplate(name="LFWT", parent_name="PELVIS", is_technical=True))
    model["PELVIS"].add_marker(MarkerTemplate(name="RBWT", parent_name="PELVIS", is_technical=True))
    model["PELVIS"].add_marker(MarkerTemplate(name="LBWT", parent_name="PELVIS", is_technical=True))
    model["PELVIS"].add_marker(
        MarkerTemplate(
            name="RIGHT_HIP_JOINT",
            function=right_hip_joint,
            parent_name="PELVIS",
            is_technical=False,
            is_anatomical=True,
        )
    )
    model["PELVIS"].add_marker(
        MarkerTemplate(
            name="LEFT_HIP_JOINT", function=left_hip_joint, parent_name="PELVIS", is_technical=False, is_anatomical=True
        )
    )

    model["RTHIGH"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            u_axis=AxisFunctionTemplate(
                function=lambda m, bio: MarkerTemplate.normal_to(m, bio, right_hip_joint(m, bio), "RKNE", "RKNI")
            ),
            proximal_point=right_hip_joint,
            # the knee joint computed from the medial femoral epicondyle and the lateral femoral epicondyle
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RKNE", "RKNI"),
            w_axis=AxisTemplate(start="RKNI", end="RKNE"),
        )
    )

    model["RTHIGH"].add_marker(
        MarkerTemplate(
            name="RIGHT_HIP_CENTER",
            function=right_hip_joint,
            parent_name="RTHIGH",
            is_technical=False,
            is_anatomical=True,
        )
    )
    model["RTHIGH"].add_marker(MarkerTemplate("RKNE", parent_name="RTHIGH", is_technical=True))
    model["RTHIGH"].add_marker(MarkerTemplate("RKNI", parent_name="RTHIGH", is_technical=True))
    model["RTHIGH"].add_marker(
        MarkerTemplate(
            "RIGHT_KNEE_JOINT", function=right_knee_joint, parent_name="RTHIGH", is_technical=False, is_anatomical=True
        )
    )

    model["LTHIGH"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            u_axis=AxisFunctionTemplate(
                function=lambda m, bio: MarkerTemplate.normal_to(m, bio, left_hip_joint(m, bio), "LKNE", "LKNI")
            ),
            proximal_point=left_hip_joint,
            # the knee joint computed from the medial femoral epicondyle and the lateral femoral epicondyle
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "LKNE", "LKNI"),
            w_axis=AxisTemplate(start="LKNE", end="LKNI"),
        )
    )

    model["LTHIGH"].add_marker(
        MarkerTemplate(
            name="LEFT_HIP_CENTER",
            function=left_hip_joint,
            parent_name="LTHIGH",
            is_technical=False,
            is_anatomical=True,
        )
    )
    model["LTHIGH"].add_marker(MarkerTemplate("LKNE", parent_name="LTHIGH", is_technical=True))
    model["LTHIGH"].add_marker(MarkerTemplate("LKNI", parent_name="LTHIGH", is_technical=True))
    model["LTHIGH"].add_marker(
        MarkerTemplate(
            "LEFT_KNEE_JOINT", function=left_knee_joint, parent_name="LTHIGH", is_technical=False, is_anatomical=True
        )
    )

    model["RSHANK"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            u_axis=AxisFunctionTemplate(
                function=lambda m, bio: MarkerTemplate.normal_to(m, bio, right_knee_joint(m, bio), "RANE", "RANI")
            ),
            proximal_point=right_knee_joint,
            # the knee joint computed from the medial femoral epicondyle and the lateral femoral epicondyle
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RANE", "RANI"),
            w_axis=AxisTemplate(start="RANI", end="RANE"),
        )
    )
    model["RSHANK"].add_marker(
        MarkerTemplate(
            "RIGHT_KNEE_JOINT", right_knee_joint, parent_name="RSHANK", is_technical=False, is_anatomical=True
        )
    )
    model["RSHANK"].add_marker(MarkerTemplate("RANE", parent_name="RSHANK", is_technical=True))
    model["RSHANK"].add_marker(MarkerTemplate("RANI", parent_name="RSHANK", is_technical=True))
    model["RSHANK"].add_marker(
        MarkerTemplate(
            "RIGHT_ANKLE_JOINT",
            function=right_ankle_joint,
            parent_name="RSHANK",
            is_technical=False,
            is_anatomical=True,
        )
    )

    model["LSHANK"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            u_axis=AxisFunctionTemplate(
                function=lambda m, bio: MarkerTemplate.normal_to(m, bio, left_knee_joint(m, bio), "LANE", "LANI")
            ),
            proximal_point=left_knee_joint,
            # the knee joint computed from the medial femoral epicondyle and the lateral femoral epicondyle
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "LANE", "LANI"),
            w_axis=AxisTemplate(start="LANE", end="LANI"),
        )
    )
    model["LSHANK"].add_marker(
        MarkerTemplate("LEFT_KNEE_JOINT", left_knee_joint, parent_name="LSHANK", is_technical=False, is_anatomical=True)
    )
    model["LSHANK"].add_marker(MarkerTemplate("LANE", parent_name="LSHANK", is_technical=True))
    model["LSHANK"].add_marker(MarkerTemplate("LANI", parent_name="LSHANK", is_technical=True))
    model["LSHANK"].add_marker(
        MarkerTemplate(
            "LEFT_ANKLE_JOINT", function=left_ankle_joint, parent_name="LSHANK", is_technical=False, is_anatomical=True
        )
    )

    model["RFOOT"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            # u_axis is defined from calcaneous (CAL) to the middle of M1 and M5
            u_axis=AxisTemplate(
                start="RHEE",
                end=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RTARI", "RTAR"),
            ),
            proximal_point=right_ankle_joint,
            #  middle of M1 and M5
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "RTARI", "RTAR"),
            w_axis=AxisTemplate(start="RTARI", end="RTAR"),
        )
    )

    model["RFOOT"].add_marker(MarkerTemplate("RHEE", parent_name="RFOOT", is_technical=True))
    model["RFOOT"].add_marker(MarkerTemplate("RTARI", parent_name="RFOOT", is_technical=True))
    model["RFOOT"].add_marker(MarkerTemplate("RTAR", parent_name="RFOOT", is_technical=True))
    model["RFOOT"].add_marker(
        MarkerTemplate(
            "RIGHT_ANKLE_JOINT", function=right_ankle_joint, parent_name="RFOOT", is_technical=False, is_anatomical=True
        )
    )

    model["LFOOT"] = SegmentTemplate(
        natural_segment=NaturalSegmentTemplate(
            # u_axis is defined from calcaneous (CAL) to the middle of M1 and M5
            u_axis=AxisTemplate(
                start="LHEE",
                end=lambda m, bio: MarkerTemplate.middle_of(m, bio, "LTARI", "LTAR"),
            ),
            proximal_point=left_ankle_joint,
            #  middle of M1 and M5
            distal_point=lambda m, bio: MarkerTemplate.middle_of(m, bio, "LTARI", "LTAR"),
            w_axis=AxisTemplate(start="LTAR", end="LTARI"),
        )
    )
    #
    model["LFOOT"].add_marker(MarkerTemplate("LHEE", parent_name="LFOOT", is_technical=True))
    model["LFOOT"].add_marker(MarkerTemplate("LTARI", parent_name="LFOOT", is_technical=True))
    model["LFOOT"].add_marker(MarkerTemplate("LTAR", parent_name="LFOOT", is_technical=True))
    model["LFOOT"].add_marker(
        MarkerTemplate(
            "LEFT_ANKLE_JOINT", function=left_ankle_joint, parent_name="LFOOT", is_technical=False, is_anatomical=True
        )
    )

    model.add_joint(
        name="right_hip",
        joint_type=JointType.SPHERICAL,
        parent="PELVIS",
        child="RTHIGH",
    )

    model.add_joint(
        name="left_hip",
        joint_type=JointType.SPHERICAL,
        parent="PELVIS",
        child="LTHIGH",
    )

    model.add_joint(
        name="right_knee",
        joint_type=JointType.SPHERICAL,
        parent="RTHIGH",
        child="RSHANK",
    )

    model.add_joint(
        name="left_knee",
        joint_type=JointType.SPHERICAL,
        parent="LTHIGH",
        child="LSHANK",
    )

    model.add_joint(
        name="right_ankle",
        joint_type=JointType.SPHERICAL,
        parent="RSHANK",
        child="RFOOT",
    )

    model.add_joint(
        name="left_ankle",
        joint_type=JointType.SPHERICAL,
        parent="LSHANK",
        child="LFOOT",
    )

    c3d_data = C3dData(f"{c3d_filename}")

    # Put the model together, print it and print it to a bioMod file
    natural_model = model.update(c3d_data)

    return natural_model


def main():
    # create a c3d file with data
    filename = module.generate_c3d_file(two_side=True)
    # Create the model from a c3d file and markers as template
    model = model_creation_from_measured_data(filename)

    # load experimental markers, usecols is used to select only the markers that are in the model
    # and it rearrange them in the same order as the model
    markers_xp = Markers.from_c3d(filename, usecols=model.marker_names_technical).to_numpy()

    # compute the natural coordinates
    Qxp = model.Q_from_markers(markers_xp[:, :, 0:2])

    from bionc import Viz

    # display the experimental markers in red and the model markers in green
    # almost superimposed because the model is well defined on the experimental data
    bionc_viz = Viz(model, show_center_of_mass=False, show_model_markers=True, show_frames=True)
    # bionc_viz.animate(Qxp, markers_xp=markers_xp)
    bionc_viz.animate(Qxp, markers_xp=markers_xp)

    # remove the c3d file
    os.remove(filename)

    # dump the model in a pickle format
    model.save("../models/lower_limbs.nc")


if __name__ == "__main__":
    main()