Skip to content

ENH: Add kwargs in Brain.add_* and pass it to mayavi modules #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Nov 1, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4399524
Feat: add kwargs in Brain.add_* and pass it to mayavi modules, so tha…
DingkunLiu Oct 3, 2019
d86b19f
Fix: Fix code styles which failed in the flake test.
DingkunLiu Oct 3, 2019
8cd93f3
Modify test_viz to pass the kwargs to the mayavi module.
DingkunLiu Oct 3, 2019
9d077da
fix flake test.
DingkunLiu Oct 3, 2019
c7a5912
Specifying `kwargs` as `mlab_kws` and make the documentation more clear.
DingkunLiu Oct 16, 2019
c3c8602
Revert "Specifying `kwargs` as `mlab_kws` and make the documentation …
DingkunLiu Oct 18, 2019
2c86a04
Link mayavi documents.
DingkunLiu Oct 18, 2019
7264ec5
Give detailed explanations about the kwargs passed to the functions.
DingkunLiu Oct 18, 2019
b746e0f
Revert "Link mayavi documents."
DingkunLiu Oct 18, 2019
0a6e0fa
Change the position of line breaking.
DingkunLiu Oct 18, 2019
9f95d24
Fix the typo and unavailable links.
DingkunLiu Oct 30, 2019
ac0b7b8
Fix render errors.
DingkunLiu Oct 31, 2019
0d5cdbf
Revert "Fix render errors."
DingkunLiu Oct 31, 2019
bbf7631
Fix render error.
DingkunLiu Oct 31, 2019
35654e8
Improve code rendering.
DingkunLiu Oct 31, 2019
e3c6ee7
Revert "Fix render error."
DingkunLiu Oct 31, 2019
6efccac
Fix line breaking error.
DingkunLiu Oct 31, 2019
533bab5
Revert "Fix line breaking error."
DingkunLiu Oct 31, 2019
a7abf61
Fix line breaking error.
DingkunLiu Oct 31, 2019
6bfe4b6
Revert "Fix line breaking error."
DingkunLiu Oct 31, 2019
6a9cdf1
Force line breaking to fix the rendering error on long code.
DingkunLiu Oct 31, 2019
97e6d55
Revert "Force line breaking to fix the rendering error on long code."
DingkunLiu Oct 31, 2019
c7f6a9f
Force line breaking to fix the rendering error on long code.
DingkunLiu Oct 31, 2019
81c8c09
Revert "Force line breaking to fix the rendering error on long code."
DingkunLiu Oct 31, 2019
de31e5f
Force line breaking to fix the rendering error on long code.
DingkunLiu Oct 31, 2019
32bbc7d
Revert "Force line breaking to fix the rendering error on long code."
DingkunLiu Oct 31, 2019
859b993
Fix render error
DingkunLiu Oct 31, 2019
4f99dd4
Render undocumented function as plain code.
DingkunLiu Oct 31, 2019
3843885
Fix sectioning.
DingkunLiu Nov 1, 2019
6453dd8
FIX: Formatting
larsoner Nov 1, 2019
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*.swp
*.orig
build
.idea/

dist/
doc/_build/
Expand Down
10 changes: 7 additions & 3 deletions surfer/tests/test_viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def test_annot():
view = get_view(brain)

for a, b, p in zip(annots, borders, alphas):
brain.add_annotation(a, b, p)
brain.add_annotation(a, b, p, opacity=0.8)
check_view(brain, view)

brain.set_surf('white')
Expand Down Expand Up @@ -226,7 +226,11 @@ def test_foci():
coords = [[-36, 18, -3],
[-43, 25, 24],
[-48, 26, -2]]
brain.add_foci(coords, map_surface="white", color="gold", name='test1')
brain.add_foci(coords,
map_surface="white",
color="gold",
name='test1',
resolution=25)

subj_dir = utils._get_subjects_dir()
annot_path = pjoin(subj_dir, subject_id, 'label', 'lh.aparc.a2009s.annot')
Expand Down Expand Up @@ -398,7 +402,7 @@ def test_overlay():
brain = Brain(*std_args)
brain.add_overlay(overlay_file)
brain.overlays["sig"].remove()
brain.add_overlay(overlay_file, min=5, max=20, sign="pos")
brain.add_overlay(overlay_file, min=5, max=20, sign="pos", opacity=0.7)
sig1 = io.read_scalar_data(pjoin(data_dir, "lh.sig.nii.gz"))
sig2 = io.read_scalar_data(pjoin(data_dir, "lh.alt_sig.nii.gz"))

