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 718c98c

Browse filesBrowse files
optimized array subsampling (#721)
* add 4D case with a single z-plane * add subsample_array function * use new subsample_array function * remove redundant target_elements from subsample * import utils from __all__ * rename subsample vars using standard numpy names * subsample max_items -> max_size * largest bytesize -> largest array size, lowercase errrrything * use subsample_array in quick_min_max * make 1e6 int * update png from artifact (imgui-screenshots) * fix subsample array * bring back original image_widget_grid * Update functions.py * revert image_widget_grid from regenerate * hopefully the correct screenshot * black * replace iw-zfish-grid-qreplace nb-iw-zfish-grid-init-mw5 to see * replace nb-image-widget screenshots * force git to refresh * nb-iw from regen screenshots * plzplzplzplzplz work --------- Co-authored-by: Kushal Kolar <kushalkolar@gmail.com>
1 parent be8e0c8 commit 718c98c
Copy full SHA for 718c98c

File tree

32 files changed

+129
-87
lines changed
Filter options

32 files changed

+129
-87
lines changed
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+2-2Lines changed: 2 additions & 2 deletions
Loading
+3-2Lines changed: 3 additions & 2 deletions
Loading

‎fastplotlib/tools/_histogram_lut.py

Copy file name to clipboardExpand all lines: fastplotlib/tools/_histogram_lut.py
+4-21Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import pygfx
77

8+
from ..utils import subsample_array
89
from ..graphics import LineGraphic, ImageGraphic, TextGraphic
910
from ..graphics.utils import pause_events
1011
from ..graphics._base import Graphic
@@ -193,28 +194,10 @@ def _fpl_add_plot_area_hook(self, plot_area):
193194
self._plot_area.controller.enabled = True
194195

195196
def _calculate_histogram(self, data):
196-
if data.ndim > 2:
197-
# subsample to max of 500 x 100 x 100,
198-
# np.histogram takes ~30ms with this size on a 8 core Ryzen laptop
199-
# dim0 is usually time, allow max of 500 timepoints
200-
ss0 = max(1, int(data.shape[0] / 500)) # max to prevent step = 0
201-
# allow max of 100 for x and y if ndim > 2
202-
ss1 = max(1, int(data.shape[1] / 100))
203-
ss2 = max(1, int(data.shape[2] / 100))
204197

205-
data_ss = data[::ss0, ::ss1, ::ss2]
206-
207-
hist, edges = np.histogram(data_ss, bins=self._nbins)
208-
209-
else:
210-
# allow max of 1000 x 1000
211-
# this takes ~4ms on a 8 core Ryzen laptop
212-
ss0 = max(1, int(data.shape[0] / 1_000))
213-
ss1 = max(1, int(data.shape[1] / 1_000))
214-
215-
data_ss = data[::ss0, ::ss1]
216-
217-
hist, edges = np.histogram(data_ss, bins=self._nbins)
198+
# get a subsampled view of this array
199+
data_ss = subsample_array(data, max_size=int(1e6)) # 1e6 is default
200+
hist, edges = np.histogram(data_ss, bins=self._nbins)
218201

219202
# used if data ptp <= 10 because event things get weird
220203
# with tiny world objects due to floating point error

‎fastplotlib/utils/functions.py

Copy file name to clipboardExpand all lines: fastplotlib/utils/functions.py
+64-6Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ def make_colors_dict(labels: Sequence, cmap: str, **kwargs) -> OrderedDict:
267267
return OrderedDict(zip(labels, colors))
268268

269269

270-
def quick_min_max(data: np.ndarray) -> tuple[float, float]:
270+
def quick_min_max(data: np.ndarray, max_size=1e6) -> tuple[float, float]:
271271
"""
272272
Adapted from pyqtgraph.ImageView.
273273
Estimate the min/max values of *data* by subsampling.
@@ -276,6 +276,9 @@ def quick_min_max(data: np.ndarray) -> tuple[float, float]:
276276
----------
277277
data: np.ndarray or array-like with `min` and `max` attributes
278278
279+
max_size : int, optional
280+
largest array size allowed in the subsampled array. Default is 1e6.
281+
279282
Returns
280283
-------
281284
(float, float)
@@ -289,11 +292,7 @@ def quick_min_max(data: np.ndarray) -> tuple[float, float]:
289292
):
290293
return data.min, data.max
291294

292-
while np.prod(data.shape) > 1e6:
293-
ax = np.argmax(data.shape)
294-
sl = [slice(None)] * data.ndim
295-
sl[ax] = slice(None, None, 2)
296-
data = data[tuple(sl)]
295+
data = subsample_array(data, max_size=max_size)
297296

298297
return float(np.nanmin(data)), float(np.nanmax(data))
299298

@@ -405,3 +404,62 @@ def parse_cmap_values(
405404
colors = np.vstack([colormap[val] for val in norm_cmap_values])
406405

407406
return colors
407+
408+
409+
def subsample_array(arr: np.ndarray, max_size: int = 1e6):
410+
"""
411+
Subsamples an input array while preserving its relative dimensional proportions.
412+
413+
The dimensions (shape) of the array can be represented as:
414+
415+
.. math::
416+
417+
[d_1, d_2, \\dots d_n]
418+
419+
The product of the dimensions can be represented as:
420+
421+
.. math::
422+
423+
\\prod_{i=1}^{n} d_i
424+
425+
To find the factor ``f`` by which to divide the size of each dimension in order to
426+
get max_size ``s`` we must solve for ``f`` in the following expression:
427+
428+
.. math::
429+
430+
\\prod_{i=1}^{n} \\frac{d_i}{\\mathbf{f}} = \\mathbf{s}
431+
432+
The solution for ``f`` is is simply the nth root of the product of the dims divided by the max_size
433+
where n is the number of dimensions
434+
435+
.. math::
436+
437+
\\mathbf{f} = \\sqrt[n]{\\frac{\\prod_{i=1}^{n} d_i}{\\mathbf{s}}}
438+
439+
Parameters
440+
----------
441+
arr: np.ndarray
442+
input array of any dimensionality to be subsampled.
443+
444+
max_size: int, default 1e6
445+
maximum number of elements in subsampled array
446+
447+
Returns
448+
-------
449+
np.ndarray
450+
subsample of the input array
451+
"""
452+
if np.prod(arr.shape) <= max_size:
453+
return arr # no need to subsample if already below the threshold
454+
455+
# get factor by which to divide all dims
456+
f = np.power((np.prod(arr.shape) / max_size), 1.0 / arr.ndim)
457+
458+
# new shape for subsampled array
459+
ns = np.floor(np.array(arr.shape) / f).clip(min=1)
460+
461+
# get the step size for the slices
462+
slices = tuple(
463+
slice(None, None, int(s)) for s in np.floor(arr.shape / ns).astype(int)
464+
)
465+
return np.asarray(arr[slices])

0 commit comments

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