Skip to content

ITK Euler3D rotation matrix not supported #295

@tomfrankkirk

Description

@tomfrankkirk

I've generated a rigid transform using ANTs AI. When I try to load the .mat file using nitransforms, it throws an "unsupported transform" error during the Matlab dict load function linked below.

affine = mdict.get(

Doing a bit of digging, my transform has 6 parameters vs the usual 12. When I load it using scipy.io.loadmat, the dict has two keys, "Euler3DTransform_double_3_3" and "fixed". A typical ANTs affine has "AffineTransform_double_3_3" and "fixed". I've given the details of each transform below.

According to chatgpt the Euler3D params are expressed as [rotx, roty, rotz, tx, ty, tz]. Its proposed the following code to convert the Euler3D into a full Affine transformation and it seems to work on my simple test cases.

I'd be happy to incorporate this into the existing from_matlab_dict function but am not expert enough in this area to fully test it or consider where it may break. Does anyone have any comment to make? Is there a reason why Euler3D isn't supported; does this solution look sensible?

Euler3D rigid transform:

{'Euler3DTransform_double_3_3': array([[ 1.34468616e-04],
        [ 1.03066786e-02],
        [ 5.49512615e-03],
        [-1.88564745e+00],
        [-1.93042799e+00],
        [ 4.81095387e+00]]),
 'fixed': array([[-3.4070487 ],
        [33.00756454],
        [-3.60144258],
        [ 0.        ]])}

Affine transform:

{'AffineTransform_double_3_3': array([[ 1.03404642],
        [-0.04273591],
        [ 0.01717928],
        [ 0.03782461],
        [ 0.99473991],
        [-0.01715351],
        [-0.00832061],
        [ 0.00896228],
        [ 0.92373895],
        [-3.23504401],
        [-1.28125352],
        [ 4.6121046 ]]),
 'fixed': array([[-3.4070487 ],
        [33.00756454],
        [-3.60144258]])}

Code to convert Euler3D into standard affine matrix

    # 1. Parse Euler angles and translation
    # input params: [angX, angY, angZ, tx, ty, tz]
    orig_params = data["Euler3DTransform_double_3_3"].flatten()
    angles = orig_params[:3]
    translation = orig_params[3:]

    # 2. Parse Fixed Parameters (Center of Rotation)
    # Euler3D has 4 fixed params; generic Affine has 3.
    orig_fixed = data["fixed"].flatten()
    center = orig_fixed[:3]

    # 3. Compute 3x3 Rotation Matrix
    # ITK Euler3D uses 'xyz' sequence
    rot_matrix = R.from_euler("xyz", angles).as_matrix()

    # 4. Flatten the matrix and append translation
    # Flattening must be row-major (m11, m12, m13...)
    new_params = np.concatenate([rot_matrix.flatten(), translation])

    # 5. Prepare data for .mat file
    # We use 'double' precision as ANTs expects this
    new_data = {
        "AffineTransform_double_3_3": new_params.reshape(-1, 1).astype(np.float64),
        "fixed": center.reshape(-1, 1).astype(np.float64),
    }

    # 6. Save
    savemat(output_mat, new_data)
    print(f"Successfully converted to generic affine: {output_mat}")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions