Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 9484594

Browse filesBrowse files
authored
Merge pull request #148 from kushalkolar/image_fixes
ImageWidget fix frame_apply(), add grid_plot_kwargs(), reset_vmin_vmax()
2 parents aec82dc + 5161429 commit 9484594
Copy full SHA for 9484594

File tree

2 files changed

+75
-24
lines changed
Filter options

2 files changed

+75
-24
lines changed

‎fastplotlib/layouts/_gridplot.py

Copy file name to clipboardExpand all lines: fastplotlib/layouts/_gridplot.py
+26-16Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ def __init__(
7171
# create the array representing the views for each subplot in the grid
7272
cameras = np.array([cameras] * self.shape[0] * self.shape[1]).reshape(self.shape)
7373

74-
if controllers == "sync":
75-
controllers = np.zeros(self.shape[0] * self.shape[1], dtype=int).reshape(self.shape)
74+
if isinstance(controllers, str):
75+
if controllers == "sync":
76+
controllers = np.zeros(self.shape[0] * self.shape[1], dtype=int).reshape(self.shape)
7677

7778
if controllers is None:
7879
controllers = np.arange(self.shape[0] * self.shape[1]).reshape(self.shape)
@@ -82,13 +83,31 @@ def __init__(
8283
if controllers.shape != self.shape:
8384
raise ValueError
8485

86+
self._controllers = np.empty(shape=cameras.shape, dtype=object)
87+
8588
cameras = to_array(cameras)
8689

8790
if cameras.shape != self.shape:
8891
raise ValueError
8992

90-
if not np.all(np.sort(np.unique(controllers)) == np.arange(np.unique(controllers).size)):
91-
raise ValueError("controllers must be consecutive integers")
93+
# create controllers if the arguments were integers
94+
if np.issubdtype(controllers.dtype, np.integer):
95+
if not np.all(np.sort(np.unique(controllers)) == np.arange(np.unique(controllers).size)):
96+
raise ValueError("controllers must be consecutive integers")
97+
98+
for controller in np.unique(controllers):
99+
cam = np.unique(cameras[controllers == controller])
100+
if cam.size > 1:
101+
raise ValueError(
102+
f"Controller id: {controller} has been assigned to multiple different camera types")
103+
104+
self._controllers[controllers == controller] = create_controller(cam[0])
105+
# else assume it's a single pygfx.Controller instance or a list of controllers
106+
else:
107+
if isinstance(controllers, pygfx.Controller):
108+
self._controllers = np.array([controllers] * shape[0] * shape[1]).reshape(shape)
109+
else:
110+
self._controllers = np.array(controllers).reshape(shape)
92111

93112
if canvas is None:
94113
canvas = WgpuCanvas()
@@ -111,18 +130,9 @@ def __init__(
111130
self._subplots: np.ndarray[Subplot] = np.ndarray(shape=(nrows, ncols), dtype=object)
112131
# self.viewports: np.ndarray[Subplot] = np.ndarray(shape=(nrows, ncols), dtype=object)
113132

114-
self._controllers: List[pygfx.PanZoomController] = [
115-
pygfx.PanZoomController() for i in range(np.unique(controllers).size)
116-
]
117-
118-
self._controllers = np.empty(shape=cameras.shape, dtype=object)
119-
120-
for controller in np.unique(controllers):
121-
cam = np.unique(cameras[controllers == controller])
122-
if cam.size > 1:
123-
raise ValueError(f"Controller id: {controller} has been assigned to multiple different camera types")
124-
125-
self._controllers[controllers == controller] = create_controller(cam[0])
133+
# self._controllers: List[pygfx.PanZoomController] = [
134+
# pygfx.PanZoomController() for i in range(np.unique(controllers).size)
135+
# ]
126136

127137
for i, j in self._get_iterator():
128138
position = (i, j)

‎fastplotlib/widgets/image.py

Copy file name to clipboardExpand all lines: fastplotlib/widgets/image.py
+49-8Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ def __init__(
170170
vmin_vmax_sliders: bool = False,
171171
grid_shape: Tuple[int, int] = None,
172172
names: List[str] = None,
173+
grid_plot_kwargs: dict = None,
173174
**kwargs
174175
):
175176
"""
@@ -227,6 +228,9 @@ def __init__(
227228
grid_shape: Optional[Tuple[int, int]]
228229
manually provide the shape for a gridplot, otherwise a square gridplot is approximated.
229230
231+
grid_plot_kwargs: dict, optional
232+
passed to `GridPlot`
233+
230234
names: Optional[str]
231235
gives names to the subplots
232236
@@ -471,12 +475,12 @@ def __init__(
471475

472476
if vmin_vmax_sliders:
473477
data_range = np.ptp(minmax)
474-
data_range_30p = np.ptp(minmax) * 0.3
478+
data_range_40p = np.ptp(minmax) * 0.3
475479

476480
minmax_slider = FloatRangeSlider(
477481
value=minmax,
478-
min=minmax[0] - data_range_30p,
479-
max=minmax[1] + data_range_30p,
482+
min=minmax[0] - data_range_40p,
483+
max=minmax[1] + data_range_40p,
480484
step=data_range / 150,
481485
description=f"min-max",
482486
readout=True,
@@ -494,11 +498,15 @@ def __init__(
494498
kwargs["vmin"], kwargs["vmax"] = minmax
495499

496500
frame = self._process_indices(self.data[0], slice_indices=self._current_index)
501+
frame = self._process_frame_apply(frame, 0)
497502

498503
self.image_graphics: List[ImageGraphic] = [self.plot.add_image(data=frame, name="image", **kwargs)]
499504

500505
elif self._plot_type == "grid":
501-
self._plot: GridPlot = GridPlot(shape=grid_shape, controllers="sync")
506+
if grid_plot_kwargs is None:
507+
grid_plot_kwargs = {"controllers": "sync"}
508+
509+
self._plot: GridPlot = GridPlot(shape=grid_shape, **grid_plot_kwargs)
502510

503511
self.image_graphics = list()
504512
for data_ix, (d, subplot) in enumerate(zip(self.data, self.plot)):
@@ -513,12 +521,12 @@ def __init__(
513521

514522
if vmin_vmax_sliders:
515523
data_range = np.ptp(minmax)
516-
data_range_30p = np.ptp(minmax) * 0.4
524+
data_range_40p = np.ptp(minmax) * 0.4
517525

518526
minmax_slider = FloatRangeSlider(
519527
value=minmax,
520-
min=minmax[0] - data_range_30p,
521-
max=minmax[1] + data_range_30p,
528+
min=minmax[0] - data_range_40p,
529+
max=minmax[1] + data_range_40p,
522530
step=data_range / 150,
523531
description=f"mm: {name_slider}",
524532
readout=True,
@@ -539,6 +547,7 @@ def __init__(
539547
_kwargs = kwargs
540548

541549
frame = self._process_indices(d, slice_indices=self._current_index)
550+
frame = self._process_frame_apply(frame, data_ix)
542551
ig = ImageGraphic(frame, name="image", **_kwargs)
543552
subplot.add_graphic(ig)
544553
subplot.name = name
@@ -767,11 +776,17 @@ def _get_window_indices(self, data_ix, dim, indices_dim):
767776
return indices_dim
768777

769778
def _process_frame_apply(self, array, data_ix) -> np.ndarray:
779+
if callable(self.frame_apply):
780+
return self.frame_apply(array)
781+
770782
if data_ix not in self.frame_apply.keys():
771783
return array
772-
if self.frame_apply[data_ix] is not None:
784+
785+
elif self.frame_apply[data_ix] is not None:
773786
return self.frame_apply[data_ix](array)
774787

788+
return array
789+
775790
def _slider_value_changed(
776791
self,
777792
dimension: str,
@@ -801,6 +816,32 @@ def _set_slider_layout(self, *args):
801816
for mm in self.vmin_vmax_sliders:
802817
mm.layout = Layout(width=f"{w}px")
803818

819+
def _get_vmin_vmax_range(self, data: np.ndarray) -> Tuple[int, int]:
820+
minmax = quick_min_max(data)
821+
822+
data_range = np.ptp(minmax)
823+
data_range_40p = np.ptp(minmax) * 0.4
824+
825+
_range = (
826+
minmax,
827+
data_range,
828+
minmax[0] - data_range_40p,
829+
minmax[1] + data_range_40p
830+
)
831+
832+
return _range
833+
834+
def reset_vmin_vmax(self):
835+
"""
836+
Reset the vmin and vmax w.r.t. the currently displayed image(s)
837+
"""
838+
for i, ig in enumerate(self.image_graphics):
839+
mm = self._get_vmin_vmax_range(ig.data())
840+
self.vmin_vmax_sliders[i].min = mm[2]
841+
self.vmin_vmax_sliders[i].max = mm[3]
842+
self.vmin_vmax_sliders[i].step = mm[1] / 150
843+
self.vmin_vmax_sliders[i].value = mm[0]
844+
804845
def show(self):
805846
"""
806847
Show the widget

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.