Skip to content

Commit 093c08f

Browse files
authored
Merge pull request #357 from oesteban/fix/compcor-reportlet-naming
ENH: Miscellaneous improvements to the Reports
2 parents 393ab5b + 03851cc commit 093c08f

File tree

3 files changed

+83
-64
lines changed

3 files changed

+83
-64
lines changed

niworkflows/reports/core.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
31
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
42
# vi: set ft=python sts=4 ts=4 sw=4 et:
53
"""
6-
Reports builder for BIDS-Apps
7-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
Reports builder for BIDS-Apps.
85
6+
Generalizes report generation across BIDS-Apps
97
108
"""
119
from pathlib import Path
@@ -20,21 +18,25 @@
2018

2119

2220
PLURAL_SUFFIX = defaultdict(str('s').format, [('echo', 'es')])
23-
SVG_SNIPPET = """\
21+
SVG_SNIPPET = ["""\
2422
<object class="svg-reportlet" type="image/svg+xml" data="./{0}">
2523
Problem loading figure {0}. If the link below works, please try \
2624
reloading the report in your browser.</object>
2725
</div>
2826
<div class="elem-filename">
2927
Get figure file: <a href="./{0}" target="_blank">{0}</a>
3028
</div>
31-
"""
29+
""", """\
30+
<img class="svg-reportlet" src="./{0}" style="width: 100%" />
31+
</div>
32+
<div class="elem-filename">
33+
Get figure file: <a href="./{0}" target="_blank">{0}</a>
34+
</div>
35+
"""]
3236

3337

3438
class Element(object):
35-
"""
36-
Just a basic component of a report
37-
"""
39+
"""Just a basic component of a report"""
3840

3941
def __init__(self, name, title=None):
4042
self.name = name
@@ -77,6 +79,15 @@ class Reportlet(Element):
7779
>>> r.name
7880
'datatype-anat_desc-reconall'
7981
82+
>>> r.components[0][0].startswith('<img')
83+
True
84+
85+
>>> r = Reportlet(bl, out_figs, config={
86+
... 'title': 'Some Title', 'bids': {'datatype': 'anat', 'desc': 'reconall'},
87+
... 'description': 'Some description', 'static': False})
88+
>>> r.name
89+
'datatype-anat_desc-reconall'
90+
8091
>>> r.components[0][0].startswith('<object')
8192
True
8293
@@ -145,7 +156,23 @@ def __init__(self, layout, out_dir, config=None):
145156
out_file = out_dir / linked_svg
146157
out_file.parent.mkdir(parents=True, exist_ok=True)
147158
copyfile(src, out_file, copy=True, use_hardlink=True)
148-
contents = SVG_SNIPPET.format(linked_svg)
159+
is_static = config.get('static', True)
160+
contents = SVG_SNIPPET[is_static].format(linked_svg)
161+
162+
# Our current implementations of dynamic reportlets do this themselves,
163+
# however I'll leave the code here since this is potentially something we
164+
# will want to transfer from every figure generator to this location.
165+
# The following code misses setting preserveAspecRatio="xMidYMid meet"
166+
# if not is_static:
167+
# # Remove height and width attributes from initial <svg> tag
168+
# svglines = out_file.read_text().splitlines()
169+
# expr = re.compile(r' (height|width)=["\'][0-9]+(\.[0-9]*)?[a-z]*["\']')
170+
# for l, line in enumerate(svglines[:6]):
171+
# if line.strip().startswith('<svg'):
172+
# newline = expr.sub('', line)
173+
# svglines[l] = newline
174+
# out_file.write_text('\n'.join(svglines))
175+
# break
149176

150177
if contents:
151178
self.components.append((contents, desc_text))
@@ -155,9 +182,7 @@ def is_empty(self):
155182

156183

157184
class SubReport(Element):
158-
"""
159-
SubReports are sections within a Report
160-
"""
185+
"""SubReports are sections within a Report."""
161186

162187
def __init__(self, name, isnested=False, reportlets=None, title=''):
163188
self.name = name
@@ -195,7 +220,7 @@ class Report(object):
195220
>>> robj.generate_report()
196221
0
197222
>>> len((testdir / 'out' / 'fmriprep' / 'sub-01.html').read_text())
198-
20862
223+
19352
199224
200225
"""
201226

niworkflows/reports/fmriprep.yml

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ sections:
2020
description: Results of nonlinear alignment of the T1w reference one or more template
2121
space(s). Hover on the panels with the mouse pointer to transition between both
2222
spaces.
23+
static: false
2324
subtitle: Spatial normalization of the anatomical T1w reference
2425
- bids: {datatype: anat, desc: reconall, suffix: T1w}
2526
caption: Surfaces (white and pial) reconstructed with FreeSurfer (<code>recon-all</code>)
@@ -29,7 +30,7 @@ sections:
2930
ordering: session
3031
reportlets:
3132
- bids: {datatype: fmap, desc: brain, suffix: mask}
32-
caption: Brain extraction of the magnitude image from the fieldmap
33+
caption: Brain extraction of the magnitude image from the fieldmap.
3334
subtitle: Skull stripped magnitude image
3435
- name: Functional
3536
ordering: session,task,run
@@ -39,72 +40,72 @@ sections:
3940
- bids: {datatype: func, desc: magnitude, suffix: bold}
4041
caption: Results of affine coregistration between the magnitude image of the fieldmap
4142
and the reference EPI image
43+
static: false
4244
subtitle: Fieldmap to EPI registration
4345
- bids: {datatype: func, desc: fieldmap, suffix: bold}
44-
caption: Overlaid on the reference EPI image
46+
caption: Overlaid on the reference EPI image.
47+
static: false
4548
subtitle: Fieldmap
4649
- bids: {datatype: func, desc: sdc, suffix: bold}
4750
caption: Results of performing susceptibility distortion correction (SDC) on the
4851
EPI
52+
static: false
4953
subtitle: Susceptibility distortion correction
5054
- bids: {datatype: func, desc: forcedsyn, suffix: bold}
5155
caption: The dataset contained some fieldmap information, but the argument <code>--force-syn</code>
5256
was used. The higher-priority SDC method was used. Here, we show the results
5357
of performing SyN-based SDC on the EPI for comparison.
58+
static: false
5459
subtitle: Experimental fieldmap-less susceptibility distortion correction
55-
- bids: {datatype: func, desc: rois, suffix: bold}
56-
caption: Brain mask calculated on the BOLD signal (red contour), along with the
57-
masks used for a/tCompCor.<br />The aCompCor mask (magenta contour) is a conservative
58-
CSF and white-matter mask for extracting physiological and movement confounds.
59-
<br />The fCompCor mask (blue contour) contains the top 5% most variable voxels
60-
within a heavily-eroded brain-mask.
61-
subtitle: ROIs in BOLD space
62-
- bids:
63-
datatype: func
64-
desc: '[at]compcor'
65-
extensions: [.html]
66-
suffix: bold
67-
- bids: {datatype: func, desc: '[at]compcorvar', suffix: bold}
68-
caption: The cumulative variance explained by the first k components of the
69-
<em>t/aCompCor</em> decomposition, plotted for all values of <em>k</em>.
70-
The number of components that must be included in the model in order to
71-
explain some fraction of variance in the decomposition mask can be used
72-
as a feature selection criterion for confound regression.
73-
- bids: {datatype: func, desc: 'confoundcorr', suffix: bold}
74-
caption: |
75-
Left: Heatmap summarizing the correlation structure among confound variables.
76-
(Cosine bases and PCA-derived CompCor components are inherently orthogonal.)
77-
Right: magnitude of the correlation between each confound time series and the
78-
mean global signal. Strong correlations might be indicative of partial volume
79-
effects and can inform decisions about feature orthogonalization prior to
80-
confound regression.
81-
subtitle: Correlations among nuisance regressors
8260
- bids: {datatype: func, desc: flirtnobbr, suffix: bold}
8361
caption: FSL <code>flirt</code> was used to generate transformations from EPI
8462
space to T1 Space - BBR refinement rejected. Note that Nearest Neighbor interpolation
8563
is used in the reportlets in order to highlight potential spin-history and other
8664
artifacts, whereas final images are resampled using Lanczos interpolation.
87-
subtitle: EPI to T1 registration
65+
static: false
66+
subtitle: Alignment of functional and anatomical MRI data (volume based)
8867
- bids: {datatype: func, desc: coreg, suffix: bold}
8968
caption: <code>mri_coreg</code> (FreeSurfer) was used to generate transformations
9069
from EPI space to T1 Space - <code>bbregister</code> refinement rejected. Note
9170
that Nearest Neighbor interpolation is used in the reportlets in order to highlight
9271
potential spin-history and other artifacts, whereas final images are resampled
9372
using Lanczos interpolation.
94-
subtitle: EPI to T1 registration
73+
static: false
74+
subtitle: Alignment of functional and anatomical MRI data (volume based)
9575
- bids: {datatype: func, desc: flirtbbr, suffix: bold}
9676
caption: FSL <code>flirt</code> was used to generate transformations from EPI-space
9777
to T1w-space - The white matter mask calculated with FSL <code>fast</code> (brain
9878
tissue segmentation) was used for BBR. Note that Nearest Neighbor interpolation
9979
is used in the reportlets in order to highlight potential spin-history and other
10080
artifacts, whereas final images are resampled using Lanczos interpolation.
101-
subtitle: EPI to T1 registration
81+
static: false
82+
subtitle: Alignment of functional and anatomical MRI data (surface driven)
10283
- bids: {datatype: func, desc: bbregister, suffix: bold}
10384
caption: <code>bbregister</code> was used to generate transformations from EPI-space
10485
to T1w-space. Note that Nearest Neighbor interpolation is used in the reportlets
10586
in order to highlight potential spin-history and other artifacts, whereas final
10687
images are resampled using Lanczos interpolation.
107-
subtitle: EPI to T1 registration
88+
static: false
89+
subtitle: Alignment of functional and anatomical MRI data (surface driven)
90+
- bids: {datatype: func, desc: rois, suffix: bold}
91+
caption: Brain mask calculated on the BOLD signal (red contour), along with the
92+
masks used for a/tCompCor.<br />The aCompCor mask (magenta contour) is a conservative
93+
CSF and white-matter mask for extracting physiological and movement confounds.
94+
<br />The fCompCor mask (blue contour) contains the top 5% most variable voxels
95+
within a heavily-eroded brain-mask.
96+
subtitle: Brain mask and (temporal/anatomical) CompCor ROIs
97+
- bids:
98+
datatype: func
99+
desc: '[at]compcor'
100+
extensions: [.html]
101+
suffix: bold
102+
- bids: {datatype: func, desc: 'compcorvar', suffix: bold}
103+
caption: The cumulative variance explained by the first k components of the
104+
<em>t/aCompCor</em> decomposition, plotted for all values of <em>k</em>.
105+
The number of components that must be included in the model in order to
106+
explain some fraction of variance in the decomposition mask can be used
107+
as a feature selection criterion for confound regression.
108+
subtitle: Variance explained by t/aCompCor components
108109
- bids: {datatype: func, desc: carpetplot, suffix: bold}
109110
caption: Summary statistics are plotted, which may reveal trends or artifacts
110111
in the BOLD data. Global signals calculated within the whole-brain (GS), within
@@ -116,6 +117,15 @@ sections:
116117
and white matter and CSF (red), indicated by the color map on the left-hand
117118
side.
118119
subtitle: BOLD Summary
120+
- bids: {datatype: func, desc: 'confoundcorr', suffix: bold}
121+
caption: |
122+
Left: Heatmap summarizing the correlation structure among confound variables.
123+
(Cosine bases and PCA-derived CompCor components are inherently orthogonal.)
124+
Right: magnitude of the correlation between each confound time series and the
125+
mean global signal. Strong correlations might be indicative of partial volume
126+
effects and can inform decisions about feature orthogonalization prior to
127+
confound regression.
128+
subtitle: Correlations among nuisance regressors
119129
- bids: {datatype: func, desc: aroma, suffix: bold}
120130
caption: |
121131
Maps created with maximum intensity projection (glass brain) with a

niworkflows/viz/utils.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import os
88
import os.path as op
9-
from pathlib import Path
109
import subprocess
1110
import base64
1211
import re
@@ -560,8 +559,6 @@ def plot_melodic_components(melodic_dir, in_file, tr=None,
560559
import seaborn as sns
561560
from matplotlib.gridspec import GridSpec
562561
import os
563-
import re
564-
from io import StringIO
565562
sns.set_style("white")
566563
current_palette = sns.color_palette()
567564
in_nii = nb.load(in_file)
@@ -702,19 +699,6 @@ def plot_melodic_components(melodic_dir, in_file, tr=None,
702699
sns.despine(left=True, bottom=True)
703700

704701
plt.subplots_adjust(hspace=0.5)
705-
706-
image_buf = StringIO()
707-
fig.savefig(image_buf, dpi=300, format='svg', transparent=True,
702+
fig.savefig(out_file, dpi=300, format='svg', transparent=True,
708703
bbox_inches='tight', pad_inches=0.01)
709704
fig.clf()
710-
image_svg = image_buf.getvalue()
711-
712-
if compress is True or compress == 'auto':
713-
image_svg = svg_compress(image_svg, compress)
714-
image_svg = re.sub(' height="[0-9]+[a-z]*"', '', image_svg, count=1)
715-
image_svg = re.sub(' width="[0-9]+[a-z]*"', '', image_svg, count=1)
716-
image_svg = re.sub(' viewBox',
717-
' preseveAspectRation="xMidYMid meet" viewBox',
718-
image_svg, count=1)
719-
720-
Path(out_file).write_text(image_svg)

0 commit comments

Comments
 (0)