Skip to content

Commit 0d5c3b5

Browse files
committed
ENH: Transparency on fieldmap plots!
1 parent 19a8cce commit 0d5c3b5

File tree

3 files changed

+57
-18
lines changed

3 files changed

+57
-18
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ jobs:
175175
mkdir -p /tmp/data/
176176
echo "b2VzdGViYW5Ac3RhbmZvcmQuZWR1CjMwNzU2CiAqQ1MzYkJ5VXMxdTVNCiBGU2kvUGJsejJxR1V3Cg==" | base64 -d > /tmp/data/fslicense.txt
177177
178-
- save_cache:
178+
- save_cache:
179179
key: data-v2-{{ .Branch }}-{{ .BuildNum }}
180180
paths:
181181
- "/opt/circleci/.pyenv/versions/3.5.2"

sdcflows/interfaces/reportlets.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
22
# vi: set ft=python sts=4 ts=4 sw=4 et:
33
"""Interfaces to generate speciality reportlets."""
4+
import numpy as np
45
from nilearn.image import threshold_img, load_img
56
from niworkflows import NIWORKFLOWS_LOG
67
from niworkflows.viz.utils import cuts_from_bbox, compose_view
7-
from nipype.interfaces.base import File, isdefined
8+
from nipype.interfaces.base import File, isdefined, traits
89
from nipype.interfaces.mixins import reporting
910

1011
from ..viz.utils import plot_registration, coolwarm_transparent
1112

1213

1314
class _FieldmapReportletInputSpec(reporting.ReportCapableInputSpec):
1415
reference = File(exists=True, mandatory=True, desc='input reference')
16+
moving = File(exists=True, desc='input moving')
1517
fieldmap = File(exists=True, mandatory=True, desc='input fieldmap')
18+
max_alpha = traits.Float(0.7, usedefault=True, desc='maximum alpha channel')
1619
mask = File(exists=True, desc='brain mask')
1720
out_report = File('report.svg', usedefault=True,
1821
desc='filename for the visual report')
22+
show = traits.Enum(1, 0, 'both', usedefault=True,
23+
desc='where the fieldmap should be shown')
1924

2025

2126
class FieldmapReportlet(reporting.ReportCapableInterface):
@@ -39,28 +44,46 @@ def _generate_report(self):
3944

4045
refnii = load_img(self.inputs.reference)
4146
fmapnii = load_img(self.inputs.fieldmap)
42-
contour_nii = load_img(self.inputs.mask) if isdefined(self.inputs.mask) else None
43-
mask_nii = threshold_img(refnii, 1e-3)
47+
48+
contour_nii = mask_nii = None
49+
if isdefined(self.inputs.mask):
50+
contour_nii = load_img(self.inputs.mask)
51+
maskdata = contour_nii.get_fdata() > 0
52+
else:
53+
mask_nii = threshold_img(refnii, 1e-3)
54+
maskdata = mask_nii.get_fdata() > 0
4455
cuts = cuts_from_bbox(contour_nii or mask_nii, cuts=self._n_cuts)
4556
fmapdata = fmapnii.get_fdata()
46-
vmax = max(fmapdata.max(), abs(fmapdata.min()))
57+
vmax = max(abs(np.percentile(fmapdata[maskdata], 99.8)),
58+
abs(np.percentile(fmapdata[maskdata], 0.2)))
59+
60+
fmap_overlay = [{
61+
'overlay': fmapnii,
62+
'overlay_params': {
63+
'cmap': coolwarm_transparent(max_alpha=self.inputs.max_alpha),
64+
'vmax': vmax,
65+
'vmin': -vmax,
66+
}
67+
}] * 2
68+
69+
if self.inputs.show != 'both':
70+
fmap_overlay[not self.inputs.show] = {}
4771