Expand Down
86 changes: 49 additions & 37 deletions surfer/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ def _iter_time(self, time_idx, interpolation):
###########################################################################
# ADDING DATA PLOTS
def add_overlay(self, source, min=2, max="robust_max", sign="abs",
name=None, hemi=None):
name=None, hemi=None, **kwargs):
"""Add an overlay to the overlay dict from a file or array.

Parameters
Expand All @@ -932,6 +932,7 @@ def add_overlay(self, source, min=2, max="robust_max", sign="abs",
If None, it is assumed to belong to the hemipshere being
shown. If two hemispheres are being shown, an error will
be thrown.
kwargs: other mayavi surface arguments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should follow the NumPy docstring format, like:

kwargs : dict
    Other Mayavi surface arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is not necessarily to be a dict here. How about changing it to "All additional keyword arguments are passed to the
mlab.pipeline.surface call." and specifying the name kwargs to mlab_kws?
Or I could just remove the ** symbols to make it a real dict?

Copy link
Contributor

@larsoner larsoner Oct 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do what nibabel does (except we don't need to escape the star characters)

https://github.com/nipy/nibabel/pull/827/files#diff-a1d0ba69368622536897ef9fe4aee05eR766

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of detailed arguments for each mayavi function, so I think it is hard to list all the possible arguments. Could we just list some useful examples?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And one more thing, I think we should use the same name for the same argument in different functions. For example, the alpha in add_data() in mayavi is called opacity, so I think we should also specify this name in other functions and avoid it to appear in mlab_kws.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to list all mayavi keywords, but it would be great if you could enable intersphinx so that the docs produce a link to the relevant mayavi api doc.

Nor do I think that we should have mlab_kws; the convention is to collect extra keyword arguments with **kwargs, and "mlab" isn't particularly informative about where the kwargs are actually going.

I'd also like to minimize changes unrelated to this specific enhancement. There is some inconsistency between pysurfer parameter names and mayavi ones, but aiming to fully streamline that would be a rabbit hole that is best to avoid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I would just do:

**kwargs : additional keyword arguments
    These are passed to the underlying :func:`mayavi.mlab.surface` call.

To figure out the links you can dump the intersphinx inventory with something like:

$ python -msphinx.ext.intersphinx http://docs.enthought.com/mayavi/mayavi/objects.inv > mayavi.txt

Then browse for relevant entries in mayavi.txt. Unfortunately they might not have entries for each function you actually want to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the documents, but unfortunately, those for the functions in mlab.pipeline are missing.

"""
hemi = self._check_hemi(hemi)
# load data here
Expand All @@ -944,7 +945,7 @@ def add_overlay(self, source, min=2, max="robust_max", sign="abs",
views = self._toggle_render(False)
for brain in self._brain_list:
if brain['hemi'] == hemi:
ol.append(brain['brain'].add_overlay(old))
ol.append(brain['brain'].add_overlay(old, **kwargs))
if name in self.overlays_dict:
name = "%s%d" % (name, len(self.overlays_dict) + 1)
self.overlays_dict[name] = ol
Expand All @@ -957,7 +958,8 @@ def add_data(self, array, min=None, max=None, thresh=None,
time_label="time index=%d", colorbar=True,
hemi=None, remove_existing=False, time_label_size=14,
initial_time=None, scale_factor=None, vector_alpha=None,
mid=None, center=None, transparent=False, verbose=None):
mid=None, center=None, transparent=False, verbose=None,
**kwargs):
"""Display data from a numpy array on the surface.

This provides a similar interface to
Expand Down Expand Up @@ -1035,6 +1037,7 @@ def add_data(self, array, min=None, max=None, thresh=None,
vector-valued data. If None (default), ``alpha`` is used.
verbose : bool, str, int, or None
If not None, override default verbose level (see surfer.verbose).
kwargs: other mayavi surface arguments.

Notes
-----
Expand Down Expand Up @@ -1172,7 +1175,7 @@ def time_label(x):
s, ct, bar, gl = brain['brain'].add_data(
array, min, mid, max, thresh, lut, colormap, alpha,
colorbar, layer_id, smooth_mat, magnitude, magnitude_max,
scale_factor, vertices, vector_alpha)
scale_factor, vertices, vector_alpha, **kwargs)
surfs.append(s)
bars.append(bar)
glyphs.append(gl)
Expand All @@ -1196,7 +1199,7 @@ def time_label(x):
self._toggle_render(True, views)

def add_annotation(self, annot, borders=True, alpha=1, hemi=None,
remove_existing=True, color=None):
remove_existing=True, color=None, **kwargs):
"""Add an annotation file.

