-
Notifications
You must be signed in to change notification settings - Fork 18
Description
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.
nitransforms/nitransforms/io/itk.py
Line 141 in 9486757
| 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}")