Bspline Free form deformable registration

You could find some inspiration from IterativeClosestPoint1.cxx example.

1 Like

Thank you so much . I will definitely look into it.

So I’m trying to convert my moving point list to the pointsets with ITK.F type and I’m getting this runtime error
TypeError: in method ‘itkPointF3___setitem__’, argument 3 of type ‘float’

so basically I’m predefining defining a list p with these parameters
PixelType = itk.F
Dimension = 3

PointSetType = itk.PointSet[PixelType, Dimension]

and then looping this list over the length of my moving points and adding those points onto this predefined list .
Is there a better and efficient way . Its just I don’t know C++ , so everything is python based .

Thanks a lot

That sounds right.

What code line triggers this?

so I have something like this

p = []  # creates an empty list 
p = itk.Point[PixelType, Dimension]()
count = 0
for i in range(0,len(moving_points)):
    p[i] = moving_points[i]  # this line triggers it 

moving points are a list of floating points with size of (70,3)

This example has a python version. You need to set each x, y and z coordinate separately to an itk.Point. Then insert those into an itk.PointSet.

Thank you so much . Looks like its working .

Is there a way to read the values inside it .When I open it its getting me an error of the variable not being packable .
Thanks

So when I print those points and p list I get this

So can I directly use these p points as my moving image points for b-spline ffd .

Thanks

I think you should be able to use the variable you call PointSet directly in the registration.

So when I used that variable I’m getting this error message

Can you please provide some insights .
Thanks

You should be using PointSetToPointSetMetric, not ImageToImageMetric family. As you are not sharing your code, I can only guess what you are doing.

Sorry my bad . This is the code I’m using

def bspline_intra_modal_registration(
    fixed_image,
    moving_image,
    fixed_image_mask=None,
    fixed_points=None,
    moving_points=None,
):

    registration_method = sitk.ImageRegistrationMethod()

    # Determine the number of BSpline control points using the physical spacing we want for the control grid.
    grid_physical_spacing = [50.0, 50.0, 50.0]  # A control point every 50mm
    image_physical_size = [
        size * spacing
        for size, spacing in zip(fixed_image.GetSize(), fixed_image.GetSpacing())
    ]
    mesh_size = [
        int(image_size / grid_spacing + 0.5)
        for image_size, grid_spacing in zip(image_physical_size, grid_physical_spacing)
    ]

    initial_transform = sitk.BSplineTransformInitializer(
        image1=fixed_image, transformDomainMeshSize=mesh_size, order=3
    )
    registration_method.SetInitialTransform(initial_transform)

    registration_method.SetMetricAsMeanSquares()
    # Settings for metric sampling, usage of a mask is optional. When given a mask the sample points will be
    # generated inside that region. Also, this implicitly speeds things up as the mask is smaller than the
    # whole image.
    registration_method.SetMetricSamplingStrategy(registration_method.RANDOM)
    registration_method.SetMetricSamplingPercentage(0.01)
    if fixed_image_mask:
        registration_method.SetMetricFixedMask(fixed_image_mask)

    # Multi-resolution framework.
    registration_method.SetShrinkFactorsPerLevel(shrinkFactors=[4, 2, 1])
    registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas=[2, 1, 0])
    registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()

    registration_method.SetInterpolator(sitk.sitkLinear)
    registration_method.SetOptimizerAsLBFGSB(
        gradientConvergenceTolerance=1e-5, numberOfIterations=100
    )

    # If corresponding points in the fixed and moving image are given then we display the similarity metric
    # and the TRE during the registration.
    if fixed_points and moving_points:
        registration_method.AddCommand(
            sitk.sitkStartEvent, rc.metric_and_reference_start_plot
        )
        registration_method.AddCommand(
            sitk.sitkEndEvent, rc.metric_and_reference_end_plot
        )
        registration_method.AddCommand(
            sitk.sitkIterationEvent,
            lambda: rc.metric_and_reference_plot_values(
                registration_method, fixed_points, moving_points
            ),
        )

    return registration_method.Execute(fixed_image, moving_image)

Correspondingly, you should use PointSetToPointSetRegistrationMethod, not ImageRegistrationMethod.

Thank you so much . Really Appreciated . I will look into that .

Is there a Python version of this registration .
Its just I thought I was doing B-spline FFD image registration which had fixed and moving image , fixed mask ( which I used airway mask ) and fixed and moving points (which are the coordinates of my moving and fixed airway masks at each bifurcations ) .

Sorry for all the trouble I’m giving
Thanks

Oh, it looks like it is not exposed in Python like most of the other registration methods and metrics. Good news: adding it is not hard, see e.g. itkImageRegistrationMethod.wrap.

1 Like

I just made a PR which should add required Python support:

1 Like

Thank you .
Sorry , But I don’t see the PR on GitHub .

I converted it into a draft PR, because I realized that my initial attempt does not work :frowning:

Oh okay . So my question about the Bspine FFD image registration is
What exactly are those fixed and moving points in this example are .
def bspline_intra_modal_registration(
fixed_image,
moving_image,
fixed_image_mask=None,
fixed_points=None,
moving_points=None,
):

registration_method = sitk.ImageRegistrationMethod()

# Determine the number of BSpline control points using the physical spacing we want for the control grid.
grid_physical_spacing = [50.0, 50.0, 50.0]  # A control point every 50mm
image_physical_size = [
    size * spacing
    for size, spacing in zip(fixed_image.GetSize(), fixed_image.GetSpacing())
]
mesh_size = [
    int(image_size / grid_spacing + 0.5)
    for image_size, grid_spacing in zip(image_physical_size, grid_physical_spacing)
]

initial_transform = sitk.BSplineTransformInitializer(
    image1=fixed_image, transformDomainMeshSize=mesh_size, order=3
)
registration_method.SetInitialTransform(initial_transform)

registration_method.SetMetricAsMeanSquares()
# Settings for metric sampling, usage of a mask is optional. When given a mask the sample points will be
# generated inside that region. Also, this implicitly speeds things up as the mask is smaller than the
# whole image.
registration_method.SetMetricSamplingStrategy(registration_method.RANDOM)
registration_method.SetMetricSamplingPercentage(0.01)
if fixed_image_mask:
    registration_method.SetMetricFixedMask(fixed_image_mask)

# Multi-resolution framework.
registration_method.SetShrinkFactorsPerLevel(shrinkFactors=[4, 2, 1])
registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas=[2, 1, 0])
registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()

registration_method.SetInterpolator(sitk.sitkLinear)
registration_method.SetOptimizerAsLBFGSB(
    gradientConvergenceTolerance=1e-5, numberOfIterations=100
)

# If corresponding points in the fixed and moving image are given then we display the similarity metric
# and the TRE during the registration.
if fixed_points and moving_points:
    registration_method.AddCommand(
        sitk.sitkStartEvent, rc.metric_and_reference_start_plot
    )
    registration_method.AddCommand(
        sitk.sitkEndEvent, rc.metric_and_reference_end_plot
    )
    registration_method.AddCommand(
        sitk.sitkIterationEvent,
        lambda: rc.metric_and_reference_plot_values(
            registration_method, fixed_points, moving_points
        ),
    )

return registration_method.Execute(fixed_image, moving_image)

For me I thought I was using this approach where my fixed and moving points are fixed mask image and moving mask image bifurcation points . OR is there a way to implement this approach on my dataset .

Sorry I’m just new to registration and trying to wrap my head around it . Thanks for understanding