Skip to content

Commit

Permalink
gif integration
Browse files Browse the repository at this point in the history
  • Loading branch information
wiheto committed Apr 4, 2022
1 parent fad6ae8 commit 816a3ad
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 105 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,23 @@ plt.show()
```
![](./examples/figures/template_inf.png)

### Plotting a rotating GIF


```python
netplotbrain.plot(template='MNI152NLin2009cAsym',
templatestyle='surface',
view='AP',
frames=10,
nodes=nodes,
nodesize='centrality_measure1',
edges=edges,
gif=True,
savename='./examples/figures/gif1')
```
![](./examples/figures/gif1.png)


### Plot different styles

```python
Expand Down
192 changes: 104 additions & 88 deletions docs/kwargs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ nodetype : str
nodealpha : float
Specify the transparency of the nodes
nodecols : list
Node column names in node dataframe. 'auto' entails the columsn are ['x', 'y', 'z'] (specifying coordinates)
Node column names in node dataframe. 'auto' entails the columns are ['x', 'y', 'z'] (specifying coordinates)
nodesizevminvmax : str, list
Scaling alternatives if nodesize is given.
Alternatives: 'absolute' (default) 'minmax', or 2-tuple list of [min, max].
Expand All @@ -24,93 +24,109 @@ nodecolorvminvmax : str, list
Scales continuous colormap between certain values.
Alternatives: 'minmax' (default), 'absmax', , or 2-tuple list of [min, max].
If minmax, colorbar starts at the smallest value to largest value.
If absmax, then colorbar goes from -abs(max(value)) to abs(max(value)), enusuring 0 is in the middle.
If absmax, then colorbar goes from -abs(max(value)) to abs(max(value)), ensuring 0 is in the middle.

EDGE KWARGS
EDGE KWARGS
------------

edgecols : list
Edge columns names in edge dataframe. Default is i and j (specifying nodes).
edgecolor : matplotlib coloring
Can be string (default 'black') or list of 3D/4D colors for each edge.
edgewidth : int, float
Specify width of edges. If auto, will plot the value in edge array (if array) or the weight column (if in pandas dataframe), otherwise 2.
edgeweights : string
String that specifies column in edge dataframe that contains weights.
If numpy array is edge input, can be True (default) to specify edge weights.
edgealpha : float
Transparency of edges (default: 1).
edgehighlightbehaviour : str
Alternatives "both" or "any" or None.
Governs edge dimming when highlightnodes is on
If both, then highlights only edges between highlighted nodes.
If any, then only edges connecting any of the nodes are highlighted.
edgewidthscale : int, float
Scale the width of all edges by a factor (default: 1)

TEMPLATE KWARGS

templatecolor : str
If templatestyle=='surface' or 'filled', the color of template voxels
templateedgethreshold : float
If templatestyle=='cloudy', can tweak the edges detection threshold. (Default: 0.7)
templatealpha : float
Opacity of template voxels.
templatevoxelsize : int
Resize voxels this size. Larger voxels = quicker. (Default: 2)
surface_detection : float
The value used to detect the surface boundrary (see argument level in marching_cubes).
Some default choices are made for various templates
surface_resolution : int
If templatestyle=='surface' controls the size of the triangles used in the surface reconstruction. (Default: 2).

LEGENDKWARGS

nodecolorlegend : True
If the colorlegend is plotted or not.
nodesizelegend : True
nodecolorlegendstyle : str
Alternatives: auto (default), discrete, continuious
If the color legend should show the entire colormap or discrete colors.
If auto, plots discrete if less than 8 unique values are detected.
legendtickfontsize : str, int
Matplotlib fontsize for title in figure legends
legendtitlefontsize : str, int
Matplotlib fontsize for ticks in figure legends

ARROW KWARGS

arrowaxis : list or str
Adds axis arrows onto plot. Alternatives are: LR, AP, SI, 'all'
arrowlength : int, float
Length of arrow
arroworigin : list
x,y,z coordinates of arrowaxis. Note 0,0,0 is bottom left.

FIGURE KWARGS

ax : matplotlib ax with 3D projection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
netplotbrain.plot(ax, ...)
fig : matplotlib figure


TEXT KWARGS

font : str
font for all text in figure.
fontcolor : str, list, tuple
font color for all text in figure
titlefontsize : str
Size of title font (default: medium). See matplotlib "fontsize"
titleloc : str,
Location of title (defualt: center). See matplotlib "loc"
titleweight : str
Font weight of title (default: regular). See matplotlib "fontweight"

STYLE KWARGS

