Skip to content
24 changes: 22 additions & 2 deletions niworkflows/anat/ants.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ def init_atropos_wf(
padding=10,
in_segmentation_model=tuple(ATROPOS_MODELS["T1w"].values()),
bspline_fitting_distance=200,
adaptive_bspline_grid=False,
n4_iter=5,
wm_prior=False,
):
"""
Expand Down Expand Up @@ -556,6 +558,12 @@ def init_atropos_wf(
``(4,4,2,3)`` uses K=4, CSF=4, GM=2, WM=3.
bspline_fitting_distance : float
The size of the b-spline mesh grid elements, in mm (default: 200)
adaptive_bspline_grid : :obj:`bool`
If true, defines the number of B-Spline mesh grid elements in each dimension rather
than using the isotropic distance given in ``bspline_fitting_distance``.
n4_iter : :obj:`int`
The number of B-Spline fitting iterations (default: 5). Fewer (e.g. 4) are recommended
for rodents and other non-human/non-adult cases.
wm_prior : :obj:`bool`
Whether the WM posterior obtained with ATROPOS should be regularized with a prior
map (typically, mapped from the template). When ``wm_prior`` is ``True`` the input
Expand Down Expand Up @@ -755,7 +763,7 @@ def init_atropos_wf(
dimension=3,
save_bias=True,
copy_header=True,
n_iterations=[50] * 5,
n_iterations=[50]*n4_iter,
convergence_threshold=1e-7,
shrink_factor=4,
bspline_fitting_distance=bspline_fitting_distance,
Expand Down Expand Up @@ -875,6 +883,19 @@ def _argmax(in_dice):
(apply_wm_prior, inu_n4_final, [("out", "weight_image")]),
])
# fmt: on

if adaptive_bspline_grid:
# set INU bspline grid based on image shape
from ..utils.images import _bspline_grid
bspline_grid = pe.Node(niu.Function(function=_bspline_grid), name="bspline_grid")

# fmt:off
wf.connect([
(inputnode, bspline_grid, [(("in_files", _pop), "in_file")]),
(bspline_grid, inu_n4_final, [("out", "args")])
])
# fmt:on

return wf


Expand Down Expand Up @@ -1045,7 +1066,6 @@ def init_n4_only_wf(
]),
])
# fmt: on

return wf


Expand Down
22 changes: 22 additions & 0 deletions niworkflows/utils/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,25 @@ def nii_ones_like(in_file, value, dtype, newpath=None):
nii.to_filename(out_file)

return out_file


def _bspline_grid(in_file):
"""
Estimate B-Spline fitting distance grid using the number of slices of ``in_file``.

Using slice number to determine grid sparsity is inspired by the conversation found at
https://itk.org/pipermail/community/2014-February/005036.html

"""
import nibabel as nb
import numpy as np
import math

img = nb.load(in_file)
zooms = img.header.get_zooms()[:3]
# find extent of each dimension wrt voxel sizes
extent = np.array(img.shape[:3]) * zooms
# convert ratio to integers
retval = [f"{math.ceil(i / extent[np.argmin(extent)])}" for i in extent]
# return string for command line call
return f"-b [{'x'.join(retval)}]"
25 changes: 24 additions & 1 deletion niworkflows/workflows/epi/refmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
def init_epi_reference_wf(
omp_nthreads,
auto_bold_nss=False,
adaptive_bspline_grid=False,
n4_iter=5,
name="epi_reference_wf",
):
"""
Expand Down Expand Up @@ -84,6 +86,12 @@ def init_epi_reference_wf(
If ``True``, determines nonsteady states in the beginning of the timeseries
and selects them for the averaging of each run.
IMPORTANT: this option applies only to BOLD EPIs.
adaptive_bspline_grid : :obj:`bool`
If ``True``, determines the number of B-Spline grid elements from data shape
and feeds them into N4BiasFieldCorrection, rather than setting an isotropic distance.
n4_iter : :obj:`int`
The number of B-Spline fitting iterations (default: 5). Fewer (e.g. 4) are recommended
for rodents and other non-human/non-adult cases.

Inputs
------
Expand Down Expand Up @@ -158,14 +166,15 @@ def init_epi_reference_wf(
N4BiasFieldCorrection(
dimension=3,
copy_header=True,
n_iterations=[50] * 5,
n_iterations=[50]*n4_iter,
convergence_threshold=1e-7,
shrink_factor=4,
),
n_procs=omp_nthreads,
name="n4_avgs",
iterfield=["input_image"],
)

clip_bg_noise = pe.MapNode(
IntensityClip(p_min=2.0, p_max=100.0),
name="clip_bg_noise",
Expand Down Expand Up @@ -225,6 +234,20 @@ def _set_threads(in_list, maximum):
else:
wf.connect(inputnode, "t_masks", per_run_avgs, "t_mask")

# rodent-specific N4 settings
if adaptive_bspline_grid:
from ...utils.images import _bspline_grid
from ...utils.connections import pop_file as _pop
# set INU bspline grid based on voxel size
bspline_grid = pe.Node(niu.Function(function=_bspline_grid), name="bspline_grid")

# fmt:off
wf.connect([
(clip_avgs, bspline_grid, [(("out_file", _pop), "in_file")]),
(bspline_grid, n4_avgs, [("out", "args")])
])
# fmt:on

return wf


Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ install_requires =
matplotlib >= 2.2.0
nibabel >= 3.0.1
nilearn >= 0.2.6, != 0.5.0, != 0.5.1
nipype >= 1.5.1
nipype @ git+https://github.com/nipy/nipype.git@master
nitransforms >= 20.0.0rc3,<20.2
numpy
packaging
Expand Down