Skip to content

Commit

Permalink
Add HMC dev from sprint 2020
Browse files Browse the repository at this point in the history
Rename to EMC (Eddy Motion Correction) and fix several workflow bugs
  • Loading branch information
dPys committed Jan 19, 2020
1 parent 47fe0e9 commit 45a16c2
Show file tree
Hide file tree
Showing 15 changed files with 1,608 additions and 70 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ include dmriprep/_version.py

# data
include dmriprep/config/reports-spec.yml
recursive-include * *.json
25 changes: 25 additions & 0 deletions dmriprep/config/emc_coarse_Affine.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"dimension": 3,
"float": true,
"winsorize_lower_quantile": 0.002,
"winsorize_upper_quantile": 0.998,
"collapse_output_transforms": true,
"write_composite_transform": false,
"use_histogram_matching": [ false, false ],
"use_estimate_learning_rate_once": [ true, true ],
"transforms": [ "Rigid", "Affine" ],
"number_of_iterations": [ [ 100, 100 ], [ 100 ] ],
"output_warped_image": true,
"transform_parameters": [ [ 0.2 ], [ 0.15 ] ],
"convergence_threshold": [ 1e-06, 1e-06 ],
"convergence_window_size": [ 20, 20 ],
"metric": [ "Mattes", "Mattes" ],
"sampling_percentage": [ 0.15, 0.2 ],
"sampling_strategy": [ "Random", "Random" ],
"smoothing_sigmas": [ [ 8.0, 2.0 ], [ 2.0 ] ],
"sigma_units": [ "mm", "mm" ],
"metric_weight": [ 1.0, 1.0 ],
"shrink_factors": [ [ 2, 1 ], [ 1 ] ],
"radius_or_number_of_bins": [ 48, 48 ],
"interpolation": "BSpline"
}
25 changes: 25 additions & 0 deletions dmriprep/config/emc_coarse_Rigid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"dimension": 3,
"float": true,
"winsorize_lower_quantile": 0.002,
"winsorize_upper_quantile": 0.998,
"collapse_output_transforms": true,
"write_composite_transform": false,
"use_histogram_matching": [ false ],
"use_estimate_learning_rate_once": [ true ],
"transforms": [ "Rigid" ],
"number_of_iterations": [ [ 100, 100 ] ],
"output_warped_image": true,
"transform_parameters": [ [ 0.2 ] ],
"convergence_threshold": [ 1e-06 ],
"convergence_window_size": [ 20 ],
"metric": [ "Mattes" ],
"sampling_percentage": [ 0.15 ],
"sampling_strategy": [ "Random" ],
"smoothing_sigmas": [ [ 8.0, 2.0 ] ],
"sigma_units": [ "mm"],
"metric_weight": [ 1.0 ],
"shrink_factors": [ [ 2, 1 ] ],
"radius_or_number_of_bins": [ 48 ],
"interpolation": "BSpline"
}
25 changes: 25 additions & 0 deletions dmriprep/config/emc_precise_Affine.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"dimension": 3,
"float": true,
"winsorize_lower_quantile": 0.002,
"winsorize_upper_quantile": 0.998,
"collapse_output_transforms": true,
"write_composite_transform": false,
"use_histogram_matching": [ false, false ],
"use_estimate_learning_rate_once": [ true, true ],
"transforms": [ "Rigid", "Affine" ],
"number_of_iterations": [ [ 1000, 1000 ], [ 1000 ] ],
"output_warped_image": true,
"transform_parameters": [ [ 0.2 ], [ 0.15 ] ],
"convergence_threshold": [ 1e-08, 1e-08 ],
"convergence_window_size": [ 20, 20 ],
"metric": [ "Mattes", "Mattes" ],
"sampling_percentage": [ 0.15, 0.2 ],
"sampling_strategy": [ "Random", "Random" ],
"smoothing_sigmas": [ [ 8.0, 2.0 ], [ 2.0 ] ],
"sigma_units": [ "mm", "mm" ],
"metric_weight": [ 1.0, 1.0 ],
"shrink_factors": [ [ 2, 1 ], [ 1 ] ],
"radius_or_number_of_bins": [ 48, 48 ],
"interpolation": "BSpline"
}
25 changes: 25 additions & 0 deletions dmriprep/config/emc_precise_Rigid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"dimension": 3,
"float": true,
"winsorize_lower_quantile": 0.002,
"winsorize_upper_quantile": 0.998,
"collapse_output_transforms": true,
"write_composite_transform": false,
"use_histogram_matching": [ false ],
"use_estimate_learning_rate_once": [ true ],
"transforms": [ "Rigid" ],
"number_of_iterations": [ [ 1000, 1000 ] ],
"output_warped_image": true,
"transform_parameters": [ [ 0.2 ], [ 0.15 ] ],
"convergence_threshold": [ 1e-08, 1e-08 ],
"convergence_window_size": [ 20, 20 ],
"metric": [ "Mattes" ],
"sampling_percentage": [ 0.15 ],
"sampling_strategy": [ "Random" ],
"smoothing_sigmas": [ [ 8.0, 2.0 ] ],
"sigma_units": [ "mm" ],
"metric_weight": [ 1.0 ],
"shrink_factors": [ [ 2, 1 ] ],
"radius_or_number_of_bins": [ 48 ],
"interpolation": "BSpline"
}
139 changes: 139 additions & 0 deletions dmriprep/interfaces/bids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import os
import re
from pathlib import Path
from nipype.interfaces.base import (
traits, TraitedSpec, BaseInterfaceInputSpec, SimpleInterface, File, isdefined,
InputMultiObject, OutputMultiPath, OutputMultiObject,
)
from ..utils.bids import splitext as _splitext, _copy_any

