ANTs: Resulting warp unexpectedly has a smaller field of view than input images
Describe the problem
ANTS produces a warp field that is smaller than the input images, so the resampled moving image is not fully aligned to the fixed image.
To Reproduce
Make some random data using numpy and nibabel and run a trivial registration of the image to itself:
$ python3 -c "import nibabel as nib; import numpy as np; nib.save(nib.nifti1.Nifti1Image(np.random.random((500, 500, 500)), np.eye(4) * 2), '/tmp/img.nii')"
$ ANTS 3 --number-of-affine-iterations 0 -m 'CC[/tmp/img.nii,/tmp/img.nii,1.0,3]' -t SyN[0.2] -r Gauss[2,1] -i 2x2x1x1 -o /tmp/ants.nii
Run Reg
values 1
Fixed image file: /tmp/img.nii
Moving image file: /tmp/img.nii
Metric 0: Not a Point-set
Fixed image file: /tmp/img.nii
Moving image file: /tmp/img.nii
similarity metric weight: 1
Radius: [3, 3, 3]
radius: [3, 3, 3]
Use identity affine transform as initial affine para.
aff_init.IsNull()==1
Use identity affine transform as initial fixed affine para.
fixed_aff_init.IsNull()==1
Continue affine registration from the input
affine_opt.use_rotation_header = 0
affine_opt.ignore_void_orgin = 0
transform_initial: IsNotNull():0
OptAffine: metric_type=AffineWithMutualInformation
MI_bins=32 MI_samples=32000
number_of_seeds=0 time_seed=1599255771
number_of_levels=1
number_of_iteration_list=[0]
graident_scales=[1,1,1,1,1,1,1,1,1,1,0.0001,0.0001,0.0001]
is_rigid = 0
mask null: 1
maximum_step_length=0.1
relaxation_factor=0.5
minimum_step_length=0.0001
translation_scales=0.0001
opt.transform_initial.IsNull(): 1
opt.use_rotation_header: 0
opt.ignore_void_orgin: 0
input affine center: [-499.008, -499.005, 498.986]
input affine para: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
level 0, iter 0, size: fix[500, 500, 500]-mov[500, 500, 500], affine para: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
does not reach oscillation, current step: 0.1>0.0001
A=1 0 0
0 1 0
0 0 1
rotation R1 0 0
0 1 0
0 0 1
upper R1 0 0
0 1 0
0 0 1
s=0.25 u=0 v=0 w0 r=1
m_Rotation from vnl0 0 0 1
level 0, iter 0, size: fix[500, 500, 500]-mov[500, 500, 500], affine para: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
does not reach oscillation, current step: 0.1>0.0001
v1 0 v2 0
final [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
outputput affine center: [-499.008, -499.005, 498.986]
output affine para: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
initial measure value (MMI): rval = -2.39058
final measure value (MMI): rval = -2.38938
finish affine registeration...
Requested Transformation Model: SyN : Using
SyN diffeomorphic model for transformation.
Grad Step 0.2 total-smoothing 1 gradient-smoothing 2
setting N-TimeSteps = 1 trunc 256
ScaleFactor 8 nlev 4 curl 0
allocated def field -1 0 0
0 -1 0
0 0 1
Its at this level 2
Allocating
Allocating Done
iteration 1 energy 0 : 0
iteration 2 energy 0 : 0
tired convergence: reached max iterations
ScaleFactor 4 nlev 4 curl 1
Its at this level 2
iteration 1 energy 0 : 0
iteration 2 energy 0 : 0
tired convergence: reached max iterations
ScaleFactor 2 nlev 4 curl 2
Its at this level 1
iteration 1 energy 0 : 0
tired convergence: reached max iterations
ScaleFactor 1 nlev 4 curl 3
Its at this level 1
iteration 1 energy 0 : 0
tired convergence: reached max iterations
Registration Done
begin writing /tmp/ants.nii
writing /tmp/ants affine
writing /tmp/ants def
filename /tmp/antsWarp.nii
$ python3 -c "import nibabel as nib; print(nib.load('/tmp/antsWarp.nii').header)"
<class 'nibabel.nifti1.Nifti1Header'> object, endian='<'
sizeof_hdr : 348
data_type : b''
db_name : b''
extents : 0
session_error : 0
regular : b'r'
dim_info : 0
dim : [ 5 378 378 378 1 3 1 1] # <- the array size has decreased from 500x500x500
intent_p1 : 0.0
intent_p2 : 0.0
intent_p3 : 0.0
intent_code : vector
datatype : float32
bitpix : 32
slice_start : 0
pixdim : [1. 2. 2. 2. 0. 0. 0. 0.] # <- while the voxel size is unchanged
vox_offset : 0.0
scl_slope : nan
scl_inter : nan
slice_end : 0
slice_code : unknown
xyzt_units : 2
cal_max : 0.0
cal_min : 0.0
slice_duration : 0.0
toffset : 0.0
glmax : 0
glmin : 0
descrip : b''
aux_file : b''
qform_code : scanner
sform_code : unknown
quatern_b : 0.0
quatern_c : 0.0
quatern_d : 0.0
qoffset_x : -0.0
qoffset_y : -0.0
qoffset_z : 0.0
srow_x : [0. 0. 0. 0.]
srow_y : [0. 0. 0. 0.]
srow_z : [0. 0. 0. 0.]
intent_name : b''
magic : b'n+1'
This seems to be independent of IO (e.g., MINC vs NIFTI inputs/outputs produce the same result).
System information
-
OS: Ubuntu Linux
-
OS version: 16.04 (system gcc 5.4.0)
-
Type of system: desktop
-
OS: CentOS
-
OS version: 6 (gcc 7.2.0)
-
Type of system: cluster
-
OS: CentOS + Nixpkgs
-
OS version: 7 (gcc 8.3)
-
Type of system: cluster
ANTs version information
- ANTs code version: [output of antsRegistration --version]
ANTs Version: 2.3.4.dev170-g11953
Compiled: Sep 3 2020 12:51:41
(also present with 2.3.1.dev90-g55fa5, but not in 2.2.0)
- ANTs installation type: compiled from source using superbuild and nearly default options (except enabling shared libs)
Additional information
Running with a debug build produces no errors, but valgrind produces a couple suspicious things such as:
==28741== Conditional jump or move depends on uninitialised value(s)
==28741== at 0x4D7FA66: SetEdgePaddingValue (itkWarpImageMultiTransformFilter.h:217)
==28741== by 0x4D7FA66: itk::ANTSImageRegistrationOptimizer<3u, float>::WarpMultiTransform(itk::SmartPointer<itk::Image<float, 3u> >, itk::SmartPointer<itk::Image<float, 3u> >, itk::SmartPointer<itk::MatrixOffsetTransformBase<double, 3u, 3u> >, itk::SmartPointer<itk::Image<itk::Vector<float, 3u>, 3u> >, bool, itk::SmartPointer<itk::MatrixOffsetTransformBase<double, 3u, 3u> >) (itkANTSImageRegistrationOptimizer.h:644)
...
but I didn’t investigate this further.
Initial discovery by @mcvaneede.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 20 (14 by maintainers)
Hey @cookpa , Sorry just got back from a run but I’m pretty sure I found the issue without even looking at the ANTs code. Look at the different signatures for the likely culprit function:
itkVectorExpandImageFilter.h
vs.
itkExpandImageFilter.h
Going to do some digging in the optimizer class to verify that this is the issue. Tagging @hjmjohnson since he made the original change and I don’t know yet if this needs to be reported back to ITK.