Skip to content

Mapping values to foci. #259

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

Open
wants to merge 50 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
1716aab
- Brain.add_foci can now map values using foci color and size.
cristidonos Jul 1, 2018
9fbd69f
re-organized add_foci function to allow data mapping and extended plo…
cristidonos Oct 23, 2018
16e5ed2
MRG: Fix scale_data_colormap and add FXAA (#261)
larsoner Oct 26, 2018
df68527
MRG: width/height in offscreen mode (#265)
larsoner Mar 22, 2019
8b0d631
Integrate better with Jupyter notebook (#268)
wmvanvliet May 22, 2019
27f54c6
MAINT: Update fsaverage (#270)
larsoner Jun 26, 2019
b9a7dd6
MAINT: Fix ignores for CIs (#272)
larsoner Jul 30, 2019
b2474cf
Set annotation LUT directly (#271)
mwaskom Jul 30, 2019
80c3ccb
Allow painting all annotation vertices in a single color (#273)
mwaskom Aug 12, 2019
9cd7511
FIX: don't change view when adding foci (#274)
christianbrodbeck Sep 9, 2019
1fb7daf
ENH: Add option to smooth to nearest (#275)
larsoner Oct 15, 2019
64a1069
MAINT: CircleCI for latest pip (#277)
larsoner Oct 15, 2019
e85753e
ENH: Add kwargs in Brain.add_* and pass it to mayavi modules (#276)
DingkunLiu Nov 1, 2019
0e1da27
BUG: Properly clean up on del
larsoner Dec 10, 2019
3692eb4
Merge pull request #279 from larsoner/cleanup
mwaskom Feb 3, 2020
e79624d
FIX: Safer exit
larsoner Feb 6, 2020
78950ed
FIX: Actually close
larsoner Feb 7, 2020
e79e15d
FIX: Try again
larsoner Feb 7, 2020
0b20e8b
FIX: Again again
larsoner Feb 7, 2020
1435b57
FIX: Different
larsoner Feb 7, 2020
2cf1d73
FIX: One more
larsoner Feb 7, 2020
5aaf043
FIX: Dont reuse by default
larsoner Feb 10, 2020
21e7183
Merge pull request #285 from larsoner/safer
mwaskom Feb 10, 2020
dd9f3b5
Fix (#286)
wmvanvliet Feb 11, 2020
cd2f074
MRG, FIX: Fix bug with scale_data_colormap (#287)
larsoner Feb 21, 2020
6962fe9
fix int (#288)
kingjr Feb 21, 2020
f576145
Update __init__.py
larsoner Feb 21, 2020
bd2bbfe
Update __init__.py
larsoner Feb 21, 2020
f67f444
FIX: CSS
larsoner Feb 21, 2020
6967306
FIX: Update changes
larsoner Feb 21, 2020
7952423
Set up CI with Azure Pipelines
larsoner Feb 26, 2020
9c43880
FIX: Remove force render
larsoner Feb 26, 2020
38db203
ENH: Azure
larsoner Feb 26, 2020
b7f25db
FIX: Missed
larsoner Feb 26, 2020
963c9b6
FIX: Missed again
larsoner Feb 26, 2020
b3522d4
FIX: Try again
larsoner Feb 26, 2020
11afc4a
FIX: Wrong call
larsoner Feb 26, 2020
283a580
FIX: Explicit close/del
larsoner Feb 26, 2020
ce7ac2d
FIX: Safer del
larsoner Feb 27, 2020
07dc04b
Merge pull request #289 from larsoner/force
mwaskom Feb 27, 2020
1afce4b
STY: Dont log info messages by default (#293)
larsoner May 18, 2020
09513f9
ENH: Move up to 3.8 (#294)
larsoner May 18, 2020
b0ca3a1
ENH: Add FXAA option (#295)
larsoner Jun 11, 2020
9e5fe1f
MRG, MAINT: Simpler vector params (#291)
larsoner Jun 23, 2020
85e9e6c
MAINT: Bump version
larsoner Jun 23, 2020
f02cca8
MAINT: Bump version
larsoner Jun 23, 2020
2512fc6
option to apply xfrom from surface files
cristidonos Nov 11, 2020
d3749dc
merged changes by CD on top of nipy's f02cca8 version from Jun 23
cristidonos Nov 11, 2020
6c9cb10
merged changes by CD on top of nipy's f02cca8 version from Jun 23
cristidonos Nov 11, 2020
cd3de2f
Merge branch 'master' of https://github.com/cristidonos/PySurfer into…
cristidonos Nov 11, 2020
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
97 changes: 88 additions & 9 deletions surfer/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1570,7 +1570,7 @@ def add_morphometry(self, measure, grayscale=False, hemi=None,

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, allow_data=False, data=None):
"""Add spherical foci, possibly mapping to displayed surf.

The foci spheres can be displayed at the coordinates given, or
Expand All @@ -1588,8 +1588,8 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,
whether the coords parameter should be interpreted as vertex ids
map_surface : Freesurfer surf or None
surface to map coordinates through, or None to use raw coords
scale_factor : float
Controls the size of the foci spheres (relative to 1cm).
scale_factor : int
Copy link
Member

Choose a reason for hiding this comment

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

Why the change to int here? Scale factors can be floats, as seen in the example: http://pysurfer.github.io/auto_examples/plot_foci.html Do your changes break that, or is this just an errant attempt to fix the documentation?

controls the size of the foci spheres
color : matplotlib color code
HTML name, RBG tuple, or hex code
alpha : float in [0, 1]
Expand All @@ -1600,6 +1600,14 @@ 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.
allow_data : bool | False
If True, will plot foci using quiver3d instead of points3d to allow
mapping data to spheres.
data : numpy array
1D array the same size as the number of foci. Spheres will be
colorcoded acording to data values, whereas spheres' sizes will be
coded using the absolute values of data.
Copy link
Contributor

Choose a reason for hiding this comment

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

API seems quite complicated. Can you achieve what you want using only one new parameter? and without adding a new method?

also you miss tests and some new example for documentation.

Copy link
Member

Choose a reason for hiding this comment

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

I agree with @agramfort. Can allow_data as an external parameter not be replaced with an internal check for data is not None?

Copy link
Member

Choose a reason for hiding this comment

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

I also am not sure that I like forcing the color and size of the spheres by default. There are lots of cases when you might want colors to reflect a scalar but a uniform size. Or (probably less commonly) vice versa. Also I don't see any way to control the size range, colormap, etc. through this API, which would be important.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for pointing out the issues, i'll try to fix them. This is my first attempt at a pull request, so bear with me, especially with the tests.


"""
from matplotlib.colors import colorConverter
hemi = self._check_hemi(hemi)
Expand All @@ -1614,8 +1622,7 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,
foci_coords = np.atleast_2d(coords)
else:
foci_surf = Surface(self.subject_id, hemi, map_surface,
subjects_dir=self.subjects_dir,
units=self._units)
subjects_dir=self.subjects_dir)
foci_surf.load_geometry()
foci_vtxs = utils.find_closest_vertices(foci_surf.coords, coords)
foci_coords = self.geo[hemi].coords[foci_vtxs]
Expand All @@ -1630,12 +1637,16 @@ def add_foci(self, coords, coords_as_verts=False, map_surface=None,

views = self._toggle_render(False)
fl = []
if self._units == 'm':
scale_factor = scale_factor / 1000.
for brain in self._brain_list:
if brain['hemi'] == hemi:
fl.append(brain['brain'].add_foci(foci_coords, scale_factor,
color, alpha, name))
if allow_data:
vtxs = utils.find_closest_vertices(self.geo[hemi].coords, foci_coords)
fl.append(brain['brain'].add_foci_data(foci_coords,
scale_factor, color, alpha, name, data,
self.geo[hemi].nn[vtxs, :]))
else:
fl.append(brain['brain'].add_foci(foci_coords,
scale_factor, color, alpha, name))
self.foci_dict[name] = fl
self._toggle_render(True, views)

Expand Down Expand Up @@ -1737,6 +1748,45 @@ def add_text(self, x, y, text, name, color=None, opacity=1.0,
if justification is not None:
text.property.justification = justification

def add_text3d(self, x, y, z, text, name, color=None, opacity=1.0,
row=-1, col=-1, font_size=None, justification=None):
Copy link
Member

Choose a reason for hiding this comment

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

Do we use this row, col parameterization anywhere else in pysurfer? Ideally if you're going to add a new method it should match the existing API as closely as possible.

""" Add a text to the visualization

Parameters
----------
x : Float
x coordinate
y : Float
y coordinate
z : Float
z coordinate
text : str
Text to add
name : str
Name of the text (text label can be updated using update_text())
color : Tuple
Color of the text. Default is the foreground color set during
initialization (default is black or white depending on the
background color).
opacity : Float
Opacity of the text. Default: 1.0
row : int
Row index of which brain to use
col : int
Column index of which brain to use
"""
if name in self.texts_dict:
self.texts_dict[name]['text'].remove()
text = self.brain_matrix[row, col].add_text3d(x, y, z, text,
name, color, opacity)
self.texts_dict[name] = dict(row=row, col=col, text=text)
if font_size is not None:
text.property.font_size = font_size
text.actor.text_scale_mode = 'viewport'
if justification is not None:
Copy link
Member

Choose a reason for hiding this comment

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

This parameter doesn't appear in the docstring. Also despite it being the property name in mayavi, "justification" is a weird term and "alignment" would be more correct.

text.property.justification = justification


def update_text(self, text, name, row=-1, col=-1):
"""Update text label

Expand Down Expand Up @@ -3290,6 +3340,27 @@ def add_foci(self, foci_coords, scale_factor, color, alpha, name):
scale_factor=(10. * scale_factor), color=color, opacity=alpha)
return points

def add_foci_data(self, foci_coords, scale_factor, color, alpha, name,data, normals):
"""Add spherical foci with attached data, possibly mapping to displayed surf"""
# Create the visualization
hemi = self.hemi
if data == None:
u = np.array(normals[:,0])
v = np.array(normals[:,1])
w = np.array(normals[:,2])
else:
u = self.geo[hemi].nn[:,0] * np.abs(data)
v = self.geo[hemi].nn[:,1] * np.abs(data)
w = self.geo[hemi].nn[:,2] * np.abs(data)
with warnings.catch_warnings(record=True): # traits
points = mlab.quiver3d(foci_coords[:, 0],foci_coords[:, 1],foci_coords[:, 2],
u,v,w, scalars = data, colormap='coolwarm', mode='sphere',
name=name, figure=self._f, scale_factor=(10. * scale_factor),
color=color, opacity=alpha)
points.glyph.color_mode = 'color_by_scalar'
points.glyph.glyph_source.glyph_source.center = [0, 0, 0]
return points

def add_contour_overlay(self, scalar_data, min=None, max=None,
n_contours=7, line_width=1.5, lut=None,
colorbar=True):
Expand Down Expand Up @@ -3325,6 +3396,14 @@ def add_text(self, x, y, text, name, color=None, opacity=1.0):
opacity=opacity, figure=self._f)
return text

def add_text3d(self, x, y, z, text, name, color=None, opacity=1.0):
""" Add a text in 3D to the visualization"""
color = self._fg_color if color is None else color
with warnings.catch_warnings(record=True):
text = mlab.text3d(x, y, z, text, name=name, color=color,
opacity=opacity, figure=self._f)
return text

def remove_data(self, layer_id):
"""Remove data shown with .add_data()"""
data = self.data.pop(layer_id)
Expand Down