Skip to content

Commit

Permalink
Merge pull request #198 from oesteban/fix/ReadSidecarJSON
Browse files Browse the repository at this point in the history
[RTM] Some love to sdc_unwarp
  • Loading branch information
chrisgorgo committed Dec 5, 2016
2 parents 43c25ec + efe10c8 commit 704678a
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 172 deletions.
4 changes: 2 additions & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ dependencies:
test:
override:
- docker run -ti --rm --entrypoint="/usr/bin/run_unittests" poldracklab/fmriprep:latest
- docker run -ti --rm -v /etc/localtime:/etc/localtime:ro -v $HOME/nipype.cfg:/root/.nipype/nipype.cfg:ro -v $HOME/data:/data:ro -v $HOME/ds054/scratch:/scratch -v $HOME/ds054/out:/out poldracklab/fmriprep:latest /data/ds054 /out/ participant -t ds054 --debug --write-graph -w /scratch:
- docker run -ti --rm -v /etc/localtime:/etc/localtime:ro -v $HOME/nipype.cfg:/root/.nipype/nipype.cfg:ro -v $HOME/data:/data:ro -v $HOME/ds054/scratch:/scratch -v $HOME/ds054/out:/out poldracklab/fmriprep:latest /data/ds054 /out/ participant -t ds054 --debug -w /scratch:
timeout: 4800
- docker run -ti --rm -v /etc/localtime:/etc/localtime:ro -v $HOME/nipype.cfg:/root/.nipype/nipype.cfg:ro -v $HOME/data:/data:ro -v $HOME/ds005/scratch:/scratch -v $HOME/ds005/out:/out poldracklab/fmriprep:latest /data/ds005 /out/ participant -t ds005 --debug --write-graph --no-skull-strip-ants -w /scratch:
- docker run -ti --rm -v /etc/localtime:/etc/localtime:ro -v $HOME/nipype.cfg:/root/.nipype/nipype.cfg:ro -v $HOME/data:/data:ro -v $HOME/ds005/scratch:/scratch -v $HOME/ds005/out:/out poldracklab/fmriprep:latest /data/ds005 /out/ participant -t ds005 --debug --no-skull-strip-ants -w /scratch:
timeout: 4800
- find ~/ds054/scratch -not -name "*.svg" -not -name "*.html" -not -name "*.svg" -not -name "*.rst" -type f -delete
- find ~/ds005/scratch -not -name "*.svg" -not -name "*.html" -not -name "*.svg" -not -name "*.rst" -type f -delete
Expand Down
44 changes: 30 additions & 14 deletions fmriprep/interfaces/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# @Author: oesteban
# @Date: 2016-06-03 09:35:13
# @Last Modified by: oesteban
# @Last Modified time: 2016-10-05 15:03:25
# @Last Modified time: 2016-12-02 17:31:40
import os
import os.path as op
import pkg_resources as pkgr
Expand All @@ -16,7 +16,7 @@

from nipype import logging
from nipype.interfaces.base import (
traits, isdefined, TraitedSpec, BaseInterface, BaseInterfaceInputSpec,
traits, isdefined, TraitedSpec, BaseInterface, BaseInterfaceInputSpec,
File, InputMultiPath, OutputMultiPath
)

Expand Down Expand Up @@ -158,38 +158,50 @@ class ReadSidecarJSONInputSpec(BaseInterfaceInputSpec):
in_file = File(exists=True, mandatory=True, desc='the input nifti file')
fields = traits.List(traits.Str, desc='get only certain fields')


class ReadSidecarJSONOutputSpec(TraitedSpec):
subject_id = traits.Str()
session_id = traits.Str()
task_id = traits.Str()
acq_id = traits.Str()
rec_id = traits.Str()
run_id = traits.Str()
out_dict = traits.Dict()


class ReadSidecarJSON(BaseInterface):
"""
An utility to find and read JSON sidecar files of a BIDS tree
"""

expr = re.compile('^sub-(?P<subject_id>[a-zA-Z0-9]+)(_ses-(?P<session_id>[a-zA-Z0-9]+))?'
'(_task-(?P<task_id>[a-zA-Z0-9]+))?(_acq-(?P<acq_id>[a-zA-Z0-9]+))?'
'(_rec-(?P<rec_id>[a-zA-Z0-9]+))?(_run-(?P<run_id>[a-zA-Z0-9]+))?')
input_spec = ReadSidecarJSONInputSpec
output_spec = ReadSidecarJSONOutputSpec