profile : str
path or name of file in netplotbrain/profiles/<filename>.json, specifies default kwargs.
Default points to netplotbrain/profiles/default.json
edgecols : list
Edge columns names in edge dataframe. Default is i and j (specifying nodes).
edgecolor : matplotlib coloring
Can be string (default 'black') or list of 3D/4D colors for each edge.
edgewidth : int, float
Specify width of edges. If auto, will plot the value in edge array (if array) or the weight column (if in pandas dataframe), otherwise 2.
edgeweights : string
String that specifies column in edge dataframe that contains weights.
If numpy array is edge input, can be True (default) to specify edge weights.
edgealpha : float
Transparency of edges (default: 1).
edgehighlightbehaviour : str
Alternatives "both" or "any" or None.
Governs edge dimming when highlightnodes is on
If both, then highlights only edges between highlighted nodes.
If any, then only edges connecting any of the nodes are highlighted.
edgewidthscale : int, float
Scale the width of all edges by a factor (default: 1)

TEMPLATE KWARGS
-----------------
templatecolor : str
If templatestyle=='surface' or 'filled', the color of template voxels
templateedgethreshold : float
If templatestyle=='cloudy', can tweak the edges detection threshold. (Default: 0.7)
templatealpha : float
Opacity of template voxels.
templatevoxelsize : int
Resize voxels this size. Larger voxels = quicker. (Default: 2)
surface_detection : float
The value used to detect the surface boundary (see argument level in marching_cubes).
Some default choices are made for various templates
surface_resolution : int
If templatestyle=='surface' controls the size of the triangles used in the surface reconstruction. (Default: 2).

LEGENDKWARGS
---------------------
nodecolorlegend : True
If the colorlegend is plotted or not.
nodesizelegend : True
nodecolorlegendstyle : str
Alternatives: auto (default), discrete, continuous
If the color legend should show the entire colormap or discrete colors.
If auto, plots discrete if less than 8 unique values are detected.
legendtickfontsize : str, int
Matplotlib fontsize for title in figure legends
legendtitlefontsize : str, int
Matplotlib fontsize for ticks in figure legends

ARROW KWARGS
--------------------
arrowaxis : list or str
Adds axis arrows onto plot. Alternatives are: LR, AP, SI, 'all'
arrowlength : int, float
Length of arrow
arroworigin : list
x,y,z coordinates of arrowaxis. Note 0,0,0 is bottom left.

FIGURE KWARGS
-------------------
ax : matplotlib ax with 3D projection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
netplotbrain.plot(ax, ...)
fig : matplotlib figure
savename : str
Save path for figure.
If string ends with .png or .svg it will one save this figure.
if the path ends with anything else, it will save both a .png and .svg figure.
Default: None and nothing is saved.
figdpi : int
Default. resolution of figure when saving png files.

GIF KWARGS
-------------------------
gif : bool
If true, saves views as a gif.
gifduration : int
Gif duration in milliseconds
gifloop : int
How many times to loop figure. 0 (default) entails infinite loop.

TEXT KWARGS
----------------------
font : str
font for all text in figure.
fontcolor : str, list, tuple
font color for all text in figure
titlefontsize : str
Size of title font (default: medium). See matplotlib "fontsize"
titleloc : str,
Location of title (default: center). See matplotlib "loc"
titleweight : str
Font weight of title (default: regular). See matplotlib "fontweight"

STYLE KWARGS
--------------------------
profile : str
path or name of file in netplotbrain/profiles/<filename>.json, specifies default kwargs.
Default points to netplotbrain/profiles/default.json

24 changes: 18 additions & 6 deletions netplotbrain/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
_scale_nodes, _add_axis_arrows, _plot_parcels,\
_select_single_hemisphere_nodes, _add_subplot_title, get_frame_input,\
_setup_legend, _process_edge_input, _process_node_input,\
_add_nodesize_legend, _add_nodecolor_legend, _init_figure, _check_axinput
_add_nodesize_legend, _add_nodecolor_legend, _init_figure, _check_axinput, \
_plot_gif
from .utils import _highlight_nodes, _get_colorby_colors, _set_axes_equal, _get_view, _load_profile, _nrows_in_fig

def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', frames=1, edges=None, template=None, templatestyle='filled',
Expand All @@ -17,7 +18,7 @@ def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', fra
Plot a network on a brain
Arguments:
nodes pd.dataframe, str:
nodes pd.dataframe, str:
The dataframe must include x, y, z columns that correspond to coordinates of nodes
(see nodecols to change this).
Can include additional infomation for node size and color.
Expand All @@ -29,12 +30,12 @@ def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', fra
if list: multiple strings (as above) which will create new rows of subplots.
if tuple: (azim, elev) where azim rotates along xy, and elev rotates along xz.
If LR or AP view combinations only, you can specify i.e. 'AP-' to rotate in the opposite direction
nodes : dataframe, string
The dataframe must include x, y, z columns that correspond to coordinates of nodes (see nodecols to change this).
Can include additional infomation for node size and color.
If string, can load a tsv file (tab seperator), assumes index column is the 0th column.
nodeimg : str or nii
String to filename or nibabel object that contains nodes as int.
edges : dataframe, numpy array, or string
Expand Down Expand Up @@ -82,7 +83,7 @@ def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', fra
Default auto, will describe the view settings.
Otherwise string or list of for subplot titles
For more kew word arguments, see `netplotbrain.node-kwargs`
For more key word arguments, see `netplotbrain.node-kwargs`
Returns
--------
Expand Down Expand Up @@ -211,7 +212,7 @@ def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', fra
ax_out.append(ax)

# Add legends to plot
if legends is not None:
if legends is not None and profile['gif'] == False:
for li, legend in enumerate(legends):
# setup legend subplot. Goes in centre or centre2 subplots
spind = gridspec.ncols
Expand All @@ -230,4 +231,15 @@ def plot(nodes=None, fig: Optional[plt.Figure]=None, ax=None, view: str='L', fra
ax_out.append(ax)
fig.tight_layout()

if profile['gif'] is True:
_plot_gif(fig, ax_out, profile['gifduration'], profile['savename'], profile['gifloop'])
elif profile['savename'] is not None:
if profile['savename'].endswith('.png'):
fig.savedfig(profile['savename'], dpi=profile['fig_dpi'])
elif profile['savename'].endswith('.svg'):
fig.savedfig(profile['savename'])
else:
fig.savedfig(profile['savename'] + '.png', dpi=profile['fig_dpi'])
fig.savedfig(profile['savename'] + '.svg')

return (fig, ax_out)
3 changes: 2 additions & 1 deletion netplotbrain/plotting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .process_input import get_frame_input, _process_edge_input, _process_node_input,\
_init_figure, _check_axinput
from .plot_legend import _add_nodecolor_legend, _add_nodesize_legend, _setup_legend
from .plot_gif import _plot_gif

__all__ = ['_plot_template', '_plot_template_style_filled',
'_plot_template_style_cloudy',
Expand All @@ -18,4 +19,4 @@
'_add_axis_arrows', '_get_nodes_from_nii', '_plot_parcels',
'_select_single_hemisphere_nodes', '_npedges2dfedges', '_add_subplot_title',
'_setup_legend', '_process_edge_input', '_process_node_input', '_add_nodesize_legend',
'_add_nodecolor_legend', 'get_frame_input', '_init_figure', '_check_axinput']
'_add_nodecolor_legend', 'get_frame_input', '_init_figure', '_check_axinput', '_plot_gif']
24 changes: 15 additions & 9 deletions netplotbrain/create_gif.py → netplotbrain/plotting/plot_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from PIL import Image


def create_gif(fig, ax_out, duration, loop, gifname='netplotbrain.gif'):
def _plot_gif(fig, ax, gifduration, savename=None, gif_loop=0):

"""
Create a GIF from plot
Expand All @@ -17,27 +17,33 @@ def create_gif(fig, ax_out, duration, loop, gifname='netplotbrain.gif'):
fig : matplotlib figure
duration: each frame in ms, int (600 is suggested)
gifduration: each frame in ms, int (600 is suggested)
loop: number of loops, int
gif_loop: number of loops, int
If 0, it becomes an infinite loop
gifname: str (default 'netplotbrain.gif')
savename: str (default 'netplotbrain.gif')
Name of the saved GIF
"""

#Ensure that savename ends in gif
if savename is None:
raise ValueError('savename must be specified to save gif')
# Add give to end of the
if savename.endswith('.gif') == False:
savename += '.gif'

#saving matplotlib figures (frame images) to buffer and opening in PIL Image objects

images = []

for i, currax in enumerate(ax_out):
extent = currax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
for i, current_ax in enumerate(ax):
extent = current_ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
buf = io.BytesIO()
fig.savefig(buf, bbox_inches=extent, format='PNG', dpi=300)
buf.seek(i)
img = Image.open(buf).convert('RGBA')
images.append(img)

images[0].save(gifname,
save_all=True, append_images=images[1:], optimize=False, duration=duration, loop=loop)
images[0].save(savename,
save_all=True, append_images=images[1:], optimize=False, duration=gifduration, loop=gif_loop)
4 changes: 4 additions & 0 deletions netplotbrain/profiles/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"edgehighlightbehaviour": "both",
"font": "DejaVu Sans",
"fontcolor": "gray",
"gif": false,
"gifloop": 0,
"gifduration": 1000,
"highlightlevel": 0.85,
"legendtickfontsize": "xx-small",
"legendtitilefontsize": "medium",
Expand All @@ -16,6 +19,7 @@
"nodescale": 5,
"nodesizelegend": true,
"nodesizevminvmax": "minmax",
"savename": null,
"surface_resolution": 2,
"surface_detection": null,
"templatealpha": 0.2,
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ templateflow>=0.6.3
numpy>=1.18.1
scipy>=1.4.1
scikit-image>=0.17.2
pandas>=1.2.4
pandas>=1.2.4
pillow>=8.4.0

0 comments on commit 816a3ad

Please sign in to comment.