Skip to content

Commit

Permalink
♻️ Replace CopyImageHeaderInformation with PrintHeader + `SetDire…
Browse files Browse the repository at this point in the history
  • Loading branch information
shnizzedy committed Apr 28, 2022
1 parent 1d39271 commit cec1b77
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 88 deletions.
3 changes: 3 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
per-file-ignores =
CPAC/func_preproc/func_preproc.py:E402
32 changes: 14 additions & 18 deletions CPAC/func_preproc/func_preproc.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
# pylint: disable=ungrouped-imports,wrong-import-order,wrong-import-position
from nipype import logging
from nipype.interfaces import ants

from nipype.interfaces import afni, ants, fsl, utility as util
logger = logging.getLogger('nipype.workflow')

from CPAC.pipeline import nipype_pipeline_engine as pe
import nipype.interfaces.fsl as fsl
import nipype.interfaces.utility as util
from nipype.interfaces import afni
from nipype.interfaces.afni import preprocess
from nipype.interfaces.afni import utils as afni_utils

from CPAC.func_preproc.utils import add_afni_prefix, nullify, chunk_ts, \
split_ts_chunks, oned_text_concat, notch_filter_motion
from CPAC.utils.interfaces.function import Function
from CPAC.generate_motion_statistics import motion_power_statistics

from CPAC.utils.interfaces.ants import AI # niworkflows
from CPAC.utils.interfaces.ants import PrintHeader, SetDirectionByMatrix
from CPAC.utils.interfaces.function import Function
from CPAC.utils.utils import check_prov_for_motion_tool

# niworkflows
from ..utils.interfaces.ants import AI, \
CopyImageHeaderInformation # noqa: E402


def collect_arguments(*args):
command_args = []
Expand Down Expand Up @@ -1667,8 +1661,10 @@ def bold_mask_fsl_afni(wf, cfg, strat_pool, pipe_num, opt=None):

# Fix precision errors
# https://github.com/ANTsX/ANTs/wiki/Inputs-do-not-occupy-the-same-physical-space#fixing-precision-errors
restore_header = pe.Node(CopyImageHeaderInformation(),
name=f'restore_header_{pipe_num}')
print_header = pe.Node(PrintHeader(what_information=4),
name=f'print_header_{pipe_num}')
set_direction = pe.Node(SetDirectionByMatrix(),
name=f'set_direction_{pipe_num}')

# Run N4 normally, force num_threads=1 for stability (images are
# small, no need for >1)
Expand Down Expand Up @@ -1714,7 +1710,6 @@ def bold_mask_fsl_afni(wf, cfg, strat_pool, pipe_num, opt=None):
wf.connect([(node, init_aff, [(out, "moving_image")]),
(node, map_brainmask, [(out, "reference_image")]),
(node, norm, [(out, "moving_image")]),
(node, restore_header, [(out, "refimage")]),
(init_aff, norm, [
("output_transform", "initial_moving_transform")]),
(norm, map_brainmask, [
Expand All @@ -1723,9 +1718,10 @@ def bold_mask_fsl_afni(wf, cfg, strat_pool, pipe_num, opt=None):
]),
(map_brainmask, binarize_mask, [("output_image", "in_file")]),
(binarize_mask, pre_dilate, [("out_file", "in_file")]),
(pre_dilate, restore_header, [
("out_file", "imagetocopyrefimageinfoto")]),
(restore_header, n4_correct, [("imageout", "mask_image")]),
(pre_dilate, print_header, [("out_file", "image")]),
(print_header, set_direction, [("header", "direction")]),
(node, set_direction, [(out, "infile"), (out, "outfile")]),
(set_direction, n4_correct, [("outfile", "mask_image")]),
(node, n4_correct, [(out, "input_image")]),
(n4_correct, skullstrip_first_pass,
[('output_image', 'in_file')]),
Expand Down
142 changes: 72 additions & 70 deletions CPAC/utils/interfaces/ants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,81 +11,12 @@
# https://fmriprep.readthedocs.io/
# https://poldracklab.stanford.edu/
# We are temporarily maintaining our own copy for more granular control.

import os
from glob import glob
from nipype.interfaces import base
from nipype.interfaces.ants.base import ANTSCommandInputSpec, ANTSCommand
from nipype.interfaces.base import traits, isdefined


class CopyImageHeaderInformationInputSpec(ANTSCommandInputSpec):
"""InputSpec for ``CopyImageHeaderInformation``.
``imagetocopyrefimageinfoto`` is also used for ``imageout`` if
``imageout`` is not specified.
"""
def __setattr__(self, name, value):
super(ANTSCommandInputSpec, self).__setattr__(name, value)
if name == 'imagetocopyrefimageinfoto':
self._infer_imageout()

def _infer_imageout(self):
if (self.imageout == base.Undefined):
self.trait_set(imageout=self.imagetocopyrefimageinfoto)

refimage = base.File(position=1, argstr='%s', name_source=['refimage'],
desc='reference image (with header to copy from)',
exists=True, mandatory=True)

imagetocopyrefimageinfoto = base.File(position=2, argstr='%s',
name_source=[
'imagetocopyrefimageinfoto'
], desc='image to copy header to',
exists=True, mandatory=True)

imageout = base.File(position=3, argstr='%s', name_sources=[
'imagetocopyrefimageinfoto',
'imageout'
],
desc='output image file', usedefault=True)

boolcopydirection = traits.Bool(True, argstr='%d', position=4,
desc='copy direction?', usedefault=True)

boolcopyorigin = traits.Bool(True, argstr='%d', position=5,
desc='copy origin?', usedefault=True)

boolcopyspacing = traits.Bool(True, argstr='%d', position=6,
desc='copy spacing?', usedefault=True)


class CopyImageHeaderInformationOutputSpec(base.TraitedSpec):
"""OutputSpec for CopyImageHeaderInformation"""
imageout = base.File(exists=True, desc='output image file')


class CopyImageHeaderInformation(ANTSCommand):
"""Copy image header information from one file to another,
optionally as a copy.
Examples
--------
>>> CopyImageHeaderInformation(
... refimage='/cpac_templates/MacaqueYerkes19_T1w_2mm_brain.nii.gz',
... imagetocopyrefimageinfoto='/cpac_templates/MacaqueYerkes19_'
... 'T1w_2mm_brain_mask.nii.gz'
... ).cmdline
'CopyImageHeaderInformation /cpac_templates/MacaqueYerkes19_T1w_2mm_brain.nii.gz /cpac_templates/MacaqueYerkes19_T1w_2mm_brain_mask.nii.gz /cpac_templates/MacaqueYerkes19_T1w_2mm_brain_mask.nii.gz 1 1 1'
""" # noqa: E501
_cmd = 'CopyImageHeaderInformation'
input_spec = CopyImageHeaderInformationInputSpec
output_spec = CopyImageHeaderInformationOutputSpec

def _list_outputs(self):
return {'imageout': self.inputs.imageout}


class ImageMathInputSpec(ANTSCommandInputSpec):
dimension = traits.Int(3, usedefault=True, position=1, argstr='%d',
desc='dimension of output image')
Expand All @@ -112,12 +43,49 @@ class ImageMath(ANTSCommand):
--------
"""

_cmd = 'ImageMath'
input_spec = ImageMathInputSpec
output_spec = ImageMathOuputSpec


class PrintHeaderInputSpec(ANTSCommandInputSpec):
"""InputSpec for ``PrintHeader``.
See `PrintHeader: DESCRIPTION <https://manpages.debian.org/testing/ants/PrintHeader.1.en.html#DESCRIPTION>`_ for ``what_information`` values.
""" # noqa: E501 # pylint: disable=line-too-long
image = base.File(position=2, argstr='%s', name_source=['image'],
desc='image to read header from', exists=True,
mandatory=True)

what_information = traits.Int(position=3, argstr='%i',
name='what_information',
desc='read what from header')


class PrintHeaderOutputSpec(base.TraitedSpec):
"""OutputSpec for ``PrintHeader``."""
header = traits.String(name='header')


class PrintHeader(ANTSCommand):
"""Print image header information."""
_cmd = 'PrintHeader'
# pylint: disable=protected-access
_gen_filename = base.StdOutCommandLine._gen_filename
input_spec = PrintHeaderInputSpec
output_spec = PrintHeaderOutputSpec
_terminal_output = 'stream'

def aggregate_outputs(self, runtime=None, needed_outputs=None):
outputs = super().aggregate_outputs(runtime, needed_outputs)
outputs.trait_set(header=runtime.stdout)
self.output_spec().trait_set(header=runtime.stdout)
return outputs

def _list_outputs(self):
return self._outputs().get()


class ResampleImageBySpacingInputSpec(ANTSCommandInputSpec):
dimension = traits.Int(3, usedefault=True, position=1, argstr='%d',
desc='dimension of output image')
Expand Down Expand Up @@ -189,6 +157,40 @@ def _format_arg(self, name, trait_spec, value):
name, trait_spec, value)


class SetDirectionByMatrixInputSpec(ANTSCommandInputSpec):
"""InputSpec for ``SetDirectionByMatrix``."""
infile = base.File(position=2, argstr='%s', name_source=['infile'],
desc='image to copy header to', exists=True,
mandatory=True)
outfile = base.File(position=3, argstr='%s',
name_sources=['infile', 'outfile'],
desc='output image file', usedefault=True)
direction = traits.String(argstr='%s', position=4,
desc='dimensions, x-delimited')


class SetDirectionByMatrixOutputSpec(base.TraitedSpec):
"""OutputSpec for ``SetDirectionByMatrix``"""
outfile = base.File(exists=True, desc='output image file')


class SetDirectionByMatrix(ANTSCommand):
"""Set image header information from a matrix of dimensions."""
_cmd = 'SetDirectionByMatrix'
# pylint: disable=protected-access
_gen_filename = base.StdOutCommandLine._gen_filename
input_spec = SetDirectionByMatrixInputSpec
output_spec = SetDirectionByMatrixOutputSpec

def _format_arg(self, name, trait_spec, value):
if name == 'direction':
return value.replace('x', ' ')
return super()._format_arg(name, trait_spec, value)

def _list_outputs(self):
return {'outfile': self.inputs.outfile}


class ThresholdImageInputSpec(ANTSCommandInputSpec):
dimension = traits.Int(3, usedefault=True, position=1, argstr='%d',
desc='dimension of output image')
Expand Down

0 comments on commit cec1b77

Please sign in to comment.