Skip to content

Commit 2231831

Browse files
authored
Merge pull request #348 from European-XFEL/hyperslicer2
Implement hyperslicer2() for plotting image arrays
2 parents 848667d + d23f1db commit 2231831

7 files changed

Lines changed: 82 additions & 1 deletion

File tree

docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Added:
2828
(AGIPD1M, AGIPD500K, DSSC1M, JUNGFRAU, LPD1M) (!177).
2929
- [imshow2][extra.utils.imshow2] now supports plotting 2D
3030
[DataArray][xarray.DataArray]s properly (!333).
31+
- Added [hyperslicer2()][extra.utils.hyperslicer2] to make plotting image arrays
32+
easier (!348).
3133

3234
Changed:
3335
- [Timepix3.spatial_bins()] is now a static method.

docs/images/hyperslicer2.gif

1.21 MB
Loading

docs/utilities.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ else.
2020

2121
::: extra.utils.imshow2
2222

23+
::: extra.utils.hyperslicer2
24+
2325
## Fitting functions
2426

2527
::: extra.utils.fit_gaussian

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ plugins:
8888
- https://docs.xarray.dev/en/stable/objects.inv
8989
- https://pint.readthedocs.io/en/stable/objects.inv
9090
- https://docs.scipy.org/doc/scipy/objects.inv
91+
- https://mpl-interactions.readthedocs.io/en/stable/objects.inv
9192
options:
9293
docstring_style: google
9394
separate_signature: yes

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies = [
3131
"oauth2-xfel-client >=6.1",
3232
"pandas",
3333
"xarray",
34+
"mpl_interactions",
3435
"pasha",
3536
"pint",
3637
"requests",

src/extra/utils/misc.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,73 @@ def imshow2(image, *args, lognorm=False, ax=None, **kwargs):
9090
if ax is None:
9191
ax = plt
9292
return ax.imshow(image, *args, **kwargs)
93+
94+
def hyperslicer2(arr, *args, ax=None, lognorm=False, colorbar=True, **kwargs):
95+
"""Interactively visualize arrays of images.
96+
97+
This is a lightweight wrapper around
98+
[hyperslicer()][mpl_interactions.generic.hyperslicer] with some useful defaults:
99+
100+
- Try to set `vmin`/`vmax` to reasonable values. Note that setting
101+
`vmin`/`vmax` is incompatible with the `norm` argument, so they will only
102+
be set if `norm` is not passed.
103+
- Set `interpolation="none"`.
104+
- Enable the play buttons.
105+
- Draw a colorbar.
106+
107+
Example usage:
108+
```python
109+
plt.figure()
110+
# Note the trailing semi-colon to swallow the return value. hyperslicer2()
111+
# returns a `controls` object by default that displays the play buttons, so
112+
# returning it from a notebook cell will end up displaying the play buttons
113+
# twice.
114+
hyperslicer2(images);
115+
```
116+
![](../images/hyperslicer2.gif)
117+
118+
All arguments other than the ones listed below are passed to
119+
[hyperslicer()][mpl_interactions.generic.hyperslicer], and explicitly
120+
passing any of `vmin`/`vmax`/`interpolation`/`play_buttons` will override
121+
the defaults.
122+
123+
Args:
124+
arr (array_like): The array of images to display. Should have at least
125+
three dimensions.
126+
ax (matplotlib.axes.Axes): The axis to plot the image in.
127+
lognorm (bool): Whether to display the images in a log color scale.
128+
colorbar (bool): Whether to display a colorbar.
129+
"""
130+
import matplotlib.pyplot as plt
131+
from mpl_interactions import hyperslicer
132+
133+
# Enable the controls by default
134+
if "play_buttons" not in kwargs:
135+
kwargs["play_buttons"] = True
136+
137+
# Disable interpolation by default
138+
if "interpolation" not in kwargs:
139+
kwargs["interpolation"] = "none"
140+
141+
# Enable log color scale if requested and `norm` is not already set
142+
if lognorm and "norm" not in kwargs:
143+
from matplotlib.colors import LogNorm
144+
kwargs["norm"] = LogNorm()
145+
146+
# Set the vmin/vmax if we're not using `norm`
147+
if "norm" not in kwargs and np.issubdtype(arr.dtype, np.number):
148+
if "vmin" not in kwargs:
149+
kwargs["vmin"] = np.nanquantile(arr, 0.01)
150+
if "vmax" not in kwargs:
151+
kwargs["vmax"] = np.nanquantile(arr, 0.99)
152+
153+
if ax is None:
154+
ax = plt.gca()
155+
fig = ax.get_figure()
156+
157+
controls = hyperslicer(arr, *args, ax=ax, **kwargs)
158+
159+
if colorbar:
160+
fig.colorbar(ax.get_images()[-1], ax=ax)
161+
162+
return controls

tests/test_utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
import xarray as xr
33

4-
from extra.utils import imshow2, fit_gaussian, gaussian
4+
from extra.utils import imshow2, hyperslicer2, fit_gaussian, gaussian
55

66

77
def test_imshow2():
@@ -13,6 +13,11 @@ def test_imshow2():
1313
imshow2(image)
1414

1515

16+
def test_hyperslicer2():
17+
# Smoke test
18+
images = np.random.rand(10, 100, 100)
19+
hyperslicer2(images)
20+
1621
def test_fit_gaussian():
1722
# Test with auto-generated xdata and nans/infs
1823
params = [0, 1, 20, 5]

0 commit comments

Comments
 (0)