How to incorporate FixedParameters in manual calculation of affine linear transformation

Hi,

I just noticed that sometime between release 5.6.2 and 5.8.1 some changes were made how the linear transform text files were saved. In particular, now FixedParameters have values different from 0. I used this original snip from the official documentation to obtain an affine matrix (Transforms — 3D Slicer documentation)

def read_itk_affine_transform(filename):
    with open(filename) as f:
        tfm_file_lines = f.readlines()
    # parse the transform parameters
    match = re.match("Transform: AffineTransform_[a-z]+_([0-9]+)_([0-9]+)", tfm_file_lines[2])
    if not match or match.group(1) != '3' or match.group(2) != '3':
        raise ValueError(f"{filename} is not an ITK 3D affine transform file")
    p = np.array( tfm_file_lines[3].split()[1:], dtype=np.float64 )
    # assemble 4x4 matrix from ITK transform parameters
    itk_transform = np.array([
        [p[0], p[1], p[2], p[9]],
        [p[3], p[4], p[5], p[10]],
        [p[6], p[7], p[8], p[11]],
        [0, 0, 0, 1]])
    return itk_transform

you can see FixedParameters were not considered in building the affine matrix. But now I saw the exported linear transformation in 5.8.1 have values in FixedParameters different from 0, and I can’t find an example how to incorporate it in the calculation of the affine matrix. For example, I have this transformation file:

#Insight Transform File V1.0
#Transform 0
Transform: AffineTransform_double_3_3
Parameters: -0.9673983954721169 0.005242446734459156 -0.25320517607311555 -0.01635075985776062 0.9964070799946877 0.08309984108611503 0.25273107662108657 0.08453074995940893 -0.9638368924362907 19.64370704805952 -10.678323249250239 188.5045069754522
FixedParameters: -1.1877449844514136 5.3974515452055405 -63.54885348036633

that should produce this affine matrix as shown in the GUI (after doing the LPS->RAS conversion):

-0.967398 -0.0163508 -0.252731 15 
0.00524245 0.996407 -0.0845307 0 
0.253205 -0.0830998 -0.963837 62 
0 0 0 1 

Can someone point me how to do the calculations of the afine matrix (using generic Python/Numpy code) when FixedParameters have values different to 0?

Thanks for any help,

Sam

I figured out, after some search , the use of FixedParameters is about a recentered rotation, the code to obtain the correct affine transformation is as follows

def read_itk_affine_transform(filename):
    with open(filename, 'r') as f:
        lines = f.readlines()

    # Extract Parameters
    params_line = next(line for line in lines if line.startswith('Parameters:'))
    params = list(map(float, params_line.replace('Parameters:', '').strip().split()))
    matrix_values = params[:9]
    translation = np.array(params[9:12])

    # Extract FixedParameters (center of rotation)
    fixed_line = next(line for line in lines if line.startswith('FixedParameters:'))
    fixed_params = np.array(list(map(float, fixed_line.replace('FixedParameters:', '').strip().split())))

    # Reshape matrix and compute adjusted translation
    M = np.array(matrix_values).reshape((3, 3))
    C = fixed_params
    T = translation
    
    adjusted_translation =M @ (-C) + C + T

    # Construct 4x4 matrix
    transform = np.eye(4)
    transform[:3, :3] = M
    transform[:3, 3] = adjusted_translation

    return transform

then when using the recipe in the official documentation to convert to RAS convention (Transforms — 3D Slicer documentation), and you obtain back the affine matrix as shown in the GUI. It would be good if the official documentation could shown the modified code, hoping I didn’t miss something else.

Thanks for reporting back. Maybe you can do a pull request to the documentation to add this to the script repository?

Excellent, thank you!

I’ve touched up the code a bit and submitted a PR to update the documentation:

1 Like