Great - thanks for the info Bradley!
So I’ll always remember to cast
I’ve got a follow on question:
I’ve got my initial registration wrapper function working, and it seems to be fine for what I’ll need.
I’m using simple mean squares metric as the similarity, as I’ll always be using images with similar greyscale values/morphology.
So I’m testing things, using a sample image, where I simply translate/rotate the image to test to register back on itself.
So in that case, for Mean Squares, I would have (naively?) thought that the mean square value when the images are registered would be ~0.
However, when I run my code, I get a great fit, in a reasonably fast time, that is robust for the translations/rotations I’m trying - however the Mean Squared final values never approach 0.
I don’t understand why not In my mind they should.
…is that because there is a region where the fixed and moving images don’t overlap, but the registration still try to calculate a mean square value for those pixels, but then gets some non-zero value???
The code I’m using is below. When I run it and view the metric value history it never approaches zero, even for pretty much perfect registrations
import SimpleITK as sitk
def register(fixed, moving, debug=False):
if debug:
print("fixed:")
print(" origin:", fixed.GetOrigin())
print(" direction:", fixed.GetDirection())
print(" width/height:", fixed.GetWidth(), fixed.GetHeight())
print(" spacing:", fixed.GetSpacing())
print(" type:", fixed.GetPixelIDTypeAsString())
print(" min/max:", sitk.GetArrayViewFromImage(fixed).min(), sitk.GetArrayViewFromImage(fixed).max())
print("moving:")
print(" origin:", moving.GetOrigin())
print(" direction:", moving.GetDirection())
print(" width/height:", moving.GetWidth(), moving.GetHeight())
print(" spacing:", moving.GetSpacing())
print(" type:", moving.GetPixelIDTypeAsString())
print(" min/max:", sitk.GetArrayViewFromImage(moving).min(), sitk.GetArrayViewFromImage(moving).max())
if debug:
metricVals = []
# set min values to both images to be zero
fixedP = fixed - sitk.GetArrayViewFromImage(fixed).min()
movingP = moving - sitk.GetArrayViewFromImage(moving).min()
# cast to float for registration
fixedP = sitk.Cast(fixedP, sitk.sitkFloat32)
movingP = sitk.Cast(movingP, sitk.sitkFloat32)
if debug:
print("zeroed values")
print(" fixed min/max:", sitk.GetArrayViewFromImage(fixedP).min(), sitk.GetArrayViewFromImage(fixedP).max())
print(" moving min/max:", sitk.GetArrayViewFromImage(movingP).min(), sitk.GetArrayViewFromImage(movingP).max())
# set initial transform
initial_transform = sitk.CenteredTransformInitializer(
fixedP,
movingP,
sitk.Euler2DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY)
# setup registration
registration = sitk.ImageRegistrationMethod()
registration.SetInterpolator(sitk.sitkLinear)
# similarity metric to use
registration.SetMetricAsMeanSquares()
# set optimizer
registration.SetOptimizerAsRegularStepGradientDescent(
learningRate = 1.0,
minStep=1e-3,
numberOfIterations=100,
gradientMagnitudeTolerance=1e-6 )
# set transform
registration.SetInitialTransform(initial_transform)
# setup multi-resolution
registration.SetShrinkFactorsPerLevel(shrinkFactors = [4,2,1])
registration.SetSmoothingSigmasPerLevel(smoothingSigmas=[2,1,0])
registration.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()
if debug:
registration.AddCommand(sitk.sitkIterationEvent, lambda: metricVals.append(registration.GetMetricValue()))
# run registration
transform = registration.Execute(fixedP, movingP)
# finished
if debug:
return transform, registration.GetOptimizerStopConditionDescription(), metricVals
else:
return transform, registration.GetOptimizerStopConditionDescription()