Parameters
Expand All @@ -1223,7 +1226,7 @@ def add_annotation(self, annot, borders=True, alpha=1, hemi=None,
color : matplotlib-style color code
If used, show all annotations in the same (specified) color.
Probably useful only when showing annotation borders.

kwargs: other mayavi surface arguments.
"""
hemis = self._check_hemis(hemi)

Expand Down Expand Up @@ -1305,11 +1308,12 @@ def add_annotation(self, annot, borders=True, alpha=1, hemi=None,
for brain in self._brain_list:
if brain['hemi'] == hemi:
self.annot_list.append(
brain['brain'].add_annotation(annot, ids, cmap))
brain['brain'].add_annotation(annot, ids, cmap,
**kwargs))
self._toggle_render(True, views)

def add_label(self, label, color=None, alpha=1, scalar_thresh=None,
borders=False, hemi=None, subdir=None):
borders=False, hemi=None, subdir=None, **kwargs):
"""Add an ROI label to the image.

Parameters
Expand Down Expand Up @@ -1341,7 +1345,7 @@ def add_label(self, label, color=None, alpha=1, scalar_thresh=None,
label directory rather than in the label directory itself (e.g.
for ``$SUBJECTS_DIR/$SUBJECT/label/aparc/lh.cuneus.label``
``brain.add_label('cuneus', subdir='aparc')``).

kwargs: other mayavi surface arguments.
Notes
-----
To remove previously added labels, run Brain.remove_labels().
Expand Down Expand Up @@ -1422,7 +1426,7 @@ def add_label(self, label, color=None, alpha=1, scalar_thresh=None,
for brain in self.brains:
if brain.hemi == hemi:
array_id, surf = brain.add_label(label, label_name, color,
alpha)
alpha, **kwargs)
surfaces.append(surf)
array_ids.append((brain, array_id))
self._label_dicts[label_name] = {'surfaces': surfaces,
Expand Down Expand Up @@ -1532,7 +1536,7 @@ def remove_labels(self, labels=None, hemi=None):

def add_morphometry(self, measure, grayscale=False, hemi=None,
remove_existing=True, colormap=None,
min=None, max=None, colorbar=True):
min=None, max=None, colorbar=True, **kwargs):
"""Add a morphometry overlay to the image.

Parameters
Expand All @@ -1554,7 +1558,7 @@ def add_morphometry(self, measure, grayscale=False, hemi=None,
of the data is used.
colorbar : bool
If True, show a colorbar corresponding to the overlay data.

kwargs: other mayavi surface arguments.
"""
hemis = self._check_hemis(hemi)
morph_files = []
Expand Down Expand Up @@ -1615,12 +1619,13 @@ def add_morphometry(self, measure, grayscale=False, hemi=None,
for brain in self.brains:
if brain.hemi == hemi:
self.morphometry_list.append(brain.add_morphometry(
morph_data, colormap, measure, min, max, colorbar))
morph_data, colormap, measure, min, max, colorbar,
**kwargs))
self._toggle_render(True, views)

def add_foci(self, coords, coords_as_verts=False, map_surface=None,
scale_factor=1, color="white", alpha=1, name=None,
hemi=None):
hemi=None, **kwargs):
"""Add spherical foci, possibly mapping to displayed surf.

The foci spheres can be displayed at the coordinates given, or
Expand Down Expand Up @@ -1650,6 +1655,7 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,
If None, it is assumed to belong to the hemipshere being
shown. If two hemispheres are being shown, an error will
be thrown.
kwargs: other mayavi point3d arguments.
"""
from matplotlib.colors import colorConverter
hemi = self._check_hemi(hemi)
Expand Down Expand Up @@ -1685,13 +1691,15 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,
for brain in self._brain_list:
if brain['hemi'] == hemi:
fl.append(brain['brain'].add_foci(foci_coords, scale_factor,
color, alpha, name))
color, alpha, name,
**kwargs))
self.foci_dict[name] = fl
self._toggle_render(True, views)

