admin管理员组

文章数量:1291065

I have a moved image that I created with simpleITK transforms and registration and I have object's measurements and centroids from the moved image(working as expected)

my transformation/registration:

final_transform1, registration_method1 = img_reg(roi11, mask11, roi21, mask21)

I then resample the images to produce the moved images:

moved_roi21 = sitk.Resample(roi21, roi11, final_transform1, sitk.sitkLinear, 0.0, roi21.GetPixelID())
moved_mask21 = sitk.Resample(mask21, roi11, final_transform1, sitk.sitkNearestNeighbor, 0.0, mask21.GetPixelID())

I do some measuring and get the centroids of those objects:

But then, I'm attempting to take those centroids from the moved image to map the location back to the original image.I need to pinpoint the centroid on the original image. Some of the centroids shift a bit due to the transformation. I tried the GetInverse() Function

Below: the column "Post centroid" is the location of the centroid from the moved image

    inverse_transform1 = final_transform1.GetInverse()
stones1["Original Post Centroid"] = stones1["Post centroid"].apply(
        lambda centroid: remap_centroid_with_inverse_transform(centroid, inverse_transform1)
     )
def remap_centroid_with_inverse_transform(centroid, inverse_transform):
    """
    Maps a centroid from the moved space back to the original space using the inverse transform.
    
    Args:
        centroid (tuple): The centroid in the moved image space (x, y, z).
        inverse_transform (sitk.Transform): The inverse transform from registration.
    
    Returns:
        tuple: The remapped centroid in the original image space (x', y', z').
    """
    try:
        # Ensure the centroid is a tuple
        if isinstance(centroid, (list, tuple)) and len(centroid) == 1:
            centroid = centroid[0]
        
        # Transform the centroid back to the original space
        remapped_centroid = inverse_transform.TransformPoint(centroid)
        print(f"Centroid {centroid} remapped to {remapped_centroid} using inverse transform.")
        return remapped_centroid
    except Exception as e:
        print(f"Error remapping centroid {centroid} with inverse transform: {e}")
        return None

My hope was this would map the centroid from the moved image(or any centoid in the moved image) to the centroid of the original image before the transform, but the remapping way off.

In case you're wondering what the img_reg function is doing:

def img_reg(fixed_image, fixed_image_mask, moving_image, moving_image_mask):

registration_method = sitk.ImageRegistrationMethod()

# Similarity metric settings.
# cross correlation
registration_method.SetMetricAsANTSNeighborhoodCorrelation(3)  
registration_method.SetMetricSamplingStrategy(registration_method.RANDOM) 
registration_method.SetMetricSamplingPercentage(0.95)   
registration_method.SetMetricFixedMask(fixed_image_mask)

registration_method.SetInterpolator(sitk.sitkLinear)

# Optimizer settings.
registration_method.SetOptimizerAsGradientDescent(learningRate=1.0, numberOfIterations=300, convergenceMinimumValue=1e-6, convergenceWindowSize=10)
registration_method.SetOptimizerScalesFromPhysicalShift()

# Setup for the multi-resolution framework.            
registration_method.SetShrinkFactorsPerLevel(shrinkFactors = [3, 2, 1])
registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas= [2, 1, 1])
registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()

# Set the initial moving and optimized transforms.
#rigid

fix_mask = sitk.Cast(fixed_image > 1.0e-6, sitk.sitkFloat32) * sitk.Cast(fixed_image_mask > 0, sitk.sitkFloat32)
mov_mask = sitk.Cast(moving_image > 1.0e-6, sitk.sitkFloat32) * sitk.Cast(moving_image_mask > 0, sitk.sitkFloat32)
fix = sitk.Cast(fixed_image, sitk.sitkFloat32) * fix_mask
mov = sitk.Cast(moving_image, sitk.sitkFloat32) * mov_mask
#sitk.WriteImage(fix, 'tfix.nii.gz')
#sitk.WriteImage(mov, 'tmov.nii.gz')

initial_transform = sitk.CenteredTransformInitializer(fix_mask, 
                                                  mov_mask, 
                                                  sitk.Euler3DTransform(), 
                                                  sitk.CenteredTransformInitializerFilter.MOMENTS)  #GEOMETRY)


registration_method.SetOptimizerScales([0.2,0.2,0.2,0.5,0,5,0.5])
registration_method.SetInitialTransform(initial_transform, inPlace=True) 
final_transform = registration_method.Execute(fix, mov)

# Always check the reason optimization terminated.
print('Final metric value: {0}'.format(registration_method.GetMetricValue()))
print('Optimizer\'s stopping condition, {0}'.format(registration_method.GetOptimizerStopConditionDescription()))

return final_transform, registration_method

Although the code runs:

The output of my debugging print statement

Centroid (-45.859375, 177.4645565257353, -218.10075018504835) remapped to (-37.074328555487725, 176.6125729911438, -323.46181403960696) using inverse transform. However the actual Centroid is or the original centroid is:

(-40.66764664446721, 165.81388511782788, -113.14662579231575)

(-45.859375, 177.4645565257353, -218.10075018504835) is the correct centroid of measurement from the moved image that was transformed from (-40.66764664446721, 165.81388511782788, -113.14662579231575), but was transformed because it was a follow up image.

I have a moved image that I created with simpleITK transforms and registration and I have object's measurements and centroids from the moved image(working as expected)

my transformation/registration:

final_transform1, registration_method1 = img_reg(roi11, mask11, roi21, mask21)

I then resample the images to produce the moved images:

moved_roi21 = sitk.Resample(roi21, roi11, final_transform1, sitk.sitkLinear, 0.0, roi21.GetPixelID())
moved_mask21 = sitk.Resample(mask21, roi11, final_transform1, sitk.sitkNearestNeighbor, 0.0, mask21.GetPixelID())

