Warning: This document is for an old version of smriprep.

Source code for smriprep.workflows.fit.registration

# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
#
# Copyright 2021 The NiPreps Developers <nipreps@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# We support and encourage derived works from this project, please read
# about our expectations at
#
#     https://www.nipreps.org/community/licensing/
#
"""Spatial normalization workflows."""

from collections import defaultdict

from nipype.interfaces import ants
from nipype.interfaces import utility as niu
from nipype.interfaces.ants.base import Info as ANTsInfo
from nipype.pipeline import engine as pe
from niworkflows.engine.workflows import LiterateWorkflow as Workflow
from niworkflows.interfaces.norm import SpatialNormalization
from templateflow import __version__ as tf_ver
from templateflow.api import get_metadata

from ...interfaces.templateflow import TemplateDesc, TemplateFlowSelect


[docs] def init_register_template_wf( *, sloppy, omp_nthreads, templates, name='register_template_wf', ): """ Build an individual spatial normalization workflow using ``antsRegistration``. Workflow Graph .. workflow :: :graph2use: orig :simple_form: yes from smriprep.workflows.fit.registration import init_register_template_wf wf = init_register_template_wf( sloppy=False, omp_nthreads=1, templates=['MNI152NLin2009cAsym', 'MNI152NLin6Asym'], ) .. important:: This workflow defines an iterable input over the input parameter ``templates``, so Nipype will produce one copy of the downstream workflows which connect ``poutputnode.template`` or ``poutputnode.template_spec`` to their inputs (``poutputnode`` stands for *parametric output node*). Nipype refers to this expansion of the graph as *parameterized execution*. If a joint list of values is required (and thus cutting off parameterization), please use the equivalent outputs of ``outputnode`` (which *joins* all the parameterized execution paths). Parameters ---------- sloppy : :obj:`bool` Apply sloppy arguments to speed up processing. Use with caution, registration processes will be very inaccurate. omp_nthreads : :obj:`int` Maximum number of threads an individual process may use. templates : :obj:`list` of :obj:`str` List of standard space fullnames (e.g., ``MNI152NLin6Asym`` or ``MNIPediatricAsym:cohort-4``) which are targets for spatial normalization. Inputs ------ moving_image The input image that will be normalized to standard space. lesion_mask (optional) A mask to exclude regions from the cost-function input domain to enable standardization of lesioned brains. template Template name and specification Outputs ------- anat2std_xfm The T1w-to-template transform. std2anat_xfm The template-to-T1w transform. template Template name extracted from the input parameter ``template``, for further use in downstream nodes. template_spec Template specifications extracted from the input parameter ``template``, for further use in downstream nodes. """ ntpls = len(templates) workflow = Workflow(name=name) if templates: workflow.__desc__ = """\ Volume-based spatial normalization to {targets} ({targets_id}) was performed through nonlinear registration with `antsRegistration` (ANTs {ants_ver}), using brain-extracted versions of both T1w reference and the T1w template. The following template{tpls} were selected for spatial normalization and accessed with *TemplateFlow* [{tf_ver}, @templateflow]: """.format( ants_ver=ANTsInfo.version() or '(version unknown)', targets='{} standard space{}'.format( defaultdict('several'.format, {1: 'one', 2: 'two', 3: 'three', 4: 'four'})[ntpls], 's' * (ntpls != 1), ), targets_id=', '.join(templates), tf_ver=tf_ver, tpls=(' was', 's were')[ntpls != 1], ) # Append template citations to description for template in templates: template_meta = get_metadata(template.split(':')[0]) template_refs = ['@{}'.format(template.split(':')[0].lower())] if template_meta.get('RRID', None): template_refs += [f'RRID:{template_meta["RRID"]}'] workflow.__desc__ += """\ *{template_name}* [{template_refs}; TemplateFlow ID: {template}]""".format( template=template, template_name=template_meta['Name'], template_refs=', '.join(template_refs), ) workflow.__desc__ += '.\n' if template == templates[-1] else ', ' inputnode = pe.Node( niu.IdentityInterface( fields=[ 'lesion_mask', 'moving_image', 'moving_mask', 'template', ] ), name='inputnode', ) inputnode.iterables = [('template', templates)] out_fields = [ 'anat2std_xfm', 'std2anat_xfm', 'template', 'template_spec', ] outputnode = _make_outputnode(workflow, out_fields, joinsource='inputnode') split_desc = pe.Node(TemplateDesc(), run_without_submitting=True, name='split_desc') tf_select = pe.Node( TemplateFlowSelect(resolution=1 + sloppy), name='tf_select', run_without_submitting=True, ) # With the improvements from nipreps/niworkflows#342 this truncation is now necessary trunc_mov = pe.Node( ants.ImageMath(operation='TruncateImageIntensity', op2='0.01 0.999 256'), name='trunc_mov', ) registration = pe.Node( SpatialNormalization( float=True, flavor=['precise', 'testing'][sloppy], ), name='registration', n_procs=omp_nthreads, mem_gb=2, ) fmt_cohort = pe.Node( niu.Function(function=_fmt_cohort, output_names=['template', 'spec']), name='fmt_cohort', run_without_submitting=True, ) # fmt:off workflow.connect([ (inputnode, split_desc, [('template', 'template')]), (inputnode, trunc_mov, [('moving_image', 'op1')]), (inputnode, registration, [ ('moving_mask', 'moving_mask'), ('lesion_mask', 'lesion_mask')]), (split_desc, tf_select, [ ('name', 'template'), ('spec', 'template_spec'), ]), (split_desc, registration, [ ('name', 'template'), ('spec', 'template_spec'), ]), (trunc_mov, registration, [ ('output_image', 'moving_image')]), (split_desc, fmt_cohort, [ ('name', 'template'), ('spec', 'spec'), ]), (fmt_cohort, outputnode, [ ('template', 'template'), ('spec', 'template_spec'), ]), (registration, outputnode, [ ('composite_transform', 'anat2std_xfm'), ('inverse_composite_transform', 'std2anat_xfm'), ]), ]) # fmt:on return workflow
def _make_outputnode(workflow, out_fields, joinsource): if joinsource: pout = pe.Node(niu.IdentityInterface(fields=out_fields), name='poutputnode') out = pe.JoinNode( niu.IdentityInterface(fields=out_fields), name='outputnode', joinsource=joinsource ) workflow.connect([(pout, out, [(f, f) for f in out_fields])]) return pout return pe.Node(niu.IdentityInterface(fields=out_fields), name='outputnode') def _fmt_cohort(template, spec): cohort = spec.pop('cohort', None) if cohort is not None: template = f'{template}:cohort-{cohort}' return template, spec