def __init__(self, **inputs):
self._results = {}
super(ReadSidecarJSON, self).__init__(**inputs)

def _list_outputs(self):
return self._results

def _run_interface(self, runtime):
metadata = get_metadata_for_nifti(self.inputs.in_file)
output_keys = [key for key in list(self.output_spec().get().keys()) if key.endswith('_id')]
outputs = self.expr.search(op.basename(self.inputs.in_file)).groupdict()

for key in output_keys:
id_value = outputs.get(key)
if id_value is not None:
self._results[key] = outputs.get(key)

if isdefined(self.inputs.fields) and self.inputs.fields:
for fname in self.inputs.fields:
self._results[fname] = metadata[fname]
else:
self._results = metadata
self._results['out_dict'] = metadata

return runtime

def _list_outputs(self):
out = self.output_spec().get()
out['out_dict'] = self._results
return out

def get_metadata_for_nifti(in_file):
"""Fetchs metadata for a given nifi file"""
Expand All @@ -201,7 +213,7 @@ def get_metadata_for_nifti(in_file):
ext = ext2 + ext

side_json = fname + '.json'
fname_comps = side_json.split('/')[-1].split("_")
fname_comps = op.basename(side_json).split("_")

session_comp_list = []
subject_comp_list = []
Expand All @@ -221,15 +233,19 @@ def get_metadata_for_nifti(in_file):
else:
top_comp_list.append(comp)

if any([comp.startswith('ses') for comp in fname_comps]):
bids_dir = '/'.join(op.dirname(in_file).split('/')[:-3])
else:
bids_dir = '/'.join(op.dirname(in_file).split('/')[:-2])

top_json = "/" + "_".join(top_comp_list)
top_json = op.join(bids_dir, "_".join(top_comp_list))
potential_json = [top_json]

subject_json = "/" + sub + "/" + "_".join(subject_comp_list)
subject_json = op.join(bids_dir, sub, "_".join(subject_comp_list))
potential_json.append(subject_json)

if ses:
session_json = "/" + sub + "/" + ses + "/" + "_".join(session_comp_list)
session_json = op.join(bids_dir, sub, ses, "_".join(session_comp_list))
potential_json.append(session_json)