__all__ = ['BIDS_NAME']

BIDS_NAME = re.compile(
r'^(.*\/)?(?P<subject_id>sub-[a-zA-Z0-9]+)(_(?P<session_id>ses-[a-zA-Z0-9]+))?'
'(_(?P<task_id>task-[a-zA-Z0-9]+))?(_(?P<acq_id>acq-[a-zA-Z0-9]+))?'
'(_(?P<rec_id>rec-[a-zA-Z0-9]+))?(_(?P<run_id>run-[a-zA-Z0-9]+))?')


class DerivativesDataSinkInputSpec(BaseInterfaceInputSpec):
base_directory = traits.Directory(
desc='Path to the base directory for storing data.')
in_file = InputMultiObject(File(exists=True), mandatory=True,
desc='the object to be saved')
source_file = File(exists=False, mandatory=True, desc='the original file')
prefix = traits.Str(mandatory=False, desc='prefix for output files')
space = traits.Str('', usedefault=True, desc='Label for space field')
desc = traits.Str('', usedefault=True, desc='Label for description field')
suffix = traits.Str('', usedefault=True, desc='suffix appended to source_file')
keep_dtype = traits.Bool(False, usedefault=True, desc='keep datatype suffix')
extra_values = traits.List(traits.Str)
compress = traits.Bool(desc="force compression (True) or uncompression (False)"
" of the output file (default: same as input)")
extension = traits.Str()


class DerivativesDataSinkOutputSpec(TraitedSpec):
out_file = OutputMultiObject(File(exists=True, desc='written file path'))
compression = OutputMultiPath(
traits.Bool, desc='whether ``in_file`` was compressed/uncompressed '
'or `it was copied directly.')


