bsplot 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bsplot/__init__.py +23 -0
- bsplot/anat.py +55 -0
- bsplot/animate.py +194 -0
- bsplot/bioicons.py +777 -0
- bsplot/brain.py +188 -0
- bsplot/colors.py +1935 -0
- bsplot/data/HCP_avg-SC.txt +379 -0
- bsplot/data/MNI152.rh.pial +0 -0
- bsplot/data/STN_lh.nii.gz +0 -0
- bsplot/data/STN_rh.nii.gz +0 -0
- bsplot/data/__init__.py +22 -0
- bsplot/data/left_electrode.ply +257270 -0
- bsplot/data/parcellations/HCP-MMP1.L.label.gii +442 -0
- bsplot/data/parcellations/HCP-MMP1.R.label.gii +442 -0
- bsplot/data/parcellations/code/split_hcpmmp1.sh +4 -0
- bsplot/data/right_electrode.ply +257270 -0
- bsplot/data/surface.py +112 -0
- bsplot/data/tpl-MNI152NLin2009b_atlas-hcpmmp1_desc-ordered_dseg.nii.gz +0 -0
- bsplot/figure.py +177 -0
- bsplot/graph/__init__.py +31 -0
- bsplot/graph/edges.py +312 -0
- bsplot/graph/flowchart.py +762 -0
- bsplot/graph/layout.py +201 -0
- bsplot/graph/network.py +974 -0
- bsplot/graph/nodes.py +452 -0
- bsplot/panels.py +110 -0
- bsplot/scientific_color_maps/__init__.py +0 -0
- bsplot/scientific_color_maps/_sync_from_zenodo_download.sh +1 -0
- bsplot/scientific_color_maps/batlowK.txt +256 -0
- bsplot/scientific_color_maps/batlowW.txt +256 -0
- bsplot/scientific_color_maps/cyclic/bamO.txt +256 -0
- bsplot/scientific_color_maps/cyclic/brocO.txt +256 -0
- bsplot/scientific_color_maps/cyclic/corkO.txt +256 -0
- bsplot/scientific_color_maps/cyclic/romaO.txt +256 -0
- bsplot/scientific_color_maps/cyclic/vikO.txt +256 -0
- bsplot/scientific_color_maps/diverging/bam.txt +256 -0
- bsplot/scientific_color_maps/diverging/berlin.txt +256 -0
- bsplot/scientific_color_maps/diverging/broc.txt +256 -0
- bsplot/scientific_color_maps/diverging/cork.txt +256 -0
- bsplot/scientific_color_maps/diverging/lisbon.txt +256 -0
- bsplot/scientific_color_maps/diverging/managua.txt +256 -0
- bsplot/scientific_color_maps/diverging/roma.txt +256 -0
- bsplot/scientific_color_maps/diverging/tofino.txt +256 -0
- bsplot/scientific_color_maps/diverging/vanimo.txt +256 -0
- bsplot/scientific_color_maps/diverging/vik.txt +256 -0
- bsplot/scientific_color_maps/multisequential/bukavu.txt +256 -0
- bsplot/scientific_color_maps/multisequential/fes.txt +256 -0
- bsplot/scientific_color_maps/multisequential/oleron.txt +256 -0
- bsplot/scientific_color_maps/naviaW.txt +256 -0
- bsplot/scientific_color_maps/sequential/acton.txt +256 -0
- bsplot/scientific_color_maps/sequential/bamako.txt +256 -0
- bsplot/scientific_color_maps/sequential/batlow.txt +256 -0
- bsplot/scientific_color_maps/sequential/bilbao.txt +256 -0
- bsplot/scientific_color_maps/sequential/buda.txt +256 -0
- bsplot/scientific_color_maps/sequential/davos.txt +256 -0
- bsplot/scientific_color_maps/sequential/devon.txt +256 -0
- bsplot/scientific_color_maps/sequential/glasgow.txt +256 -0
- bsplot/scientific_color_maps/sequential/grayC.txt +256 -0
- bsplot/scientific_color_maps/sequential/hawaii.txt +256 -0
- bsplot/scientific_color_maps/sequential/imola.txt +256 -0
- bsplot/scientific_color_maps/sequential/lajolla.txt +256 -0
- bsplot/scientific_color_maps/sequential/lapaz.txt +256 -0
- bsplot/scientific_color_maps/sequential/lipari.txt +256 -0
- bsplot/scientific_color_maps/sequential/navia.txt +256 -0
- bsplot/scientific_color_maps/sequential/nuuk.txt +256 -0
- bsplot/scientific_color_maps/sequential/oslo.txt +256 -0
- bsplot/scientific_color_maps/sequential/tokyo.txt +256 -0
- bsplot/scientific_color_maps/sequential/turku.txt +256 -0
- bsplot/streamlines.py +314 -0
- bsplot/style.py +791 -0
- bsplot/styles/black.mplstyle +78 -0
- bsplot/styles/bss.mplstyle +50 -0
- bsplot/styles/bwcomp.mplstyle +30 -0
- bsplot/styles/nature.mplstyle +57 -0
- bsplot/styles/transparent.mplstyle +16 -0
- bsplot/styles/tvbo.mplstyle +57 -0
- bsplot/surface.py +2370 -0
- bsplot/templates.py +270 -0
- bsplot/text.py +199 -0
- bsplot/text2obj.py +59 -0
- bsplot/timeseries.py +10 -0
- bsplot/utils.py +13 -0
- bsplot/volume.py +958 -0
- bsplot-0.0.2.dist-info/METADATA +60 -0
- bsplot-0.0.2.dist-info/RECORD +89 -0
- bsplot-0.0.2.dist-info/WHEEL +5 -0
- bsplot-0.0.2.dist-info/entry_points.txt +2 -0
- bsplot-0.0.2.dist-info/licenses/LICENSE +193 -0
- bsplot-0.0.2.dist-info/top_level.txt +1 -0
bsplot/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from os.path import abspath, dirname, join
|
|
2
|
+
|
|
3
|
+
__version__ = "0.0.2"
|
|
4
|
+
|
|
5
|
+
ROOT = abspath(dirname(__file__))
|
|
6
|
+
datadir = join(ROOT, "data")
|
|
7
|
+
|
|
8
|
+
from . import volume, anat, animate, brain, colors, data, figure, streamlines, style, timeseries, surface, templates, utils
|
|
9
|
+
from .surface import vol_to_mesh, LabeledMeshes, project_vol_to_surf, plot_surf
|
|
10
|
+
from .brain import glass_brain
|
|
11
|
+
from .volume import plot_slice, apply_brain_mask
|
|
12
|
+
from .colors import explore_colormaps
|
|
13
|
+
from .bioicons import (
|
|
14
|
+
BioIcons,
|
|
15
|
+
plot_icon,
|
|
16
|
+
plot_icons_grid,
|
|
17
|
+
IconAnnotation,
|
|
18
|
+
search_icons,
|
|
19
|
+
list_categories,
|
|
20
|
+
list_licenses,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
style.use("tvbo")
|
bsplot/anat.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Backwards compatibility module. Use bsplot.volume instead."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
import inspect
|
|
5
|
+
|
|
6
|
+
# Only warn if imported directly by user code, not by bsplot's __init__.py
|
|
7
|
+
_stack = inspect.stack()
|
|
8
|
+
if len(_stack) >= 2:
|
|
9
|
+
_caller_filename = _stack[1].filename
|
|
10
|
+
if "bsplot/__init__.py" not in _caller_filename and "bsplot\\__init__.py" not in _caller_filename:
|
|
11
|
+
warnings.warn(
|
|
12
|
+
"bsplot.anat is deprecated, use bsplot.volume instead. "
|
|
13
|
+
"Also note that plot_img has been renamed to plot_slice.",
|
|
14
|
+
DeprecationWarning,
|
|
15
|
+
stacklevel=2,
|
|
16
|
+
)
|
|
17
|
+
del _stack, inspect
|
|
18
|
+
|
|
19
|
+
from bsplot.volume import (
|
|
20
|
+
get_center_of_mass,
|
|
21
|
+
get_cut_coords,
|
|
22
|
+
get_com_slice_index,
|
|
23
|
+
get_slice_index,
|
|
24
|
+
get_world_coords,
|
|
25
|
+
get_view_index,
|
|
26
|
+
get_plane_indices,
|
|
27
|
+
plot_slice,
|
|
28
|
+
plot_img,
|
|
29
|
+
translate_com,
|
|
30
|
+
get_com_view,
|
|
31
|
+
plot_img_on_bg,
|
|
32
|
+
plot_brain_schematics,
|
|
33
|
+
get_world_extent,
|
|
34
|
+
plot_image_extent,
|
|
35
|
+
apply_brain_mask,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"get_center_of_mass",
|
|
40
|
+
"get_cut_coords",
|
|
41
|
+
"get_com_slice_index",
|
|
42
|
+
"get_slice_index",
|
|
43
|
+
"get_world_coords",
|
|
44
|
+
"get_view_index",
|
|
45
|
+
"get_plane_indices",
|
|
46
|
+
"plot_slice",
|
|
47
|
+
"plot_img",
|
|
48
|
+
"translate_com",
|
|
49
|
+
"get_com_view",
|
|
50
|
+
"plot_img_on_bg",
|
|
51
|
+
"plot_brain_schematics",
|
|
52
|
+
"get_world_extent",
|
|
53
|
+
"plot_image_extent",
|
|
54
|
+
"apply_brain_mask",
|
|
55
|
+
]
|
bsplot/animate.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Composable animation utilities for 4D neuroimaging data (matplotlib-only).
|
|
2
|
+
|
|
3
|
+
The core abstraction is :class:`AnimationComposer`, which manages a shared
|
|
4
|
+
timeline and lets you plug in per-axis animation callbacks. All higher-level
|
|
5
|
+
patterns (single view, multi-view, multi-image comparison) can be expressed
|
|
6
|
+
via this composer and a small generic helper.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import nibabel as nib
|
|
11
|
+
import numpy as np
|
|
12
|
+
from matplotlib.animation import FuncAnimation
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AnimationComposer:
|
|
16
|
+
"""Composable multi-axes animation helper (matplotlib-only).
|
|
17
|
+
|
|
18
|
+
This class lets you register per-axis update functions that are called for
|
|
19
|
+
every frame with the same frame index, so you can build complex, modular
|
|
20
|
+
animations across several axes.
|
|
21
|
+
|
|
22
|
+
Each registered callback should have the signature::
|
|
23
|
+
|
|
24
|
+
func(frame_index: int, ax: matplotlib.axes.Axes) -> list[Artist] | Artist | None
|
|
25
|
+
|
|
26
|
+
and is expected to draw on the provided axis.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, fig, axes, n_frames, interval=100, blit=False):
|
|
30
|
+
"""Create a new AnimationComposer.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
fig : matplotlib.figure.Figure
|
|
35
|
+
Parent figure for the animation.
|
|
36
|
+
axes : Axes or sequence of Axes
|
|
37
|
+
One or more axes to animate.
|
|
38
|
+
n_frames : int
|
|
39
|
+
Number of frames (time points).
|
|
40
|
+
interval : int, default 100
|
|
41
|
+
Delay between frames in milliseconds.
|
|
42
|
+
blit : bool, default False
|
|
43
|
+
Whether to use blitting.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
self.fig = fig
|
|
47
|
+
if hasattr(axes, "__iter__") and not isinstance(axes, (list, tuple)):
|
|
48
|
+
self.axes = list(axes)
|
|
49
|
+
elif hasattr(axes, "__iter__"):
|
|
50
|
+
self.axes = list(axes)
|
|
51
|
+
else:
|
|
52
|
+
self.axes = [axes]
|
|
53
|
+
|
|
54
|
+
self.n_frames = int(n_frames)
|
|
55
|
+
self.interval = interval
|
|
56
|
+
self.blit = blit
|
|
57
|
+
self._callbacks = [] # list of (ax, func)
|
|
58
|
+
|
|
59
|
+
def add_axis_animation(self, ax, func):
|
|
60
|
+
"""Register an animation callback for a given axis.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
ax : matplotlib.axes.Axes
|
|
65
|
+
Axis that ``func`` will draw on.
|
|
66
|
+
func : callable
|
|
67
|
+
Function with signature ``func(frame_index: int, ax)`` that returns
|
|
68
|
+
a Matplotlib Artist or a list of Artists (optional).
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
if ax not in self.axes:
|
|
72
|
+
self.axes.append(ax)
|
|
73
|
+
self._callbacks.append((ax, func))
|
|
74
|
+
|
|
75
|
+
def _update(self, frame):
|
|
76
|
+
artists = []
|
|
77
|
+
for ax, func in self._callbacks:
|
|
78
|
+
res = func(frame, ax)
|
|
79
|
+
if res is None:
|
|
80
|
+
continue
|
|
81
|
+
if isinstance(res, (list, tuple)):
|
|
82
|
+
artists.extend(res)
|
|
83
|
+
else:
|
|
84
|
+
artists.append(res)
|
|
85
|
+
return artists
|
|
86
|
+
|
|
87
|
+
def to_animation(self):
|
|
88
|
+
"""Create and return the underlying FuncAnimation object."""
|
|
89
|
+
|
|
90
|
+
anim = FuncAnimation(
|
|
91
|
+
self.fig,
|
|
92
|
+
self._update,
|
|
93
|
+
frames=self.n_frames,
|
|
94
|
+
interval=self.interval,
|
|
95
|
+
blit=self.blit,
|
|
96
|
+
)
|
|
97
|
+
return anim
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _ensure_4d(img_4d):
|
|
101
|
+
"""Load and validate a 4D NIfTI-like image."""
|
|
102
|
+
if isinstance(img_4d, str):
|
|
103
|
+
img_4d = nib.load(img_4d)
|
|
104
|
+
if len(img_4d.shape) != 4:
|
|
105
|
+
raise ValueError(f"Expected 4D image, got shape {img_4d.shape}")
|
|
106
|
+
return img_4d
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _global_scale_kwargs(img_4d, threshold=0, **plot_kwargs):
|
|
110
|
+
"""Compute global vmin/vmax across time, unless user provided scaling.
|
|
111
|
+
|
|
112
|
+
Returns a dict with optional ``vmin``/``vmax`` to forward into ``plot_img``.
|
|
113
|
+
"""
|
|
114
|
+
if "vmin" in plot_kwargs or "norm" in plot_kwargs:
|
|
115
|
+
return {}
|
|
116
|
+
|
|
117
|
+
data_4d = img_4d.get_fdata()
|
|
118
|
+
if threshold > 0:
|
|
119
|
+
masked = np.where(np.abs(data_4d) <= threshold, np.nan, data_4d)
|
|
120
|
+
vmin = np.nanmin(masked)
|
|
121
|
+
vmax = np.nanmax(masked)
|
|
122
|
+
else:
|
|
123
|
+
nonzero = data_4d[data_4d != 0]
|
|
124
|
+
vmin = np.nanmin(nonzero) if nonzero.size else np.nanmin(data_4d)
|
|
125
|
+
vmax = np.nanmax(data_4d)
|
|
126
|
+
|
|
127
|
+
contains_negative = np.any(data_4d < 0)
|
|
128
|
+
if contains_negative:
|
|
129
|
+
vmax_abs = max(abs(vmin), abs(vmax))
|
|
130
|
+
vmin, vmax = -vmax_abs, vmax_abs
|
|
131
|
+
|
|
132
|
+
return {"vmin": vmin, "vmax": vmax}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def animate_axes(fig, axes, n_frames, update_fn, context=None, interval=100, blit=False):
|
|
136
|
+
"""Convenience wrapper to build an animation over one or many axes.
|
|
137
|
+
|
|
138
|
+
This captures the common pattern used in the docs: one timeline, multiple
|
|
139
|
+
axes, and a single user-defined ``update_fn`` that can branch on the axis
|
|
140
|
+
index and/or shared ``context``.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
fig : matplotlib.figure.Figure
|
|
145
|
+
Parent figure for the animation.
|
|
146
|
+
axes : Axes or sequence of Axes
|
|
147
|
+
Axes to animate. Can be a single axis, a list/tuple, or any iterable
|
|
148
|
+
of axes as returned by ``plt.subplots``.
|
|
149
|
+
n_frames : int
|
|
150
|
+
Number of frames in the animation.
|
|
151
|
+
update_fn : callable
|
|
152
|
+
Function with signature::
|
|
153
|
+
|
|
154
|
+
update_fn(frame: int, ax, axis_idx: int, context: dict | None)
|
|
155
|
+
|
|
156
|
+
It should draw the desired state for the given ``frame`` on ``ax`` and
|
|
157
|
+
return a Matplotlib Artist or list of Artists (or ``None``).
|
|
158
|
+
context : dict or None, optional
|
|
159
|
+
Arbitrary, user-defined state shared across axes and frames.
|
|
160
|
+
interval : int, default 100
|
|
161
|
+
Delay between frames in milliseconds.
|
|
162
|
+
blit : bool, default False
|
|
163
|
+
Whether to use blitting.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
matplotlib.animation.FuncAnimation
|
|
168
|
+
The constructed animation object.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
# Normalize axes into a flat list
|
|
172
|
+
if hasattr(axes, "__iter__") and not isinstance(axes, (list, tuple)):
|
|
173
|
+
axes_list = list(axes)
|
|
174
|
+
elif hasattr(axes, "__iter__"):
|
|
175
|
+
axes_list = list(axes)
|
|
176
|
+
else:
|
|
177
|
+
axes_list = [axes]
|
|
178
|
+
|
|
179
|
+
composer = AnimationComposer(fig, axes_list, n_frames=n_frames, interval=interval, blit=blit)
|
|
180
|
+
|
|
181
|
+
for axis_idx, ax in enumerate(axes_list):
|
|
182
|
+
|
|
183
|
+
def make_cb(idx_):
|
|
184
|
+
def _cb(frame, ax_cb):
|
|
185
|
+
return update_fn(frame, ax_cb, idx_, context)
|
|
186
|
+
|
|
187
|
+
return _cb
|
|
188
|
+
|
|
189
|
+
composer.add_axis_animation(ax, make_cb(axis_idx))
|
|
190
|
+
|
|
191
|
+
return composer.to_animation()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|