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
586 stars 161 forks source link

Encountering the error "RuntimeError: Registration failed with error code 1", when MRA images and T1-weighted images are co-registered with "SyN" #676

Open tomo-akiyama opened 2 weeks ago

tomo-akiyama commented 2 weeks ago

Hello, everyone,

I’m interested in co-registering the T1 weighted images and MRA images using IXIdataset (http://brain-development.org/ixi-dataset/). I intend to primarily perform analysis with the MRA images, so I am trying to use the MRA as the reference image for the co-registration. After resampling and add-padding, I attempted to co-register using SyN. However, I encountered the error “RuntimeError: Registration failed with error code 1.”

Take case "IXI039-HH-1261" as an example. IXI039-HH-1261-MRA.nii.gz IXI039-HH-1261-T1.nii.gz

img_MRA = ants.image_read(input_MRA_path). img_T1 = ants.image_read(input_T1_path) ants.plot(img_T1, overlay=img_MRA)

MRA image header ANTsImage (RPI) Pixel Type : float (float32) Components : 1 Dimensions : (512, 512, 100) Spacing : (0.4688, 0.4688, 0.8) Origin : (-126.4276, 83.1077, -21.5363) Direction : [ 0.9968 0.0793 0.0106 0.079 -0.9964 0.0321 -0.0131 0.0311 0.9994]

T1w image header ANTsImage (AIL) Pixel Type : float (float32) Components : 1 Dimensions : (256, 256, 150) Spacing : (0.9375, 0.9375, 1.2) Origin : (99.2376, -120.2171, -120.6638) Direction : [-0.0776 0.0184 -0.9968 0.9882 -0.1311 -0.0794 0.1322 0.9912 0.008 ]

04c455ff-6f54-48f0-ade0-7aa4a55a115f The MRA does not cover the whole brain and has a narrower imaging range compared to the T1-weighted image.

First, I performed isotropic resampling. # Isotropic voxels of 0.5mm voxel_size = 0.5 img_MRA_resampled = ants.resample_image(img_MRA, (voxel_size, voxel_size, voxel_size),False,4) img_T1_resampled = ants.resample_image(img_T1, (voxel_size, voxel_size, voxel_size),False,4)

In this state, when using img_MRA_resampled as the reference for co-registering the T1 image, it is possible to perform the co-registration using “SyN.” However, as mentioned earlier, since the MRA image has a narrower range compared to the T1 image, data from the top of the head and brainstem in the T1 image is lost as shown bellow. 3a5b4d28-e732-4288-bab8-d756c3145409

Since I need the whole brain T1 image after co-registration, I decided to add padding to the MRA image.

matrix_size = max(img_MRA_resampled.shape) img_MRA_resampled = ants.pad_image(img_MRA_resampled, shape=(matrix_size, matrix_size, matrix_size))

MRA image header after resampling and padding ANTsImage (RPI) Pixel Type : float (float32) Components : 1 Dimensions : (480, 480, 480) Spacing : (0.5, 0.5, 0.5) Origin : (-126.4276, 83.1077, -21.5363) Direction : [ 0.9968 0.0793 0.0106 0.079 -0.9964 0.0321 -0.0131 0.0311 0.9994]

Then, using resampled and add-padded T1 image and resampled the MRA image, I performed an initial registration using TRSAA transformation, which executed without any issues.

initial_registration = ants.registration( fixed=img_MRA_resampled, moving=img_T1_resampled, type_of_transform='TRSAA', verbose=True ) initial_registered_image = initial_registration['warpedmovout']

The issue arises at the next step. When attempting non-linear registration using SyN, the following error occurs:

final_registration = ants.registration( fixed=img_MRA_resampled, moving=initial_registered_image, type_of_transform='SyN', initial_transform=initial_transform )

in registration(fixed, moving, type_of_transform, initial_transform, outprefix, mask, moving_mask, mask_all_stages, grad_step, flow_sigma, total_sigma, aff_metric, aff_sampling, aff_random_sampling_rate, syn_metric, syn_sampling, reg_iterations, aff_iterations, aff_shrink_factors, aff_smoothing_sigmas, write_composite_transform, random_seed, verbose, multivariate_extras, restrict_transformation, smoothing_in_mm, *kwargs) 1346 reg_exit = libfn(processed_args) 1347 if (reg_exit != 0): -> 1348 raise RuntimeError(f"Registration failed with error code {reg_exit}") 1349 afffns = glob.glob(outprefix + "" + "[0-9]GenericAffine.mat") 1350 fwarpfns = glob.glob(outprefix + "*" + "[0-9]Warp.nii.gz") RuntimeError: Registration failed with error code 1

mask = ants.get_mask(img_MRA_resampled) moving_mask = ants.get_mask(img_T1_resampled) Using these masks in co-registration made no difference.

The same error occurs when using ‘SyNAggro’ as well. The co-registration using TRSAA is quite successful, but there is still some misalignment. Therefore, I want to perform non-linear alignment as well.

Any advice would be greatly appreciated. Thank you.

gdevenyi commented 2 weeks ago

This is the repo for ANTs, this issue should be transferred to https://github.com/ANTsX/ANTsPy

cookpa commented 2 weeks ago

final_registration = ants.registration( fixed=img_MRA_resampled, moving=initial_registered_image, type_of_transform='SyN', initial_transform=initial_transform )

Try replacing SyN with SyNOnly and add initial_transform='Identity'. You don't want to re-do the affine part

cookpa commented 2 weeks ago

Actually, I think there's two problems in your initial command.

final_registration = ants.registration( fixed=img_MRA_resampled, moving=initial_registered_image, type_of_transform='SyN', initial_transform=initial_transform )

You're passing the initial_transform, but you're also using initial_registered_image as the moving image. So you're giving the registration a moving image that has already had initial_transform applied, then it's being applied again. The correct thing do do would be

final_registration = ants.registration( fixed=img_MRA_resampled, moving=initial_registered_image, type_of_transform='SyNOnly', initial_transform='Identity')

But you can also do

final_registration = ants.registration( fixed=img_MRA_resampled,  moving=img_T1_resampled, 
type_of_transform='SyNOnly', initial_transform=initial_registration['fwdtransforms'] )
tomo-akiyama commented 2 weeks ago

@cookpa

Thank you for your help.

As you mentioned, I wasn't fully clear on the procedure of combining a non-linear transformation as the final registration step after the initial registration (Rigid or TRSAA). However, even after following your suggestion to use "SyNOnly," I encountered the same error.

final_registration = ants.registration( fixed=img_MRA_resampled, moving=initial_registered_image, type_of_transform='SyNOnly', initial_transform='Identity') or final_registration = ants.registration( fixed=img_MRA_resampled, moving=img_T1_resampled, type_of_transform='SyNOnly', initial_transform=initial_registration['fwdtransforms'] )

-> 1348 raise RuntimeError(f"Registration failed with error code {reg_exit}") 1349 afffns = glob.glob(outprefix + "" + "[0-9]GenericAffine.mat") 1350 fwarpfns = glob.glob(outprefix + "" + "[0-9]Warp.nii.gz")

RuntimeError: Registration failed with error code 1

@gdevenyi Thank you for your suggestion.

cookpa commented 2 weeks ago

If you add verbose=True, does it give more information?

tomo-akiyama commented 2 weeks ago

@cookpa

Unfortunately, the error log is the same as before.

========================= Running SyN registration (varianceForUpdateField = 3.0000e+00, varianceForTotalField = 0.0000e+00)

XXDIAGNOSTIC,Iteration,metricValue,convergenceValue,ITERATION_TIME_INDEX,SINCE_LAST 1DIAGNOSTIC, 1, -2.069232761860e-01, inf, 1.3341e+00, 1.3341e+00, 1DIAGNOSTIC, 2, -2.104631662369e-01, inf, 1.6698e+00, 3.3565e-01, 1DIAGNOSTIC, 3, -2.135409414768e-01, inf, 2.0126e+00, 3.4281e-01, 1DIAGNOSTIC, 4, -2.159593105316e-01, inf, 2.3583e+00, 3.4574e-01, 1DIAGNOSTIC, 5, -2.178752869368e-01, inf, 2.7043e+00, 3.4592e-01, 1DIAGNOSTIC, 6, -2.192840427160e-01, inf, 3.0550e+00, 3.5075e-01, 1DIAGNOSTIC, 7, -2.203439474106e-01, inf, 3.4087e+00, 3.5366e-01, 1DIAGNOSTIC, 8, -2.207382023335e-01, 5.225569475442e-03, 3.7746e+00, 3.6591e-01, 1DIAGNOSTIC, 9, -2.214111983776e-01, 3.555110655725e-03, 4.1341e+00, 3.5948e-01, 1DIAGNOSTIC, 10, -2.217025458813e-01, 2.354470547289e-03, 4.4869e+00, 3.5281e-01, 1DIAGNOSTIC, 11, -2.219092845917e-01, 1.531834015623e-03, 4.8425e+00, 3.5562e-01, 1DIAGNOSTIC, 12, -2.219463884830e-01, 9.647732949816e-04, 5.1977e+00, 3.5524e-01, 1DIAGNOSTIC, 13, -2.219606339931e-01, 5.888302111998e-04, 5.5536e+00, 3.5590e-01, 1DIAGNOSTIC, 14, -2.220667898655e-01, 3.566694213077e-04, 5.9200e+00, 3.6640e-01, 1DIAGNOSTIC, 15, -2.219611108303e-01, 2.080217527691e-04, 6.2853e+00, 3.6528e-01, 1DIAGNOSTIC, 16, -2.217740416527e-01, 6.959090387681e-05, 6.6492e+00, 3.6386e-01, 1DIAGNOSTIC, 17, -2.216869294643e-01, -6.804820259276e-06, 7.0094e+00, 3.6027e-01, Exception caught: itk::InvalidRequestedRegionError (0x12182e8f0) Location: "unknown" File: /Users/admin/actions-runner/_work/ANTsPy/ANTsPy/itksource/Modules/Core/Common/src/itkDataObject.cxx Line: 367 Description: Requested region is (at least partially) outside the largest possible region.

cookpa commented 2 weeks ago

That suggests the registration has gone severely off track. Registering a slab to a whole brain is difficult with nonlinear deformation. Unfortunately the python registration interface does not expose all regularization options, but you can try doing fewer iterations by setting something like reg_iterations=(10), this will limit the amount of deformation.

You can also restrict deformation along a particular axis with the restrict_transformation option, if the expected deformations are along a particular axis (eg, the phase encoding axis).

gdevenyi commented 1 week ago

I have had good success with slab-to-wholebrain registration with the following steps:

  1. Skullstirp both
  2. Rigid align slab to wholebrain
  3. Add a mask to slab and refine with affine registration
  4. Continue registration with slab mask and SyN

I've done all this with classic ants so I can't recommend commands for antspy

gdevenyi commented 1 week ago

Clarification: for the slab mask I mean simply an otsu foreground/background ROI type mask or brain mask

cookpa commented 1 week ago

That's a good idea. Even a crude mask (eg, including all the MRA FOV, but excluding the added padding slices, which should all be zero) would be helpful.

tomo-akiyama commented 1 week ago

@cookpa @gdevenyi

I'm sorry for the delay in my response.

I've tried various methods, but it seems that @gdevenyi's suggested approach is indeed the best.

Using add_pad causes issues with ANTs registration. So far, I've been segmenting images after registration, but now I'm starting to think it might be better to segment directly from the original image, then apply registration and transform the segmented binary image using transform information with nearest neighbor. This way, I feel I can avoid artifacts introduced by resampling or registration.

Thank you very much in any case.