ANTsX / ANTsPy

A fast medical imaging analysis library in Python with algorithms for registration, segmentation, and more.
https://antspyx.readthedocs.io
Apache License 2.0
629 stars 161 forks source link

Descrepancy in performance of AntsPy Behaviour when running in a loop for a long time. #552

Open tushaarkataria opened 7 months ago

tushaarkataria commented 7 months ago

I am trying to register patches of size 12000x12000 that are sampled from a bigger image. I am observing inconsistent performance of AntsPy.

Let's say i have 20 patches of the above mentioned size. Ants framework when executed with a python scripts works for 18 of them and doesn't for the other 2. By doesn't i mean, it gives bad performance on registration. If I rerun it still works for 18 of them, but the 2 for which it doesn't work, are different patches. AntsPy is performing inconsistently while in a loop.

Then I tested running ants on those 2 patches using a Notebook, and it registered them well. So Ants is working but AntsPy while in a loop is not.

I am not sure how to debug this issue ? Can it be a memory flushing issue ?? But i see in the AntsPy Code that it's calling a binary, which i think gets flushed on it own.

Has anyone else come across this issue ?? Any help would be appreciated.

cookpa commented 7 months ago

We can help more if you can share code and example data.

There is variation in registration due to random sampling, which you can disable by using one of the "repro" methods in antsRegistration.

It is suspicious if it works for exactly 18 and fails for 2 every time. What if you run the same patch 20 times?

tushaarkataria commented 7 months ago

I tried the "repro" setting and flushing all the variables related to the registration but i still see the same issue.

And i just want to clarify, it's kind of like 2-3 out the 20 patches, but similar in percentage when compared to the total number of patches like approximately 10-20% of the patches are not registered. But I can chalk that upto optimization issue and feature issue that i used for registration.

It's when i try to register the same un-registered image from the previous run in a notebook or another script, it registers well. I am not sure how to debug that behavior.

cookpa commented 7 months ago

I made a 2D example experiment here

https://github.com/cookpa/antsPyRegistrationStability

Basically, I took a slice from two 0.5mm T1w images, then ran registration 500 times, using

  1. antsRegistrationSyN.sh on the command line
  2. ants.registration in a for loop
  3. ants.registration outside of a for loop

Here's a plot of the CC metric (radius = 4) after registration. These are negative values as used in ANTs optimizers so a more negative number is greater similarity

image

I did notice a few outliers in both the CLI and the python output, but I don't see strong evidence that using a for loop makes things worse.

The failure rate is subjective but to my eye it was less than 10-20%. If your registrations are more unstable, here's a few things you could try:

  1. Pad the images. Having images extend all the way to the boundary of the FOV can lead to stability issues. ImageMath PadImage can be used to add (say 20 voxels of) empty space around the borders before registration. This doesn't change the physical space so you can still apply the transforms to the original images.

  2. Change the default downsampling and smoothing. The default parameters have been somewhat optimized for 3D anatomical images. It might be downsampling or smoothing too much.

  3. Check initialization. The default is to align the center of mass of the two images, if this is likely to go wrong (eg, there are negative intensities), an alternative strategy may be required.

  4. Check affine registration. This is faster and its worth optimizing this a bit because a poor affine stage will often lead to bad deformable results.

  5. Impose regularization eg using a transform like SyN[0.2, 3,1] rather than the default SyN[0.2, 3,0]. If the problem is unreasonably large deformations in the syn stage.

cookpa commented 6 months ago

@tushaarkataria it looks like you were definitely on to something! #579

cookpa commented 6 months ago

OK we've tried our best to eliminate any uninitialized memory being accessed in images. I'm not certain that this was affecting antsRegistration, but it was affecting cortical thickness in a similar way. Anyway, wheels available here if anyone wants to try them out

https://github.com/ANTsX/ANTsPy/actions/runs/8392167991

cookpa commented 6 months ago

I updated the scripts here

https://github.com/cookpa/antsPyRegistrationStability

to use the "repro" mode options. I get 100% reproducible results over 100 runs even before the recent fixes, so maybe registration wasn't affected? Though other functions definitely were. Running more iterations now to confirm

cookpa commented 6 months ago

Confirmed after 500 iterations - registration of the same data is 100% deterministic with this call

import ants
import sys

fi = ants.image_read('data/fixed_slice_2d.nii.gz')
mi = ants.image_read('data/moving_slice_2d.nii.gz')

iterations = int(sys.argv[1])

for i in range(iterations):
    reg = ants.registration(fixed=fi, moving=mi, type_of_transform='antsRegistrationSyNQuickRepro[s]')

    deformed_mi = ants.apply_transforms(fixed=fi, moving=mi, transformlist=reg['fwdtransforms'])

    ants.image_write(deformed_mi, 'output/antspy_loop/moving_deformed_iteration_{:04d}.nii.gz'.format(i))

    if (i + 1) % int(iterations / 10) == 0:
        print(f"Iteration {i + 1}/{iterations} completed.")

On Mac OS 12 (Intel) with ANTsPy 0.4.2