def add_contour_overlay(self, source, min=None, max=None,
n_contours=7, line_width=1.5, colormap="YlOrRd_r",
hemi=None, remove_existing=True, colorbar=True):
hemi=None, remove_existing=True, colorbar=True,
**kwargs):
"""Add a topographic contour overlay of the positive data.

Note: This visualization will look best when using the "low_contrast"
Expand Down Expand Up @@ -1721,7 +1729,7 @@ def add_contour_overlay(self, source, min=None, max=None,
If there is an existing contour overlay, remove it before plotting.
colorbar : bool
If True, show the colorbar for the scalar value.

kwargs: other mayavi surface arguments.
"""
hemi = self._check_hemi(hemi)

Expand All @@ -1748,11 +1756,11 @@ def add_contour_overlay(self, source, min=None, max=None,
if brain.hemi == hemi:
self.contour_list.append(brain.add_contour_overlay(
scalar_data, min, max, n_contours, line_width, lut,
colorbar))
colorbar, **kwargs))
self._toggle_render(True, views)

def add_text(self, x, y, text, name, color=None, opacity=1.0,
row=-1, col=-1, font_size=None, justification=None):
row=-1, col=-1, font_size=None, justification=None, **kwargs):
""" Add a text to the visualization

Parameters
Expand All @@ -1775,11 +1783,13 @@ def add_text(self, x, y, text, name, color=None, opacity=1.0,
Row index of which brain to use
col : int
Column index of which brain to use
kwargs: other mayavi text3d arguments.
"""
if name in self.texts_dict:
self.texts_dict[name]['text'].remove()
text = self.brain_matrix[row, col].add_text(x, y, text,
name, color, opacity)
name, color, opacity,
**kwargs)
self.texts_dict[name] = dict(row=row, col=col, text=text)
if font_size is not None:
text.property.font_size = font_size
Expand Down Expand Up @@ -3196,7 +3206,7 @@ def _remove_vector_data(self, glyphs):
if glyphs is not None:
glyphs.parent.parent.remove()

def add_overlay(self, old):
def add_overlay(self, old, **kwargs):
"""Add an overlay to the overlay dict from a file or array"""
array_id, mesh = self._add_scalar_data(old.mlab_data)

Expand All @@ -3206,7 +3216,7 @@ def add_overlay(self, old):
pos = mlab.pipeline.surface(
pos_thresh, colormap="YlOrRd", figure=self._f,
vmin=old.pos_lims[1], vmax=old.pos_lims[2],
reset_zoom=False)
reset_zoom=False, **kwargs)
pos.actor.property.backface_culling = False
pos_bar = mlab.scalarbar(pos, nb_labels=5)
pos_bar.reverse_lut = True
Expand All @@ -3222,7 +3232,7 @@ def add_overlay(self, old):
neg = mlab.pipeline.surface(
neg_thresh, colormap="PuBu", figure=self._f,
vmin=old.neg_lims[1], vmax=old.neg_lims[2],
reset_zoom=False)
reset_zoom=False, **kwargs)
neg.actor.property.backface_culling = False
neg_bar = mlab.scalarbar(neg, nb_labels=5)
neg_bar.scalar_bar_representation.position = (0.05, 0.01)
Expand All @@ -3236,7 +3246,7 @@ def add_overlay(self, old):
@verbose
def add_data(self, array, fmin, fmid, fmax, thresh, lut, colormap, alpha,
colorbar, layer_id, smooth_mat, magnitude, magnitude_max,
scale_factor, vertices, vector_alpha):
scale_factor, vertices, vector_alpha, **kwargs):
"""Add data to the brain"""
# Calculate initial data to plot
if array.ndim == 1:
Expand Down Expand Up @@ -3278,7 +3288,8 @@ def add_data(self, array, fmin, fmid, fmax, thresh, lut, colormap, alpha,
with warnings.catch_warnings(record=True):
surf = mlab.pipeline.surface(
pipe, colormap=colormap, vmin=fmin, vmax=fmax,
opacity=float(alpha), figure=self._f, reset_zoom=False)
opacity=float(alpha), figure=self._f, reset_zoom=False,
**kwargs)
surf.actor.property.backface_culling = False

