Skip to content

Commit

Permalink
Merge tag '1.1.12'
Browse files Browse the repository at this point in the history
1.1.12 (March 19, 2020)

Bug-fix release in the 1.1.x series.

* FIX: Update naming patterns in figures.json (#483)
* FIX: Add CE agent to output figure filename templates (#482)
  • Loading branch information
effigies committed Mar 19, 2020
2 parents bfffaf8 + 0277b6e commit 12d9858
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 25 deletions.
13 changes: 13 additions & 0 deletions CHANGES.rst
@@ -1,3 +1,16 @@
1.1.12 (March 19, 2020)
=======================
Bug-fix release in the 1.1.x series.

* FIX: Update naming patterns in figures.json (#483)
* FIX: Add CE agent to output figure filename templates (#482)

1.1.11 (March 17, 2020)
=======================
Bug-fix release to improve CIFTI compatibility with workbench tools.

* FIX: Ensure BOLD and label orientations are equal (#477)

1.1.10 (March 11, 2020)
=======================
Bug-fix release in the 1.1.x series.
Expand Down
65 changes: 65 additions & 0 deletions niworkflows/interfaces/cifti.py
Expand Up @@ -318,6 +318,8 @@ def _create_cifti_image(bold_file, label_file, bold_surfs, annotation_files, tr,
warnings.warn("Resampling bold volume to match label dimensions")
bold_img = resample_to_img(bold_img, label_img)

bold_img = _reorient_image(bold_img, target_img=label_img)

bold_data = bold_img.get_fdata(dtype='float32')
timepoints = bold_img.shape[3]
label_data = np.asanyarray(label_img.dataobj).astype('int16')
Expand Down Expand Up @@ -422,3 +424,66 @@ def _create_cifti_image(bold_file, label_file, bold_surfs, annotation_files, tr,
out_file = "{}.dtseries.nii".format(split_filename(bold_file)[1])
ci.save(img, out_file)
return Path.cwd() / out_file


def _reorient_image(img, *, target_img=None, orientation=None):
"""
Coerce an image to a target orientation.
.. note::
Only RAS -> LAS conversion is currently supported
Parameters
----------
img : :obj:`SpatialImage`
image to be reoriented
target_img : :obj:`SpatialImage`, optional
target in desired orientation
orientation : :obj:`str` or :obj:`tuple`, optional
desired orientation, if no target image is provided
.. testsetup::
>>> img = nb.load(Path(test_data) / 'testRobustMNINormalizationRPTMovingWarpedImage.nii.gz')
>>> las_img = img.as_reoriented([[0, -1], [1, 1], [2, 1]])
Examples
--------
>>> nimg = _reorient_image(img, target_img=img)
>>> nb.aff2axcodes(nimg.affine)
('R', 'A', 'S')
>>> nimg = _reorient_image(img, target_img=las_img)
>>> nb.aff2axcodes(nimg.affine)
('L', 'A', 'S')
>>> nimg = _reorient_image(img, orientation='LAS')
>>> nb.aff2axcodes(nimg.affine)
('L', 'A', 'S')
>>> _reorient_image(img, orientation='LPI')
Traceback (most recent call last):
...
NotImplementedError: Cannot reorient ...
>>> _reorient_image(img)
Traceback (most recent call last):
...
RuntimeError: No orientation ...
"""
orient0 = nb.aff2axcodes(img.affine)
if target_img is not None:
orient1 = nb.aff2axcodes(target_img.affine)
elif orientation is not None:
orient1 = tuple(orientation)
else:
raise RuntimeError("No orientation to reorient to!")

if orient0 == orient1: # already in desired orientation
return img
elif orient0 == tuple('RAS') and orient1 == tuple('LAS'): # RAS -> LAS
return img.as_reoriented([[0, -1], [1, 1], [2, 1]])
else:
raise NotImplementedError(
"Cannot reorient {0} to {1}.".format(orient0, orient1)
)
15 changes: 6 additions & 9 deletions niworkflows/reports/core.py
Expand Up @@ -234,7 +234,7 @@ class Report(object):
>>> robj.generate_report()
0
>>> len((testdir / 'out' / 'fmriprep' / 'sub-01.html').read_text())
36450
36540
.. testcleanup::
Expand Down Expand Up @@ -311,17 +311,14 @@ def index(self, config):
reportlets = []
for c in list_combos:
# do not display entities with the value None.
c = list(filter(None, c))
ent = list(compress(entities, c))
missing_entities = list(set(entities) - set(ent))
c_filt = list(filter(None, c))
ent_filt = list(compress(entities, c))
# Set a common title for this particular combination c
title = 'Reports for: %s.' % ', '.join(
['%s <span class="bids-entity">%s</span>' % (ent[i], c[i])
for i in range(len(c))])
['%s <span class="bids-entity">%s</span>' % (ent_filt[i], c_filt[i])
for i in range(len(c_filt))])
for cfg in subrep_cfg['reportlets']:
for m_e in missing_entities:
cfg['bids'].pop(m_e, None)
cfg['bids'].update({ent[i]: c[i] for i in range(len(c))})
cfg['bids'].update({entities[i]: c[i] for i in range(len(c))})
rlet = Reportlet(self.layout, self.out_dir, config=cfg)
if not rlet.is_empty():
rlet.title = title
Expand Down
10 changes: 5 additions & 5 deletions niworkflows/reports/figures.json
Expand Up @@ -21,15 +21,15 @@
"pattern": "[_/\\\\]acq-([a-zA-Z0-9]+)"
},
{
"name": "ce",
"name": "ceagent",
"pattern": "[_/\\\\]ce-([a-zA-Z0-9]+)"
},
{
"name": "reconstruction",
"pattern": "[_/\\\\]rec-([a-zA-Z0-9]+)"
},
{
"name": "dir",
"name": "direction",
"pattern": "[_/\\\\]dir-([a-zA-Z0-9]+)"
},
{
Expand All @@ -47,7 +47,7 @@
},
{
"name": "echo",
"pattern": "[_/\\\\]echo-([0-9]+)\\_bold."
"pattern": "[_/\\\\]echo-([0-9]+)"
},
{
"name": "recording",
Expand Down Expand Up @@ -112,7 +112,7 @@
],

"default_path_patterns": [
"sub-{subject}[/ses-{session}]/{datatype<anat|figures>}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{contrast}][_rec-{reconstruction}][_space-{space}][_desc-{desc}]_{suffix<T1w|T2w|T1rho|T1map|T2map|T2star|FLAIR|FLASH|PDmap|PD|PDT2|inplaneT[12]|angio|dseg|mask>}.{extension<html|svg>}",
"sub-{subject}[/ses-{session}]/{datatype<func|figures>}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}][_desc-{desc}]_{suffix<bold>}.{extension<html|svg>}"
"sub-{subject}[/ses-{session}]/{datatype<anat|figures>}/sub-{subject}[_ses-{session}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_space-{space}][_desc-{desc}]_{suffix<T1w|T2w|T1rho|T1map|T2map|T2star|FLAIR|FLASH|PDmap|PD|PDT2|inplaneT[12]|angio|dseg|mask>}.{extension<html|svg>}",
"sub-{subject}[/ses-{session}]/{datatype<func|figures>}/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ceagent}][_dir-{direction}][_rec-{reconstruction}][_run-{run}][_echo-{echo}][_space-{space}][_desc-{desc}]_{suffix<bold>}.{extension<html|svg>}"
]
}
29 changes: 18 additions & 11 deletions niworkflows/reports/tests/test_core.py
Expand Up @@ -9,6 +9,7 @@

import matplotlib.pyplot as plt
from bids.layout.writing import build_path
from bids.layout import BIDSLayout

import pytest

Expand All @@ -24,26 +25,28 @@ def bids_sessions(tmpdir_factory):
pattern = (
"sub-{subject}[/ses-{session}]/{datatype<anat|func>}/"
"sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}]"
"[_ce-{contrast}][_dir-{direction}][_rec-{reconstruction}]"
"[_ce-{ceagent}][_dir-{direction}][_rec-{reconstruction}]"
"[_mod-{modality}][_run-{run}][_echo-{echo}][_space-{space}]"
"[_desc-{desc}]_{suffix<dseg|T1w|bold>}.{extension<svg>}"
)
subjects = ['01']
tasks = ['t1', 't2', 't3']
runs = ['01', '02', None]
ces = ['none', 'Gd']
descs = ['aroma', 'bbregister', 'carpetplot', 'rois']
# create functional data for both sessions
ses1_combos = product(subjects, ['1'], tasks, runs, descs)
ses2_combos = product(subjects, ['2'], tasks, [None], descs)
ses1_combos = product(subjects, ['1'], tasks, [None], runs, descs)
ses2_combos = product(subjects, ['2'], tasks, ces, [None], descs)
# have no runs in the second session (ex: dmriprep test data)
# https://github.com/nipreps/dmriprep/pull/59
all_combos = list(ses1_combos) + list(ses2_combos)

for subject, session, task, run, desc in all_combos:
for subject, session, task, ce, run, desc in all_combos:
entities = {
'subject': subject,
'session': session,
'task': task,
'ceagent': ce,
'run': run,
'desc': desc,
'extension': 'svg',
Expand Down Expand Up @@ -143,10 +146,10 @@ def test_process_orderings_small(test_report1, orderings,
@pytest.mark.parametrize(
"orderings,expected_entities,first_value_combo,last_value_combo",
[
(['session', 'task', 'run'],
['session', 'task', 'run'],
('1', 't1', None),
('2', 't3', None),
(['session', 'task', 'ceagent', 'run'],
['session', 'task', 'ceagent', 'run'],
('1', 't1', None, None),
('2', 't3', 'none', None),
),
(['run', 'task', 'session'],
['run', 'task', 'session'],
Expand Down Expand Up @@ -182,6 +185,7 @@ def test_process_orderings_large(test_report2, orderings,
("run"),
("session,task"),
("session,task,run"),
("session,task,ceagent,run"),
("session,task,acquisition,ceagent,reconstruction,direction,run,echo"),
("session,task,run,madeupentity"),
])
Expand All @@ -198,12 +202,15 @@ def test_generated_reportlets(bids_sessions, ordering):
# expected number of reportlets
expected_reportlets_num = len(report.layout.get(extension='svg'))
# bids_session uses these entities
needed_entities = ['session', 'task', 'run']
needed_entities = ['session', 'task', 'ceagent', 'run']
# the last section is the most recently run
reportlets_num = len(report.sections[-1].reportlets)
# get the number of figures in the output directory
out_layout = BIDSLayout(out_dir, config='figures', validate=False)
out_figs = len(out_layout.get())
# if ordering does not contain all the relevent entities
# then there should be fewer reportlets than expected
if all(ent in ordering for ent in needed_entities):
assert reportlets_num == expected_reportlets_num
assert reportlets_num == expected_reportlets_num == out_figs
else:
assert reportlets_num < expected_reportlets_num
assert reportlets_num < expected_reportlets_num == out_figs

0 comments on commit 12d9858

Please sign in to comment.