Thanks for your replies @zivy and @dzenanz. I’m exploring registration as a way to “line up” a head CT - they’re often acquired with a patient’s head tilted to the side, etc. I still want the lined-up version to be in the same physical space as the original acquisition so if viewed in PACS, the reference lines would still match up. When I register the original CT image to a template (the Rorden standard head CT) in sitk, it moves that CT image to the same physical space as the template. I want to move it back to the original physical space but keep the newly aligned pixels.
I tried applying the inverse and that didn’t seem to work. It’s entirely probable I’m doing that wrong. Probably helpful to include the code I’m using - a large part of it is directly from the sitk registration introduction so may look familiar. Really appreciate your help:
fixed_image = sitk.Cast(fixed_image, sitk.sitkFloat32)
moving_image = sitk.Cast(moving_image, sitk.sitkFloat32)
# Use the CenteredTransformInitializer to align the centers of the two volumes and set the center of rotation to the center of the fixed image
initial_transform = sitk.CenteredTransformInitializer(fixed_image,
moving_image,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY)
# ***** Registration
registration_method = sitk.ImageRegistrationMethod()
# Similarity metric settings.
registration_method.SetMetricAsMattesMutualInformation(numberOfHistogramBins=50)
registration_method.SetMetricSamplingStrategy(registration_method.RANDOM)
registration_method.SetMetricSamplingPercentage(0.01)
registration_method.SetInterpolator(sitk.sitkLinear)
# Optimizer settings.
registration_method.SetOptimizerAsGradientDescent(learningRate=1.0, numberOfIterations=100, convergenceMinimumValue=1e-6, convergenceWindowSize=10)
registration_method.SetOptimizerScalesFromPhysicalShift()
# Setup for the multi-resolution framework.
registration_method.SetShrinkFactorsPerLevel(shrinkFactors = [4,2,1])
registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas=[2,1,0])
registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()
# Don't optimize in-place, we would possibly like to run this cell multiple times.
registration_method.SetInitialTransform(initial_transform, inPlace=False)
final_transform = registration_method.Execute(sitk.Cast(fixed_image, sitk.sitkFloat32),
sitk.Cast(moving_image, sitk.sitkFloat32))
print('Final metric value: {0}'.format(registration_method.GetMetricValue()))
print('Optimizer\'s stopping condition, {0}'.format(registration_method.GetOptimizerStopConditionDescription()))
# Perform the registration
min_value = float(sitk.GetArrayFromImage(moving_image).min())
registered_image = sitk.Resample(moving_image, fixed_image, final_transform, sitk.sitkLinear, min_value, moving_image.GetPixelID())
# Adjust the registered image direction
width, height, depth = registered_image.GetSize()
center = registered_image.TransformIndexToPhysicalPoint((int(np.ceil(width/2)),
int(np.ceil(height/2)),
int(np.ceil(depth/2))))
vector_x = moving_image.GetDirection()[0:3]
vector_y = moving_image.GetDirection()[3:6]
vector_z = moving_image.GetDirection()[6:9]
new_vector_x = final_transform.GetInverse().TransformVector(vector_x, center)
new_vector_y = final_transform.GetInverse().TransformVector(vector_y, center)
new_vector_z = final_transform.GetInverse().TransformVector(vector_z, center)
new_direction = (new_vector_x[0], new_vector_x[1], new_vector_x[2], new_vector_y[0], new_vector_y[1], new_vector_y[2], new_vector_z[0], new_vector_z[1], new_vector_z[2])
registered_image.SetDirection(new_direction)
# Adjust the registered image origin
new_origin = final_transform.GetInverse().TransformPoint(registered_image.GetOrigin())
registered_image.SetOrigin(new_origin)