potential_json.append(side_json)
Expand Down
2 changes: 1 addition & 1 deletion fmriprep/workflows/fieldmap/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
Fieldmap-processing workflows.
"""
from fmriprep.workflows.fieldmap.base import create_encoding_file, mcflirt2topup
from fmriprep.workflows.fieldmap.utils import create_encoding_file, mcflirt2topup
from fmriprep.workflows.fieldmap.phase_diff_and_magnitudes import phase_diff_and_magnitudes
from fmriprep.workflows.fieldmap.unwarp import sdc_unwarp
82 changes: 17 additions & 65 deletions fmriprep/workflows/fieldmap/base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
"""
Base fieldmap handling utilities.
Possible fieldmap specifications according to BIDS 1.0.0
--------------------------------------------------------
8.9.1 one phase diff image, at least one magnitude image
8.9.2 two phase images, two magnitude images
8.9.3 fieldmap image (and one magnitude image)
8.9.4 multiple phase encoded directions (topup)
"""
from __future__ import print_function, division, absolute_import, unicode_literals
from future.utils import raise_from

from nipype import logging

LOGGER = logging.getLogger('workflow')


def is_fmap_type(fmap_type, filename):
from fmriprep.utils import misc
import re
Expand All @@ -25,13 +41,6 @@ def sort_fmaps(fieldmaps): # i.e. filenames
def fieldmap_decider(fieldmap_data, settings):
''' Run fieldmap_decider to automatically find a
Fieldmap preprocessing workflow '''

# POSSIBLE FILES ACCORDING TO BIDS 1.0.0
# 8.9.1 one phase diff image, at least one magnitude image
# 8.9.2 two phase images, two magnitude images
# 8.9.3 fieldmap image (and one magnitude image)
# 8.9.4 multiple phase encoded directions (topup)

# inputs = { 'fieldmaps': None }
# outputs = { 'fieldmaps': None,
# 'outputnode.mag_brain': None,
Expand All @@ -58,60 +67,3 @@ def fieldmap_decider(fieldmap_data, settings):
return se_fmap_workflow(settings=settings)

raise IOError("Unrecognized fieldmap structure")


def create_encoding_file(input_images, in_dict):
"""Creates a valid encoding file for topup"""
import json
import nibabel as nb
import numpy as np
import os

if not isinstance(input_images, list):
input_images = [input_images]
if not isinstance(in_dict, list):
in_dict = [in_dict]

pe_dirs = {'i': 0, 'j': 1, 'k': 2}
enc_table = []
for fmap, meta in zip(input_images, in_dict):
line_values = [0, 0, 0, meta['TotalReadoutTime']]
line_values[pe_dirs[meta['PhaseEncodingDirection'][0]]] = 1 + (
-2*(len(meta['PhaseEncodingDirection']) == 2))

nvols = 1
if len(nb.load(fmap).shape) > 3:
nvols = nb.load(fmap).shape[3]

enc_table += [line_values] * nvols

np.savetxt(os.path.abspath('parameters.txt'), enc_table, fmt=['%0.1f', '%0.1f', '%0.1f', '%0.20f'])
return os.path.abspath('parameters.txt')


def mcflirt2topup(in_files, in_mats, out_movpar=None):
"""
Converts a list of matrices from MCFLIRT to the movpar input
of TOPUP (a row per file with 6 parameters - 3 translations and 3 rotations
in this particular order).
"""

import os.path as op
import numpy as np
params = np.zeros((len(in_files), 6))

if in_mats:
if len(in_mats) != len(in_files):
raise RuntimeError('Number of input matrices and files do not match')
else:
raise NotImplementedError

if out_movpar is None:
fname, fext = op.splitext(op.basename(in_files[0]))
if fext == '.gz':
fname, _ = op.splitext(fname)
out_movpar = op.abspath('./%s_movpar.txt' % fname)

np.savetxt(out_movpar, params)
return out_movpar
30 changes: 22 additions & 8 deletions fmriprep/workflows/fieldmap/phase_diff_and_magnitudes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from __future__ import division
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
"""
Fieldmap preprocessing workflow for fieldmap data structure
8.9.1 in BIDS 1.0.0: one phase diff and at least one magnitude image
"""
from __future__ import print_function, division, absolute_import, unicode_literals

import logging
import os.path as op
Expand All @@ -16,9 +25,6 @@
from fmriprep.utils.misc import fieldmap_suffixes
from fmriprep.viz import stripped_brain_overlay

''' Fieldmap preprocessing workflow for fieldmap data structure
8.9.1 in BIDS 1.0.0: one phase diff and at least one magnitude image'''


def _sort_fmaps(input_images):
''' just a little data massaging'''
Expand Down Expand Up @@ -56,7 +62,7 @@ def _pick1st(inlist):
return inlist[0]

# Read phasediff echo times
meta = pe.Node(ReadSidecarJSON(fields=['EchoTime1', 'EchoTime2']), name='metadata')
meta = pe.Node(ReadSidecarJSON(), name='metadata')
dte = pe.Node(niu.Function(input_names=['in_values'], output_names=['delta_te'],
function=_delta_te), name='ComputeDeltaTE')

Expand Down Expand Up @@ -199,14 +205,18 @@ def phdiff2fmap(in_file, delta_te, out_file=None):
return out_file


def _delta_te(in_values):
def _delta_te(in_values, te1=None, te2=None):
if isinstance(in_values, float):
te2 = in_values
te1 = 0.

if isinstance(in_values, dict):
te1 = in_values['EchoTime1']
te2 = in_values['EchoTime2']
te1 = in_values.get('EchoTime1')
te2 = in_values.get('EchoTime2')

if not all((te1, te2)):
te2 = in_values.get('EchoTimeDifference')
te1 = 0

if isinstance(in_values, list):
te2, te1 = in_values
Expand All @@ -215,4 +225,8 @@ def _delta_te(in_values):
if isinstance(te2, list):
te2 = te2[1]

if te1 is None or te2 is None:
raise RuntimeError(
'No echo time information found')

return abs(float(te2)-float(te1))
2 changes: 1 addition & 1 deletion fmriprep/workflows/fieldmap/se_fmap_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from fmriprep.utils.misc import _first, gen_list
from fmriprep.interfaces import ImageDataSink, ReadSidecarJSON
from fmriprep.viz import stripped_brain_overlay
from fmriprep.workflows.fieldmap.base import create_encoding_file
from fmriprep.workflows.fieldmap.utils import create_encoding_file

WORKFLOW_NAME = 'Fieldmap_SEs'

Expand Down

0 comments on commit 704678a

Please sign in to comment.