# apply look up table if given
Expand All @@ -3303,13 +3314,13 @@ def add_data(self, array, fmin, fmid, fmax, thresh, lut, colormap, alpha,
scale_factor_norm=scale_factor_norm)
return surf, orig_ctable, bar, glyphs

def add_annotation(self, annot, ids, cmap):
def add_annotation(self, annot, ids, cmap, **kwargs):
"""Add an annotation file"""
# Add scalar values to dataset
array_id, pipe = self._add_scalar_data(ids)
with warnings.catch_warnings(record=True):
surf = mlab.pipeline.surface(pipe, name=annot, figure=self._f,
reset_zoom=False)
reset_zoom=False, **kwargs)
surf.actor.property.backface_culling = False

# Set the color table
Expand All @@ -3320,13 +3331,13 @@ def add_annotation(self, annot, ids, cmap):
return dict(surface=surf, name=annot, colormap=cmap, brain=self,
array_id=array_id)

def add_label(self, label, label_name, color, alpha):
def add_label(self, label, label_name, color, alpha, **kwargs):
"""Add an ROI label to the image"""
from matplotlib.colors import colorConverter
array_id, pipe = self._add_scalar_data(label)
with warnings.catch_warnings(record=True):
surf = mlab.pipeline.surface(pipe, name=label_name, figure=self._f,
reset_zoom=False)
reset_zoom=False, **kwargs)
surf.actor.property.backface_culling = False
color = colorConverter.to_rgba(color, alpha)
cmap = np.array([(0, 0, 0, 0,), color])
Expand All @@ -3338,13 +3349,13 @@ def add_label(self, label, label_name, color, alpha):
return array_id, surf

def add_morphometry(self, morph_data, colormap, measure,
min, max, colorbar):
min, max, colorbar, **kwargs):
"""Add a morphometry overlay to the image"""
array_id, pipe = self._add_scalar_data(morph_data)
with warnings.catch_warnings(record=True):
surf = mlab.pipeline.surface(
pipe, colormap=colormap, vmin=min, vmax=max, name=measure,
figure=self._f, reset_zoom=False)
figure=self._f, reset_zoom=False, **kwargs)

# Get the colorbar
if colorbar:
Expand All @@ -3358,27 +3369,28 @@ def add_morphometry(self, morph_data, colormap, measure,
return dict(surface=surf, colorbar=bar, measure=measure, brain=self,
array_id=array_id)

def add_foci(self, foci_coords, scale_factor, color, alpha, name):
def add_foci(self, foci_coords, scale_factor, color, alpha, name,
**kwargs):
"""Add spherical foci, possibly mapping to displayed surf"""
# Create the visualization
with warnings.catch_warnings(record=True): # traits
points = mlab.points3d(
foci_coords[:, 0], foci_coords[:, 1], foci_coords[:, 2],
np.ones(foci_coords.shape[0]), name=name, figure=self._f,
scale_factor=(10. * scale_factor), color=color, opacity=alpha,
reset_zoom=False)
reset_zoom=False, **kwargs)
return points

def add_contour_overlay(self, scalar_data, min=None, max=None,
n_contours=7, line_width=1.5, lut=None,
colorbar=True):
colorbar=True, **kwargs):
"""Add a topographic contour overlay of the positive data"""
array_id, pipe = self._add_scalar_data(scalar_data)
with warnings.catch_warnings(record=True):
thresh = threshold_filter(pipe, low=min)
surf = mlab.pipeline.contour_surface(
thresh, contours=n_contours, line_width=line_width,
reset_zoom=False)
reset_zoom=False, **kwargs)
if lut is not None:
l_m = surf.module_manager.scalar_lut_manager
l_m.load_lut_from_list(lut / 255.)
Expand All @@ -3396,12 +3408,12 @@ def add_contour_overlay(self, scalar_data, min=None, max=None,
# Set up a dict attribute with pointers at important things
return dict(surface=surf, colorbar=bar, brain=self, array_id=array_id)

def add_text(self, x, y, text, name, color=None, opacity=1.0):
def add_text(self, x, y, text, name, color=None, opacity=1.0, **kwargs):
""" Add a text to the visualization"""
color = self._fg_color if color is None else color
with warnings.catch_warnings(record=True):
text = mlab.text(x, y, text, name=name, color=color,
opacity=opacity, figure=self._f)
opacity=opacity, figure=self._f, **kwargs)
return text

def remove_data(self, layer_id):
Expand Down