def test_angles_creation():
    dims = ("axis", "channel", "time")
    array = Angles()
    np.testing.assert_array_equal(x=array, y=xr.DataArray())
    assert array.dims == dims

    array = Angles(MARKERS_DATA.values, time=MARKERS_DATA.time)
    is_expected_array(array, **EXPECTED_VALUES[57])

    size = 10, 10, 100
    array = Angles.from_random_data(size=size)
    assert array.shape == size
    assert array.dims == dims

    with pytest.raises(ValueError):
        Angles(ANALOGS_DATA)
示例#2
0
def test_construct_rt():
    eye = Rototrans()
    np.testing.assert_equal(eye.time.size, 1)
    np.testing.assert_equal(eye.sel(time=0), np.eye(4))

    eye = Rototrans.from_euler_angles()
    np.testing.assert_equal(eye.time.size, 1)
    np.testing.assert_equal(eye.sel(time=0), np.eye(4))

    # Test the way to create a rt, but not when providing bot angles and sequence
    nb_frames = 10
    random_vector = Angles(np.random.rand(3, 1, nb_frames))

    # with angles
    rt_random_angles = Rototrans.from_euler_angles(angles=random_vector,
                                                   angle_sequence="xyz")
    np.testing.assert_equal(rt_random_angles.time.size, nb_frames)
    np.testing.assert_equal(rt_random_angles[:-1, -1:, :],
                            np.zeros((3, 1, nb_frames)))  # Translation is 0

    # with translation
    rt_random_translation = Rototrans.from_euler_angles(
        translations=random_vector)
    np.testing.assert_equal(rt_random_translation.time.size, nb_frames)
    np.testing.assert_equal(
        rt_random_translation[:3, :3, :],
        np.repeat(np.eye(3)[:, :, np.newaxis], nb_frames, axis=2),
    )  # rotation is eye3
    np.arange(0, rt_random_angles.time.size / 0.5, 1 / 0.5)

    rt_with_time = Rototrans(
        rt_random_angles,
        time=np.arange(0, rt_random_angles.time.size / 100, 1 / 100),
    )
    assert rt_with_time.time[-1] == 0.09

    with pytest.raises(IndexError):
        Rototrans(data=np.zeros(1))

    with pytest.raises(ValueError):
        Rototrans(data=np.zeros((4, 4, 1)))

    with pytest.raises(ValueError):
        Rototrans(data=np.ones((4, 4, 1)))

    with pytest.raises(IndexError):
        Rototrans.from_euler_angles(
            angles=random_vector[..., :5],
            translations=random_vector,
            angle_sequence="x",
        )

    with pytest.raises(IndexError):
        Rototrans.from_euler_angles(angles=random_vector, angle_sequence="x")

    with pytest.raises(ValueError):
        Rototrans.from_euler_angles(angles=random_vector, angle_sequence="nop")
def test_average_rt():
    # TODO: investigate why this does not work
    # angles = Angles.from_random_data(size=(3, 1, 100))
    # or
    # angles = Angles(np.arange(300).reshape((3, 1, 100)))
    angles = Angles(np.random.rand(3, 1, 100))
    seq = "xyz"

    rt = Rototrans.from_euler_angles(angles, seq)
    rt_mean = Rototrans.from_averaged_rototrans(rt)
    angles_mean = Angles.from_rototrans(rt_mean, seq).isel(time=0)

    angles_mean_ref = Angles.from_rototrans(rt, seq).mean(dim="time")

    np.testing.assert_array_almost_equal(angles_mean,
                                         angles_mean_ref,
                                         decimal=2)
def test_rototrans_creation():
    dims = ("row", "col", "time")
    array = Rototrans()
    np.testing.assert_array_equal(x=array,
                                  y=xr.DataArray(np.eye(4)[..., np.newaxis]))
    assert array.dims == dims

    array = Rototrans(MARKERS_DATA.values, time=MARKERS_DATA.time)
    is_expected_array(array, **EXPECTED_VALUES[67])

    size = 4, 4, 100
    array = Rototrans.from_random_data(size=size)
    assert array.shape == size
    assert array.dims == dims

    with pytest.raises(ValueError):
        Angles(ANALOGS_DATA)
示例#5
0
def rototrans_from_averaged_rototrans(
    caller: Callable, rt: xr.DataArray
) -> xr.DataArray:
    # arbitrary angle sequence
    seq = "xyz"

    target = rt.mean(dim="time").expand_dims("time", axis=-1)

    angles = Angles(np.ndarray((3, 1, 1)))

    def objective_function(x):
        angles[:3, 0, 0] = x
        rt = caller.from_euler_angles(angles, seq)
        return np.ravel(rt.meca.rotation - target.meca.rotation)

    initia_guess = Angles.from_rototrans(target, seq).squeeze()
    angles[:3, 0, 0] = least_squares(objective_function, initia_guess).x
    return caller.from_euler_angles(angles, seq, translations=target.meca.translation)
def test_rototrans_creation():
    dims = ("row", "col", "time")
    array = Rototrans()
    np.testing.assert_array_equal(x=array,
                                  y=xr.DataArray(np.eye(4)[..., np.newaxis]))
    assert array.dims == dims

    data = Markers(MARKERS_DATA.values)
    array = Rototrans.from_markers(
        origin=data.isel(channel=[0]),
        axis_1=data.isel(channel=[0, 1]),
        axis_2=data.isel(channel=[0, 2]),
        axes_name="xy",
        axis_to_recalculate="y",
    )
    is_expected_array(array, **EXPECTED_VALUES[67])

    size = 4, 4, 100
    array = Rototrans.from_random_data(size=size)
    assert array.shape == size
    assert array.dims == dims

    with pytest.raises(ValueError):
        Angles(ANALOGS_DATA)
