Subsample NIfTI

I am looking to subsample a 3D NIfTI file at 1:2 (basically make the data matrix half as long in every dimension.

Ideally this transformation would be paired with appropriate updating of the header (SFORM and QFORM should also be scaled, to reflect the new data matrix). Ideally I would also have control over the interpolation, though at this point I don’t care that much.

Can you recommend any simple tool to accomplish this? I was surprised that neither FSL nor ANTs have a simple function or parameter for this.

Pretty sure nipy images do all this magic with:

from nipy.io.api import load_image, save_image
img = load_image(fname_in)
save_image(img[::2, ::2, ::2], fname_out)

If you have Matlab and SPM12, you can use my nii_scale_dims script. For example, nii_scale_dims(‘img.nii’,0.5) halves the resolution in all three dimensions. This tends to make analyses 8 times faster which is nice for tutorials and testing pipelines. You can set an arbitrary scaling factor and it should keep the correct sform and qform.

@Chris_Rorden and others: I created this notebook for a quick demonstration of some signal processing principles:

in summary, one has to be careful about what downsampling means for subsequent use. for certain use-cases and dimensions, slicing is completely fine, but in other cases subsampling may introduce noise into the image.

2 Likes

@satra brings up an excellent point. I have only downsampled to speed up tutorials or accelerate testing of scripts. In general, one wants to be very cautious when downsampling data for real analyses, though I do think there are clear applications, e.g. getting robust kurtosis fits from high resolution images. I have now updated my Matlab script so it uses an anti-aliasing filter during downsampling if available (e.g. you have the image processing toolbox installed). If unavailable, it warns the user.

Hi friend… ANTs certainly does have this functionality - ResampleImage. You can resample based on spacing or voxels, with a wide range of interpolation schemes. Here’s a simple example in ANTsPy.

import ants

mni = ants.image_read(ants.get_ants_data('mni'))

print(mni)
#Out[3]: 
#ANTsImage
#	 Pixel Type : float
#	 Components : 1
#	 Dimensions : (182, 218, 182)
#	 Spacing    : (1.0, 1.0, 1.0)
#	 Origin     : (-90.0, 126.0, -72.0)
#	 Direction  : [ 1.  0.  0.  0. -1.  0.  0.  0.  1.]

import numpy as np

# resample based on spacing, linear interp
mni2 = ants.resample_image(mni,np.array(mni.spacing)*2, interp_type=0)

print(mni2)
#Out[8]: 
#ANTsImage
#	 Pixel Type : float
#	 Components : 1
#	 Dimensions : (91, 109, 91)
#	 Spacing    : (2.0, 2.0, 2.0)
#	 Origin     : (-90.0, 126.0, -72.0)
#	 Direction  : [ 1.  0.  0.  0. -1.  0.  0.  0.  1.]

# resample to a specific size, nearest neighbor interp
mni2a = ants.resample_image(mni,(100,100,100),True, interp_type=1)

print(mni2a)
#Out[10]: 
#ANTsImage
#	 Pixel Type : float
#	 Components : 1
#	 Dimensions : (100, 100, 100)
#	 Spacing    : (1.828, 2.192, 1.828)
#	 Origin     : (-90.0, 126.0, -72.0)
#	 Direction  : [ 1.  0.  0.  0. -1.  0.  0.  0.  1.]

Note there are also a ton of interpolations - namely from the ANTsPy docs: one of 0 (linear), 1 (nearest neighbor), 2 (gaussian), 3 (windowed sinc), 4 (bspline)

You can download the ANTsPy binaries really quickly (takes ~1 min) from the github (github.com/ANTsX/ANTsPy)

1 Like

@ncullen93 - i don’t think ants does any antialiasing though if you resample to a lower resolution. in most imaging experiments people would typically collect low resolution data and essentially upsample to the MNI template. in those cases, the various interpolation functions absolutely become relevant.