class DerivativesDataSink(SimpleInterface):
"""
Saves the `in_file` into a BIDS-Derivatives folder provided
by `base_directory`, given the input reference `source_file`.
>>> from pathlib import Path
>>> import tempfile
>>> from dmriprep.utils.bids import collect_data
>>> tmpdir = Path(tempfile.mkdtemp())
>>> tmpfile = tmpdir / 'a_temp_file.nii.gz'
>>> tmpfile.open('w').close() # "touch" the file
>>> dsink = DerivativesDataSink(base_directory=str(tmpdir))
>>> dsink.inputs.in_file = str(tmpfile)
>>> dsink.inputs.source_file = collect_data('ds114', '01')[0]['t1w'][0]
>>> dsink.inputs.keep_dtype = True
>>> dsink.inputs.suffix = 'target-mni'
>>> res = dsink.run()
>>> res.outputs.out_file # doctest: +ELLIPSIS
'.../dmriprep/sub-01/ses-retest/anat/sub-01_ses-retest_target-mni_T1w.nii.gz'
>>> bids_dir = tmpdir / 'bidsroot' / 'sub-02' / 'ses-noanat' / 'func'
>>> bids_dir.mkdir(parents=True, exist_ok=True)
>>> tricky_source = bids_dir / 'sub-02_ses-noanat_task-rest_run-01_bold.nii.gz'
>>> tricky_source.open('w').close()
>>> dsink = DerivativesDataSink(base_directory=str(tmpdir))
>>> dsink.inputs.in_file = str(tmpfile)
>>> dsink.inputs.source_file = str(tricky_source)
>>> dsink.inputs.keep_dtype = True
>>> dsink.inputs.desc = 'preproc'
>>> res = dsink.run()
>>> res.outputs.out_file # doctest: +ELLIPSIS
'.../dmriprep/sub-02/ses-noanat/func/sub-02_ses-noanat_task-rest_run-01_\
desc-preproc_bold.nii.gz'
"""
input_spec = DerivativesDataSinkInputSpec
output_spec = DerivativesDataSinkOutputSpec
out_path_base = "dmriprep"
_always_run = True

def __init__(self, out_path_base=None, **inputs):
super(DerivativesDataSink, self).__init__(**inputs)
self._results['out_file'] = []
if out_path_base:
self.out_path_base = out_path_base

def _run_interface(self, runtime):
src_fname, _ = _splitext(self.inputs.source_file)
src_fname, dtype = src_fname.rsplit('_', 1)
_, ext = _splitext(self.inputs.in_file[0])
if self.inputs.compress is True and not ext.endswith('.gz'):
ext += '.gz'
elif self.inputs.compress is False and ext.endswith('.gz'):
ext = ext[:-3]

m = BIDS_NAME.search(src_fname)

mod = os.path.basename(os.path.dirname(self.inputs.source_file))

base_directory = runtime.cwd
if isdefined(self.inputs.base_directory):
base_directory = str(self.inputs.base_directory)

out_path = '{}/{subject_id}'.format(self.out_path_base, **m.groupdict())
if m.groupdict().get('session_id') is not None:
out_path += '/{session_id}'.format(**m.groupdict())
out_path += '/{}'.format(mod)

out_path = os.path.join(base_directory, out_path)

os.makedirs(out_path, exist_ok=True)

if isdefined(self.inputs.prefix):
base_fname = os.path.join(out_path, self.inputs.prefix)
else:
base_fname = os.path.join(out_path, src_fname)

formatstr = '{bname}{space}{desc}{suffix}{dtype}{ext}'
if len(self.inputs.in_file) > 1 and not isdefined(self.inputs.extra_values):
formatstr = '{bname}{space}{desc}{suffix}{i:04d}{dtype}{ext}'

space = '_space-{}'.format(self.inputs.space) if self.inputs.space else ''
desc = '_desc-{}'.format(self.inputs.desc) if self.inputs.desc else ''
suffix = '_{}'.format(self.inputs.suffix) if self.inputs.suffix else ''
dtype = '' if not self.inputs.keep_dtype else ('_%s' % dtype)

self._results['compression'] = []
for i, fname in enumerate(self.inputs.in_file):
out_file = formatstr.format(
bname=base_fname,
space=space,
desc=desc,
suffix=suffix,
i=i,
dtype=dtype,
ext=ext)
if isdefined(self.inputs.extra_values):
out_file = out_file.format(extra_value=self.inputs.extra_values[i])
self._results['out_file'].append(out_file)
self._results['compression'].append(_copy_any(fname, out_file))
return runtime
Loading

0 comments on commit 45a16c2

Please sign in to comment.