示例#7
0
def rototrans_from_euler_angles(
    caller: Callable,
    angles: Optional[xr.DataArray] = None,
    angle_sequence: Optional[str] = None,
    translations: Optional[xr.DataArray] = None,
):
    if angles is None:
        angles = Angles()

    if translations is None:
        translations = Angles()

    if angle_sequence is None:
        angle_sequence = ""

    # Convert special zyzz angle sequence to zyz
    if angle_sequence == "zyzz":
        angles[2, :, :] -= angles[0, :, :]
        angle_sequence = "zyz"

    # If the user asked for a pure rotation
    if angles.time.size != 0 and translations.time.size == 0:
        translations = Angles(np.zeros((3, 1, angles.time.size)))

    # If the user asked for a pure translation
    if angles.time.size == 0 and translations.time.size != 0:
        angles = Angles(np.zeros((0, 1, translations.time.size)))

    # Sanity checks
    if angles.time.size != translations.time.size:
        raise IndexError(
            "Angles and translations must have the same number of frames. "
            f"You have translation = {translations.shape} and angles = {angles.shape}"
        )
    if angles.axis.size != len(angle_sequence):
        raise IndexError(
            "Angles and angles_sequence must be the same size. "
            f"You have angles axis = {angles.axis.size} and angle_sequence length = {len(angle_sequence)}"
        )
    if angles.time.size == 0:
        return caller()

    empty_rt = np.repeat(np.eye(4)[..., np.newaxis], repeats=angles.time.size, axis=2)
    rt = empty_rt.copy()
    for i in range(angles.axis.size):
        a = angles[i, ...]
        matrix_to_prod = empty_rt.copy()
        if angle_sequence[i] == "x":
            # [[1, 0     ,  0     ],
            #  [0, cos(a), -sin(a)],
            #  [0, sin(a),  cos(a)]]
            matrix_to_prod[1, 1, :] = np.cos(a)
            matrix_to_prod[1, 2, :] = -np.sin(a)
            matrix_to_prod[2, 1, :] = np.sin(a)
            matrix_to_prod[2, 2, :] = np.cos(a)
        elif angle_sequence[i] == "y":
            # [[ cos(a), 0, sin(a)],
            #  [ 0     , 1, 0     ],
            #  [-sin(a), 0, cos(a)]]
            matrix_to_prod[0, 0, :] = np.cos(a)
            matrix_to_prod[0, 2, :] = np.sin(a)
            matrix_to_prod[2, 0, :] = -np.sin(a)
            matrix_to_prod[2, 2, :] = np.cos(a)
        elif angle_sequence[i] == "z":
            # [[cos(a), -sin(a), 0],
            #  [sin(a),  cos(a), 0],
            #  [0     ,  0     , 1]]
            matrix_to_prod[0, 0, :] = np.cos(a)
            matrix_to_prod[0, 1, :] = -np.sin(a)
            matrix_to_prod[1, 0, :] = np.sin(a)
            matrix_to_prod[1, 1, :] = np.cos(a)
        else:
            raise ValueError(
                "angle_sequence must be a permutation of axes (e.g. 'xyz', 'yzx', ...)"
            )
        rt = np.einsum("ijk,jlk->ilk", rt, matrix_to_prod)
    # Put the translations
    rt[:-1, -1:, :] = translations[:3, ...]
    return caller(rt)
from itertools import permutations

import numpy as np
import pytest

from pyomeca import Angles, Rototrans, Markers

SEQ = (["".join(p) for i in range(1, 4)
        for p in permutations("xyz", i)] + ["zyzz"] + ["zxz"])
SEQ = [s for s in SEQ if s not in ["yxz"]]
EPSILON = 1e-12
ANGLES = Angles(np.random.rand(4, 1, 100))


@pytest.mark.parametrize("seq", SEQ)
def test_euler2rot_rot2euleur(seq, angles=ANGLES, epsilon=EPSILON):
    if seq == "zyzz":
        angles_to_test = angles[:3, ...]
    else:
        angles_to_test = angles[:len(seq), ...]
    r = Rototrans.from_euler_angles(angles=angles_to_test, angle_sequence=seq)
    a = Angles.from_rototrans(rt=r, angle_sequence=seq)

    np.testing.assert_array_less((a - angles_to_test).meca.abs().sum(),
                                 epsilon)


def test_construct_rt():
    eye = Rototrans()
    np.testing.assert_equal(eye.time.size, 1)
    np.testing.assert_equal(eye.sel(time=0), np.eye(4))
示例#9
0
                        rt_length=100)
vtkModelMid = VtkModel(vtkWindow,
                       markers_color=(0, 0, 1),
                       markers_size=10.0,
                       markers_opacity=0.5,
                       rt_length=100)
vtkModelFromC3d = VtkModel(vtkWindow,
                           markers_color=(0, 1, 0),
                           markers_size=10.0,
                           markers_opacity=0.5,
                           rt_length=100)

# Create some RotoTrans attached to the first model
all_rt_real = []
all_rt_real.append(
    Rototrans.from_euler_angles(angles=Angles(np.zeros((3, 1, 1))),
                                angle_sequence="yxz",
                                translations=d[:, [0], [0]]))
all_rt_real.append(
    Rototrans.from_euler_angles(angles=Angles(np.zeros((3, 1, 1))),
                                angle_sequence="yxz",
                                translations=d[:, [0], [0]]))

# Create some Rototrans attached to the second model
one_rt = Rototrans.from_markers(
    origin=d[:, [3, 5], :].mean("channel", keepdims=True),
    axis_1=d[:, [4, 3], :],
    axis_2=d[:, [4, 5], :],
    axes_name="zx",
    axis_to_recalculate="z",
)