4872
# Call composer
4973
compose_view(
50-
plot_registration(refnii, 'fixed-image',
74+
plot_registration(refnii, 'moving-image',
5175
estimate_brightness=True,
5276
cuts=cuts,
53-
label='reference',
77+
label='fieldmap (Hz)',
5478
contour=contour_nii,
55-
compress=False),
56-
plot_registration(fmapnii, 'moving-image',
79+
compress=False,
80+
**fmap_overlay[1]),
81+
plot_registration(refnii, 'fixed-image',
5782
estimate_brightness=True,
5883
cuts=cuts,
59-
label='fieldmap (Hz)',
84+
label='reference',
6085
contour=contour_nii,
6186
compress=False,
62-
plot_params={'cmap': coolwarm_transparent(),
63-
'vmax': vmax,
64-
'vmin': -vmax}),
87+
**fmap_overlay[0]),
6588
out_file=self._out_report
6689
)

sdcflows/viz/utils.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
def plot_registration(anat_nii, div_id, plot_params=None,
77
order=('z', 'x', 'y'), cuts=None,
88
estimate_brightness=False, label=None, contour=None,
9-
compress='auto'):
9+
compress='auto', overlay=None, overlay_params=None):
1010
"""
1111
Plot the foreground and background views.
1212
@@ -15,6 +15,7 @@ def plot_registration(anat_nii, div_id, plot_params=None,
1515
from uuid import uuid4
1616

1717
from lxml import etree
18+
import matplotlib.pyplot as plt
1819
from nilearn.plotting import plot_anat
1920
from svgutils.transform import SVGFigure
2021
from niworkflows.viz.utils import robust_set_limits, extract_svg, SVGNS
@@ -41,6 +42,15 @@ def plot_registration(anat_nii, div_id, plot_params=None,
4142

4243
# Generate nilearn figure
4344
display = plot_anat(anat_nii, **plot_params)
45+
if overlay is not None:
46+
_overlay_params = {
47+
'vmin': overlay.get_fdata().min(),
48+
'vmax': overlay.get_fdata().max(),
49+
'cmap': plt.cm.gray,
50+
'interpolation': 'nearest',
51+
}
52+
_overlay_params.update(overlay_params)
53+
display.add_overlay(overlay, **_overlay_params)
4454
if contour is not None:
4555
display.add_contours(contour, colors='g', levels=[0.5],
4656
linewidths=0.5)
@@ -60,7 +70,7 @@ def plot_registration(anat_nii, div_id, plot_params=None,
6070
return out_files
6171

6272

63-
def coolwarm_transparent():
73+
def coolwarm_transparent(max_alpha=0.7, opaque_perc=30, transparent_perc=8):
6474
"""Modify the coolwarm color scale to have full transparency around the middle."""
6575
import numpy as np
6676
import matplotlib.pylab as pl
@@ -72,9 +82,15 @@ def coolwarm_transparent():
7282
# Get the colormap colors
7383
my_cmap = cmap(np.arange(cmap.N))
7484

85+
_20perc = (cmap.N * opaque_perc) // 100
86+
midpoint = cmap.N // 2 + 1
87+
_10perc = (cmap.N * transparent_perc) // 100
7588
# Set alpha
76-
alpha = np.ones(cmap.N)
77-
alpha[128:160] = np.linspace(0, 1, len(alpha[128:160]))
78-
alpha[96:128] = np.linspace(1, 0, len(alpha[96:128]))
89+
alpha = np.ones(cmap.N) * max_alpha
90+
alpha[midpoint - _10perc:midpoint + _10perc] = 0
91+
alpha[_20perc:midpoint - _10perc - 1] = np.linspace(
92+
max_alpha, 0, len(alpha[_20perc:midpoint - _10perc - 1]))
93+
alpha[midpoint + _10perc:-_20perc] = np.linspace(
94+
0, max_alpha, len(alpha[midpoint + _10perc:-_20perc]))
7995
my_cmap[:, -1] = alpha
8096
return ListedColormap(my_cmap)

0 commit comments

Comments
 (0)