I do some measuring and get the centroids of those objects:

But then, I'm attempting to take those centroids from the moved image to map the location back to the original image.I need to pinpoint the centroid on the original image. Some of the centroids shift a bit due to the transformation. I tried the GetInverse() Function

Below: the column "Post centroid" is the location of the centroid from the moved image

    inverse_transform1 = final_transform1.GetInverse()
stones1["Original Post Centroid"] = stones1["Post centroid"].apply(
        lambda centroid: remap_centroid_with_inverse_transform(centroid, inverse_transform1)
     )
def remap_centroid_with_inverse_transform(centroid, inverse_transform):
    """
    Maps a centroid from the moved space back to the original space using the inverse transform.
    
    Args:
        centroid (tuple): The centroid in the moved image space (x, y, z).
        inverse_transform (sitk.Transform): The inverse transform from registration.
    
    Returns:
        tuple: The remapped centroid in the original image space (x', y', z').
    """
    try:
        # Ensure the centroid is a tuple
        if isinstance(centroid, (list, tuple)) and len(centroid) == 1:
            centroid = centroid[0]
        
        # Transform the centroid back to the original space
        remapped_centroid = inverse_transform.TransformPoint(centroid)
        print(f"Centroid {centroid} remapped to {remapped_centroid} using inverse transform.")
        return remapped_centroid
    except Exception as e:
        print(f"Error remapping centroid {centroid} with inverse transform: {e}")
        return None

My hope was this would map the centroid from the moved image(or any centoid in the moved image) to the centroid of the original image before the transform, but the remapping way off.

In case you're wondering what the img_reg function is doing:

def img_reg(fixed_image, fixed_image_mask, moving_image, moving_image_mask):

registration_method = sitk.ImageRegistrationMethod()

# Similarity metric settings.
# cross correlation
registration_method.SetMetricAsANTSNeighborhoodCorrelation(3)  
registration_method.SetMetricSamplingStrategy(registration_method.RANDOM) 
registration_method.SetMetricSamplingPercentage(0.95)   
registration_method.SetMetricFixedMask(fixed_image_mask)

registration_method.SetInterpolator(sitk.sitkLinear)

# Optimizer settings.
registration_method.SetOptimizerAsGradientDescent(learningRate=1.0, numberOfIterations=300, convergenceMinimumValue=1e-6, convergenceWindowSize=10)
registration_method.SetOptimizerScalesFromPhysicalShift()

# Setup for the multi-resolution framework.            
registration_method.SetShrinkFactorsPerLevel(shrinkFactors = [3, 2, 1])
registration_method.SetSmoothingSigmasPerLevel(smoothingSigmas= [2, 1, 1])
registration_method.SmoothingSigmasAreSpecifiedInPhysicalUnitsOn()

# Set the initial moving and optimized transforms.
#rigid

fix_mask = sitk.Cast(fixed_image > 1.0e-6, sitk.sitkFloat32) * sitk.Cast(fixed_image_mask > 0, sitk.sitkFloat32)
mov_mask = sitk.Cast(moving_image > 1.0e-6, sitk.sitkFloat32) * sitk.Cast(moving_image_mask > 0, sitk.sitkFloat32)
fix = sitk.Cast(fixed_image, sitk.sitkFloat32) * fix_mask
mov = sitk.Cast(moving_image, sitk.sitkFloat32) * mov_mask
#sitk.WriteImage(fix, 'tfix.nii.gz')
#sitk.WriteImage(mov, 'tmov.nii.gz')

initial_transform = sitk.CenteredTransformInitializer(fix_mask, 
                                                  mov_mask, 
                                                  sitk.Euler3DTransform(), 
                                                  sitk.CenteredTransformInitializerFilter.MOMENTS)  #GEOMETRY)


registration_method.SetOptimizerScales([0.2,0.2,0.2,0.5,0,5,0.5])
registration_method.SetInitialTransform(initial_transform, inPlace=True) 
final_transform = registration_method.Execute(fix, mov)

# Always check the reason optimization terminated.
print('Final metric value: {0}'.format(registration_method.GetMetricValue()))
print('Optimizer\'s stopping condition, {0}'.format(registration_method.GetOptimizerStopConditionDescription()))

return final_transform, registration_method

Although the code runs:

The output of my debugging print statement

Centroid (-45.859375, 177.4645565257353, -218.10075018504835) remapped to (-37.074328555487725, 176.6125729911438, -323.46181403960696) using inverse transform. However the actual Centroid is or the original centroid is:

(-40.66764664446721, 165.81388511782788, -113.14662579231575)

(-45.859375, 177.4645565257353, -218.10075018504835) is the correct centroid of measurement from the moved image that was transformed from (-40.66764664446721, 165.81388511782788, -113.14662579231575), but was transformed because it was a follow up image.

Share Improve this question asked Feb 13 at 15:04 Joseph LoganJoseph Logan 11 silver badge1 bronze badge
Add a comment  | 

1 Answer 1

Reset to default 1

If I followed your code correctly we have the following:

  • Registering roi11 and roi21 where roi11 is the fixed image and roi21 is the moving image.
  • The result from registration is final_transform1, maps points from the roi11 physical space to roi21 physical space. roi21 and mask21 are resampled onto the roi11 grid using this transformation, yielding moved_roi21 and moved_mask21.
  • Points are somehow identified on moved_mask21 or moved_roi21 (make sure these points are in physical space, not image index based, see TransformContinuousIndexToPhysicalPoint).
  • To map these physical points back to the roi21 physical space you need to apply the final_transform to them. They are defined in the roi11 physical space and you want them in the roi21 physical space.

For further discussions relating to SimpleITK please use the ITK discourse which is a dedicated forum for the toolkit.

本文标签: