Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions niworkflows/reports/core.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
#!/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:
"""
Reports builder for BIDS-Apps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Reports builder for BIDS-Apps.

Generalizes report generation across BIDS-Apps

"""
from pathlib import Path
Expand All @@ -20,21 +18,25 @@


PLURAL_SUFFIX = defaultdict(str('s').format, [('echo', 'es')])
SVG_SNIPPET = """\
SVG_SNIPPET = ["""\
<object class="svg-reportlet" type="image/svg+xml" data="./{0}">
Problem loading figure {0}. If the link below works, please try \
reloading the report in your browser.</object>
</div>
<div class="elem-filename">
Get figure file: <a href="./{0}" target="_blank">{0}</a>
</div>
"""
""", """\
<img class="svg-reportlet" src="./{0}" style="width: 100%" />
</div>
<div class="elem-filename">
Get figure file: <a href="./{0}" target="_blank">{0}</a>
</div>
"""]


class Element(object):
"""
Just a basic component of a report
"""
"""Just a basic component of a report"""

def __init__(self, name, title=None):
self.name = name
Expand Down Expand Up @@ -77,6 +79,15 @@ class Reportlet(Element):
>>> r.name
'datatype-anat_desc-reconall'

>>> r.components[0][0].startswith('<img')
True

>>> r = Reportlet(bl, out_figs, config={
... 'title': 'Some Title', 'bids': {'datatype': 'anat', 'desc': 'reconall'},
... 'description': 'Some description', 'static': False})
>>> r.name
'datatype-anat_desc-reconall'

>>> r.components[0][0].startswith('<object')
True

Expand Down Expand Up @@ -145,7 +156,23 @@ def __init__(self, layout, out_dir, config=None):
out_file = out_dir / linked_svg
out_file.parent.mkdir(parents=True, exist_ok=True)
copyfile(src, out_file, copy=True, use_hardlink=True)
contents = SVG_SNIPPET.format(linked_svg)
is_static = config.get('static', True)
contents = SVG_SNIPPET[is_static].format(linked_svg)

# Our current implementations of dynamic reportlets do this themselves,
# however I'll leave the code here since this is potentially something we
# will want to transfer from every figure generator to this location.
# The following code misses setting preserveAspecRatio="xMidYMid meet"
# if not is_static:
# # Remove height and width attributes from initial <svg> tag
# svglines = out_file.read_text().splitlines()
# expr = re.compile(r' (height|width)=["\'][0-9]+(\.[0-9]*)?[a-z]*["\']')
# for l, line in enumerate(svglines[:6]):
# if line.strip().startswith('<svg'):
# newline = expr.sub('', line)
# svglines[l] = newline
# out_file.write_text('\n'.join(svglines))
# break

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


class SubReport(Element):
"""
SubReports are sections within a Report
"""
"""SubReports are sections within a Report."""

def __init__(self, name, isnested=False, reportlets=None, title=''):
self.name = name
Expand Down Expand Up @@ -195,7 +220,7 @@ class Report(object):
>>> robj.generate_report()
0
>>> len((testdir / 'out' / 'fmriprep' / 'sub-01.html').read_text())
20862
19352

"""

Expand Down
76 changes: 43 additions & 33 deletions niworkflows/reports/fmriprep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sections:
description: Results of nonlinear alignment of the T1w reference one or more template
space(s). Hover on the panels with the mouse pointer to transition between both
spaces.
static: false
subtitle: Spatial normalization of the anatomical T1w reference
- bids: {datatype: anat, desc: reconall, suffix: T1w}
caption: Surfaces (white and pial) reconstructed with FreeSurfer (<code>recon-all</code>)
Expand All @@ -29,7 +30,7 @@ sections:
ordering: session
reportlets:
- bids: {datatype: fmap, desc: brain, suffix: mask}
caption: Brain extraction of the magnitude image from the fieldmap
caption: Brain extraction of the magnitude image from the fieldmap.
subtitle: Skull stripped magnitude image
- name: Functional
ordering: session,task,run
Expand All @@ -39,72 +40,72 @@ sections:
- bids: {datatype: func, desc: magnitude, suffix: bold}
caption: Results of affine coregistration between the magnitude image of the fieldmap
and the reference EPI image
static: false
subtitle: Fieldmap to EPI registration
- bids: {datatype: func, desc: fieldmap, suffix: bold}
caption: Overlaid on the reference EPI image
caption: Overlaid on the reference EPI image.
static: false
subtitle: Fieldmap
- bids: {datatype: func, desc: sdc, suffix: bold}
caption: Results of performing susceptibility distortion correction (SDC) on the
EPI
static: false
subtitle: Susceptibility distortion correction
- bids: {datatype: func, desc: forcedsyn, suffix: bold}
caption: The dataset contained some fieldmap information, but the argument <code>--force-syn</code>
was used. The higher-priority SDC method was used. Here, we show the results
of performing SyN-based SDC on the EPI for comparison.
static: false
subtitle: Experimental fieldmap-less susceptibility distortion correction
- bids: {datatype: func, desc: rois, suffix: bold}
caption: Brain mask calculated on the BOLD signal (red contour), along with the
masks used for a/tCompCor.<br />The aCompCor mask (magenta contour) is a conservative
CSF and white-matter mask for extracting physiological and movement confounds.
<br />The fCompCor mask (blue contour) contains the top 5% most variable voxels
within a heavily-eroded brain-mask.
subtitle: ROIs in BOLD space
- bids:
datatype: func
desc: '[at]compcor'
extensions: [.html]
suffix: bold
- bids: {datatype: func, desc: '[at]compcorvar', suffix: bold}
caption: The cumulative variance explained by the first k components of the
<em>t/aCompCor</em> decomposition, plotted for all values of <em>k</em>.
The number of components that must be included in the model in order to
explain some fraction of variance in the decomposition mask can be used
as a feature selection criterion for confound regression.
- bids: {datatype: func, desc: 'confoundcorr', suffix: bold}
caption: |
Left: Heatmap summarizing the correlation structure among confound variables.
(Cosine bases and PCA-derived CompCor components are inherently orthogonal.)
Right: magnitude of the correlation between each confound time series and the
mean global signal. Strong correlations might be indicative of partial volume
effects and can inform decisions about feature orthogonalization prior to
confound regression.
subtitle: Correlations among nuisance regressors
- bids: {datatype: func, desc: flirtnobbr, suffix: bold}
caption: FSL <code>flirt</code> was used to generate transformations from EPI
space to T1 Space - BBR refinement rejected. Note that Nearest Neighbor interpolation
is used in the reportlets in order to highlight potential spin-history and other
artifacts, whereas final images are resampled using Lanczos interpolation.
subtitle: EPI to T1 registration
static: false
subtitle: Alignment of functional and anatomical MRI data (volume based)
- bids: {datatype: func, desc: coreg, suffix: bold}
caption: <code>mri_coreg</code> (FreeSurfer) was used to generate transformations
from EPI space to T1 Space - <code>bbregister</code> refinement rejected. Note
that Nearest Neighbor interpolation is used in the reportlets in order to highlight
potential spin-history and other artifacts, whereas final images are resampled
using Lanczos interpolation.
subtitle: EPI to T1 registration
static: false
subtitle: Alignment of functional and anatomical MRI data (volume based)
- bids: {datatype: func, desc: flirtbbr, suffix: bold}
caption: FSL <code>flirt</code> was used to generate transformations from EPI-space
to T1w-space - The white matter mask calculated with FSL <code>fast</code> (brain
tissue segmentation) was used for BBR. Note that Nearest Neighbor interpolation
is used in the reportlets in order to highlight potential spin-history and other
artifacts, whereas final images are resampled using Lanczos interpolation.
subtitle: EPI to T1 registration
static: false
subtitle: Alignment of functional and anatomical MRI data (surface driven)
- bids: {datatype: func, desc: bbregister, suffix: bold}
caption: <code>bbregister</code> was used to generate transformations from EPI-space
to T1w-space. Note that Nearest Neighbor interpolation is used in the reportlets
in order to highlight potential spin-history and other artifacts, whereas final
images are resampled using Lanczos interpolation.
subtitle: EPI to T1 registration
static: false
subtitle: Alignment of functional and anatomical MRI data (surface driven)
- bids: {datatype: func, desc: rois, suffix: bold}
caption: Brain mask calculated on the BOLD signal (red contour), along with the
masks used for a/tCompCor.<br />The aCompCor mask (magenta contour) is a conservative
CSF and white-matter mask for extracting physiological and movement confounds.
<br />The fCompCor mask (blue contour) contains the top 5% most variable voxels
within a heavily-eroded brain-mask.
subtitle: Brain mask and (temporal/anatomical) CompCor ROIs
- bids:
datatype: func
desc: '[at]compcor'
extensions: [.html]
suffix: bold
- bids: {datatype: func, desc: 'compcorvar', suffix: bold}
caption: The cumulative variance explained by the first k components of the
<em>t/aCompCor</em> decomposition, plotted for all values of <em>k</em>.
The number of components that must be included in the model in order to
explain some fraction of variance in the decomposition mask can be used
as a feature selection criterion for confound regression.
subtitle: Variance explained by t/aCompCor components
- bids: {datatype: func, desc: carpetplot, suffix: bold}
caption: Summary statistics are plotted, which may reveal trends or artifacts
in the BOLD data. Global signals calculated within the whole-brain (GS), within
Expand All @@ -116,6 +117,15 @@ sections:
and white matter and CSF (red), indicated by the color map on the left-hand
side.
subtitle: BOLD Summary
- bids: {datatype: func, desc: 'confoundcorr', suffix: bold}
caption: |
Left: Heatmap summarizing the correlation structure among confound variables.
(Cosine bases and PCA-derived CompCor components are inherently orthogonal.)
Right: magnitude of the correlation between each confound time series and the
mean global signal. Strong correlations might be indicative of partial volume
effects and can inform decisions about feature orthogonalization prior to
confound regression.
subtitle: Correlations among nuisance regressors
- bids: {datatype: func, desc: aroma, suffix: bold}
caption: |
Maps created with maximum intensity projection (glass brain) with a
Expand Down
18 changes: 1 addition & 17 deletions niworkflows/viz/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import os
import os.path as op
from pathlib import Path
import subprocess
import base64
import re
Expand Down Expand Up @@ -560,8 +559,6 @@ def plot_melodic_components(melodic_dir, in_file, tr=None,
import seaborn as sns
from matplotlib.gridspec import GridSpec
import os
import re
from io import StringIO
sns.set_style("white")
current_palette = sns.color_palette()
in_nii = nb.load(in_file)
Expand Down Expand Up @@ -702,19 +699,6 @@ def plot_melodic_components(melodic_dir, in_file, tr=None,
sns.despine(left=True, bottom=True)

plt.subplots_adjust(hspace=0.5)

image_buf = StringIO()
fig.savefig(image_buf, dpi=300, format='svg', transparent=True,
fig.savefig(out_file, dpi=300, format='svg', transparent=True,
bbox_inches='tight', pad_inches=0.01)
fig.clf()
image_svg = image_buf.getvalue()

if compress is True or compress == 'auto':
image_svg = svg_compress(image_svg, compress)
image_svg = re.sub(' height="[0-9]+[a-z]*"', '', image_svg, count=1)
image_svg = re.sub(' width="[0-9]+[a-z]*"', '', image_svg, count=1)
image_svg = re.sub(' viewBox',
' preseveAspectRation="xMidYMid meet" viewBox',
image_svg, count=1)

Path(out_file).write_text(image_svg)