Skip to content

Commit

Permalink
Merge pull request #387 from tsalo/mbme-reproin
Browse files Browse the repository at this point in the history
ENH: Support SBRef and phase data in ReproIn heuristic
  • Loading branch information
yarikoptic authored Mar 27, 2020
2 parents 09f7501 + dce8e23 commit 74d9140
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 39 deletions.
31 changes: 19 additions & 12 deletions heudiconv/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@ def save_converted_files(res, item_dicoms, bids_options, outtype, prefix, outnam
bids_files = (sorted(res.outputs.bids)
if len(res.outputs.bids) == len(res_files)
else [None] * len(res_files))
# preload since will be used in multiple spots
bids_metas = [load_json(b) for b in bids_files if b]

### Do we have a multi-echo series? ###
# Some Siemens sequences (e.g. CMRR's MB-EPI) set the label 'TE1',
Expand All @@ -531,19 +533,17 @@ def save_converted_files(res, item_dicoms, bids_options, outtype, prefix, outnam

# Check for varying echo times
echo_times = sorted(list(set(
load_json(b).get('EchoTime', nan)
for b in bids_files
b.get('EchoTime', nan)
for b in bids_metas
if b
)))

is_multiecho = len(echo_times) > 1

### Loop through the bids_files, set the output name and save files
for fl, suffix, bids_file in zip(res_files, suffixes, bids_files):
for fl, suffix, bids_file, bids_meta in zip(res_files, suffixes, bids_files, bids_metas):

# TODO: monitor conversion duration
if bids_file:
fileinfo = load_json(bids_file)

# set the prefix basename for this specific file (we'll modify it,
# and we don't want to modify it for all the bids_files):
Expand All @@ -552,11 +552,18 @@ def save_converted_files(res, item_dicoms, bids_options, outtype, prefix, outnam
# _sbref sequences reconstructing magnitude and phase generate
# two NIfTI files IN THE SAME SERIES, so we cannot just add
# the suffix, if we want to be bids compliant:
if bids_file and this_prefix_basename.endswith('_sbref'):
if bids_meta and this_prefix_basename.endswith('_sbref') \
and len(suffixes) > len(echo_times):
if len(suffixes) != len(echo_times)*2:
lgr.warning(
"Got %d suffixes for %d echo times, which isn't "
"multiple of two as if it was magnitude + phase pairs",
len(suffixes), len(echo_times)
)
# Check to see if it is magnitude or phase reconstruction:
if 'M' in fileinfo.get('ImageType'):
if 'M' in bids_meta.get('ImageType'):
mag_or_phase = 'magnitude'
elif 'P' in fileinfo.get('ImageType'):
elif 'P' in bids_meta.get('ImageType'):
mag_or_phase = 'phase'
else:
mag_or_phase = suffix
Expand Down Expand Up @@ -585,12 +592,12 @@ def save_converted_files(res, item_dicoms, bids_options, outtype, prefix, outnam
# (Note: it can be _sbref and multiecho, so don't use "elif"):
# For multi-echo sequences, we have to specify the echo number in
# the file name:
if bids_file and is_multiecho:
if bids_meta and is_multiecho:
# Get the EchoNumber from json file info. If not present, use EchoTime
if 'EchoNumber' in fileinfo.keys():
echo_number = fileinfo['EchoNumber']
if 'EchoNumber' in bids_meta:
echo_number = bids_meta['EchoNumber']
else:
echo_number = echo_times.index(fileinfo['EchoTime']) + 1
echo_number = echo_times.index(bids_meta['EchoTime']) + 1

supported_multiecho = ['_bold', '_phase', '_epi', '_sbref', '_T1w', '_PDT2']
# Now, decide where to insert it.
Expand Down
4 changes: 2 additions & 2 deletions heudiconv/external/dlad.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

lgr = logging.getLogger(__name__)

MIN_VERSION = '0.12.2'
MIN_VERSION = '0.12.4'


def prepare_datalad(studydir, outdir, sid, session, seqinfo, dicoms, bids):
Expand Down Expand Up @@ -177,4 +177,4 @@ def mark_sensitive(ds, path_glob):
init=dict([('distribution-restrictions', 'sensitive')]),
recursive=True)
if inspect.isgenerator(res):
res = list(res)
res = list(res)
74 changes: 50 additions & 24 deletions heudiconv/heuristics/reproin.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,10 @@ def infotodict(seqinfo):
# 3 - Image IOD specific specialization (optional)
dcm_image_iod_spec = s.image_type[2]
image_type_seqtype = {
'P': 'fmap', # phase
# Note: P and M are too generic to make a decision here, could be
# for different seqtypes (bold, fmap, etc)
'FMRI': 'func',
'MPR': 'anat',
# 'M': 'func', "magnitude" -- can be for scout, anat, bold, fmap
'DIFFUSION': 'dwi',
'MIP_SAG': 'anat', # angiography
'MIP_COR': 'anat', # angiography
Expand Down Expand Up @@ -570,29 +570,55 @@ def infotodict(seqinfo):
# prefix = ''
prefix = ''

#
# Figure out the seqtype_label (BIDS _suffix)
#
# If none was provided -- let's deduce it from the information we find:
# analyze s.protocol_name (series_id is based on it) for full name mapping etc
if seqtype == 'func' and not seqtype_label:
if '_pace_' in series_spec:
seqtype_label = 'pace' # or should it be part of seq-
else:
# assume bold by default
seqtype_label = 'bold'

if seqtype == 'fmap' and not seqtype_label:
if not dcm_image_iod_spec:
raise ValueError("Do not know image data type yet to make decision")
seqtype_label = {
# might want explicit {file_index} ?
# _epi for pepolar fieldmaps, see
# https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#case-4-multiple-phase-encoded-directions-pepolar
'M': 'epi' if 'dir' in series_info else 'magnitude',
'P': 'phasediff',
'DIFFUSION': 'epi', # according to KODI those DWI are the EPIs we need
}[dcm_image_iod_spec]

# label for dwi as well
if seqtype == 'dwi' and not seqtype_label:
seqtype_label = 'dwi'
if not seqtype_label:
if seqtype == 'func':
if '_pace_' in series_spec:
seqtype_label = 'pace' # or should it be part of seq-
elif 'P' in s.image_type:
seqtype_label = 'phase'
elif 'M' in s.image_type:
seqtype_label = 'bold'
else:
# assume bold by default
seqtype_label = 'bold'
elif seqtype == 'fmap':
# TODO: support phase1 phase2 like in "Case 2: Two phase images ..."
if not dcm_image_iod_spec:
raise ValueError("Do not know image data type yet to make decision")
seqtype_label = {
# might want explicit {file_index} ?
# _epi for pepolar fieldmaps, see
# https://bids-specification.readthedocs.io/en/stable/04-modality-specific-files/01-magnetic-resonance-imaging-data.html#case-4-multiple-phase-encoded-directions-pepolar
'M': 'epi' if 'dir' in series_info else 'magnitude',
'P': 'phasediff',
'DIFFUSION': 'epi', # according to KODI those DWI are the EPIs we need
}[dcm_image_iod_spec]
elif seqtype == 'dwi':
# label for dwi as well
seqtype_label = 'dwi'

#
# Even if seqtype_label was provided, for some data we might need to override,
# since they are complementary files produced along-side with original
# ones.
#
if s.series_description.endswith('_SBRef'):
seqtype_label = 'sbref'

if not seqtype_label:
# Might be provided by the bids ending within series_spec, we would
# just want to check if that the last element is not _key-value pair
bids_ending = series_info.get('bids', None)
if not bids_ending \
or "-" in bids_ending.split('_')[-1]:
lgr.warning(
"We ended up with an empty label/suffix for %r",
series_spec)

run = series_info.get('run')
if run is not None:
Expand Down
2 changes: 1 addition & 1 deletion heudiconv/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
EXTRA_REQUIRES = {
'tests': TESTS_REQUIRES,
'extras': [], # Requires patched version ATM ['dcmstack'],
'datalad': ['datalad >=0.12.2']
'datalad': ['datalad >=0.12.3']
}

# Flatten the lists
Expand Down

0 comments on commit 74d9140

Please sign in to comment.