toolsandogh 0.3.3__tar.gz → 0.4.0__tar.gz

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.
Files changed (47) hide show
  1. {toolsandogh-0.3.3/src/toolsandogh.egg-info → toolsandogh-0.4.0}/PKG-INFO +1 -1
  2. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/benchmarks/bench_locate.py +12 -4
  3. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/__init__.py +1 -2
  4. toolsandogh-0.4.0/src/toolsandogh/_canonicalize_video.py +422 -0
  5. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_generate_video.py +4 -0
  6. toolsandogh-0.4.0/src/toolsandogh/_link.py +314 -0
  7. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_load_video.py +1 -1
  8. toolsandogh-0.4.0/src/toolsandogh/_locate.py +1217 -0
  9. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_simulate_particles.py +4 -0
  10. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_validate_video.py +17 -0
  11. toolsandogh-0.4.0/src/toolsandogh/tests/test_canonicalize_video.py +131 -0
  12. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/test_link.py +240 -21
  13. toolsandogh-0.4.0/src/toolsandogh/tests/test_locate.py +1342 -0
  14. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/test_simulate_particles.py +9 -8
  15. {toolsandogh-0.3.3 → toolsandogh-0.4.0/src/toolsandogh.egg-info}/PKG-INFO +1 -1
  16. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh.egg-info/SOURCES.txt +1 -0
  17. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh.egg-info/scm_file_list.json +20 -19
  18. toolsandogh-0.4.0/src/toolsandogh.egg-info/scm_version.json +8 -0
  19. toolsandogh-0.3.3/src/toolsandogh/_canonicalize_video.py +0 -282
  20. toolsandogh-0.3.3/src/toolsandogh/_link.py +0 -223
  21. toolsandogh-0.3.3/src/toolsandogh/_locate.py +0 -766
  22. toolsandogh-0.3.3/src/toolsandogh/tests/test_locate.py +0 -641
  23. toolsandogh-0.3.3/src/toolsandogh.egg-info/scm_version.json +0 -8
  24. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/.github/workflows/ci.yml +0 -0
  25. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/.github/workflows/release.yml +0 -0
  26. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/.gitignore +0 -0
  27. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/.pre-commit-config.yaml +0 -0
  28. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/DESIGN.md +0 -0
  29. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/LICENSE +0 -0
  30. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/README.md +0 -0
  31. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/demos/demo_iscat_50nm_bead.py +0 -0
  32. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/pyproject.toml +0 -0
  33. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/setup.cfg +0 -0
  34. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_rolling.py +0 -0
  35. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_rvt.py +0 -0
  36. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_simulate_psf.py +0 -0
  37. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/_store_video.py +0 -0
  38. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/__init__.py +0 -0
  39. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/conftest.py +0 -0
  40. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/test_io.py +0 -0
  41. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/test_rolling.py +0 -0
  42. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/test_simulate_psf.py +0 -0
  43. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh/tests/testfile.tiff +0 -0
  44. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh.egg-info/dependency_links.txt +0 -0
  45. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh.egg-info/requires.txt +0 -0
  46. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/src/toolsandogh.egg-info/top_level.txt +0 -0
  47. {toolsandogh-0.3.3 → toolsandogh-0.4.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toolsandogh
3
- Version: 0.3.3
3
+ Version: 0.4.0
4
4
  Summary: A collection of Python scripts for iSCAT microscopy data analysis
5
5
  Author-email: Marco Heisig <marco.heisig@mpl.mpg.de>
6
6
  License-Expression: GPL-3.0-or-later
@@ -61,8 +61,15 @@ def bench(
61
61
  psf = make_psf()
62
62
  video = make_video(n_frames, hw, psf)
63
63
 
64
- # Warmup (JIT compilation, etc.).
65
- for _ in range(warmup):
64
+ # First call includes JIT compilation; record it as the cold-start time.
65
+ t0 = time.perf_counter()
66
+ tog.locate(
67
+ video, psf, chunk_size=chunk_size, min_contrast=min_contrast, iterations=10, atol=1e-3
68
+ )
69
+ cold = time.perf_counter() - t0
70
+
71
+ # Remaining warmup.
72
+ for _ in range(warmup - 1):
66
73
  tog.locate(
67
74
  video, psf, chunk_size=chunk_size, min_contrast=min_contrast, iterations=10, atol=1e-3
68
75
  )
@@ -80,7 +87,8 @@ def bench(
80
87
  fps = n_frames / best
81
88
  print(
82
89
  f" n_frames={n_frames:4d} hw={hw:3d} chunk={str(chunk_size):>3} mc={min_contrast:.2f} "
83
- f"best={best * 1e3:8.2f} ms fps={fps:8.2f} n_locs={locs.height}"
90
+ f"cold={cold * 1e3:8.1f} ms warm={best * 1e3:8.2f} ms "
91
+ f"fps={fps:8.2f} n_locs={locs.height}"
84
92
  )
85
93
  return fps
86
94
 
@@ -88,7 +96,7 @@ def bench(
88
96
  def main() -> None:
89
97
  jax.config.update("jax_enable_x64", False)
90
98
  print(f"jax devices: {jax.devices()} backend: {jax.default_backend()}")
91
- print("Baseline locate() benchmark")
99
+ print("locate() benchmark (cold = first call incl. JIT compile, warm = best of repeats)")
92
100
  for hw in (64, 128):
93
101
  for cs in (1, 8):
94
102
  bench(64, hw, cs, min_contrast=0.2)
@@ -8,7 +8,7 @@ from ._canonicalize_video import canonicalize_video
8
8
  from ._generate_video import generate_video
9
9
  from ._link import link
10
10
  from ._load_video import load_video
11
- from ._locate import locate, locate_in_chunk
11
+ from ._locate import locate
12
12
  from ._rolling import differential_rolling_average, rolling_average, rolling_sum
13
13
  from ._rvt import radial_variance_transform
14
14
  from ._simulate_particles import simulate_particles
@@ -51,7 +51,6 @@ __all__: list[str] = [
51
51
  "differential_rolling_average",
52
52
  "link",
53
53
  "locate",
54
- "locate_in_chunk",
55
54
  "focused_incident_field",
56
55
  "generate_video",
57
56
  "image_field_from_pupil",
@@ -0,0 +1,422 @@
1
+ import sys
2
+ from datetime import datetime, timezone
3
+
4
+ import dask.array as da
5
+ import numpy as np
6
+ import numpy.typing as npt
7
+ import xarray as xr
8
+ from ome_types.model import (
9
+ OME,
10
+ Image,
11
+ Pixels,
12
+ Pixels_DimensionOrder,
13
+ PixelType,
14
+ UnitsLength,
15
+ UnitsTime,
16
+ )
17
+
18
+ from ._validate_video import validate_video
19
+
20
+
21
+ def canonicalize_video(
22
+ video: npt.ArrayLike,
23
+ # optional video properties
24
+ T: int | None = None,
25
+ C: int | None = None,
26
+ Z: int | None = None,
27
+ Y: int | None = None,
28
+ X: int | None = None,
29
+ dt: float | None = None,
30
+ dz: float | None = None,
31
+ dy: float | None = None,
32
+ dx: float | None = None,
33
+ t0: float | None = None,
34
+ z0: float | None = None,
35
+ y0: float | None = None,
36
+ x0: float | None = None,
37
+ dtype: npt.DTypeLike | None = None,
38
+ # optional video metadata
39
+ acquisition_date: datetime | None = None,
40
+ creator: str | None = None,
41
+ ) -> xr.DataArray:
42
+ """
43
+ Turn the supplied data into its canonical TCZYX video representation.
44
+
45
+ Parameters
46
+ ----------
47
+ video : npt.ArrayLike
48
+ An array-like entity with up to five axes. If the axes are not labeled
49
+ already, a 1D array is treated as having an `X` axis only, a 2D array is
50
+ treated as having dimensions `XY`, a 3D array is treated as having
51
+ dimensions `TYX`, a 4D array is treated as having dimensions `TZYX`, and
52
+ a 5D array is treated as having dimensions `TCZYX`.
53
+ T : int
54
+ The expected T (time) extent of the video.
55
+ C : int
56
+ The expected C (channel) extent of the video.
57
+ Z : int
58
+ The expected Z (height) extent of the video.
59
+ Y : int
60
+ The expected Y (row) extent of the video.
61
+ X : int
62
+ The expected X (column) extent of the video.
63
+ dt : float, optional
64
+ The time interval in milliseconds between one video frame and the
65
+ next. When ``None`` (the default) the spacing is inferred from the
66
+ video's ``T`` coordinate when one is present, or else falls back to
67
+ ``1000 / 60`` ms (60 fps). If supplied together with an existing
68
+ ``T`` coordinate, the two must agree to within a small tolerance.
69
+ dz : float, optional
70
+ The spatial distance in micrometre between one Z slice and the next.
71
+ When ``None`` (the default) the spacing is inferred from the video's
72
+ ``Z`` coordinate, or else defaults to 1 µm. If supplied together
73
+ with an existing ``Z`` coordinate, the two must agree.
74
+ dy : float, optional
75
+ The spatial distance in micrometre between one row of pixels and the
76
+ next. When ``None`` (the default) the spacing is inferred from the
77
+ video's ``Y`` coordinate, or else defaults to 1 µm. If supplied
78
+ together with an existing ``Y`` coordinate, the two must agree.
79
+ dx : float, optional
80
+ The spatial distance in micrometre between one column of pixels and
81
+ the next. When ``None`` (the default) the spacing is inferred from
82
+ the video's ``X`` coordinate, or else defaults to 1 µm. If supplied
83
+ together with an existing ``X`` coordinate, the two must agree.
84
+ t0 : float, optional
85
+ The time of the first frame, in milliseconds. When ``None`` (the
86
+ default) the origin is inferred from the video's ``T`` coordinate,
87
+ or else defaults to zero. If supplied together with an existing
88
+ ``T`` coordinate, the two must agree.
89
+ z0 : float, optional
90
+ The position of the first Z slice, in micrometre. When ``None``
91
+ (the default) the origin is inferred from the video's ``Z``
92
+ coordinate, or else defaults to zero. If supplied together with an
93
+ existing ``Z`` coordinate, the two must agree.
94
+ y0 : float, optional
95
+ The position of the first row of pixels, in micrometre. When
96
+ ``None`` (the default) the origin is inferred from the video's ``Y``
97
+ coordinate, or else defaults to zero. If supplied together with an
98
+ existing ``Y`` coordinate, the two must agree.
99
+ x0 : float, optional
100
+ The position of the first column of pixels, in micrometre. When
101
+ ``None`` (the default) the origin is inferred from the video's ``X``
102
+ coordinate, or else defaults to zero. If supplied together with an
103
+ existing ``X`` coordinate, the two must agree.
104
+ dtype : npt.DTypeLike
105
+ The expected dtype of the video.
106
+ acquisition_date : datetime.datetime
107
+ A timestamp of when the video was created. Defaults to datetime.now().
108
+ creator : str
109
+ A string describing who created the video.
110
+
111
+ Returns
112
+ -------
113
+ xarray.DataArray
114
+ A TCZYX video with the supplied parameters that passes :py:func:`~toolsandogh.validate_video`.
115
+
116
+ Notes
117
+ -----
118
+ The ``T``, ``Z``, ``Y`` and ``X`` axes carry continuous, uniformly
119
+ spaced coordinates in milliseconds (time) and micrometres (space),
120
+ with the ``units`` attribute set on each. For each axis the scale
121
+ (``dt``, ``dz``, ``dy``, ``dx``) and origin (``t0``, ``z0``, ``y0``,
122
+ ``x0``) are resolved by one of three routes: taken from an existing
123
+ coordinate on the video (e.g. physical coordinates supplied by a
124
+ reader), taken from the explicit parameter, or filled with a
125
+ default. When a parameter is supplied *and* the video already
126
+ carries a coordinate, the two must agree to within a small
127
+ tolerance; the coordinate values are always retained. The channel
128
+ (``C``) axis is categorical and carries no units.
129
+ """
130
+ # Turn video into an xarray.
131
+ if not isinstance(video, xr.DataArray):
132
+ video = _coerce_to_xarray(video)
133
+
134
+ # Ensure the video's data is a Dask array.
135
+ if not isinstance(video.data, da.Array):
136
+ video = video.copy(data=da.from_array(video.data), deep=False)
137
+
138
+ # Ensure the TZYX axes exist. Missing axes are added as size-1
139
+ # dimensions so that the coordinate-resolution pass below treats
140
+ # them uniformly.
141
+ for dim in ("T", "Z", "Y", "X"):
142
+ if dim not in video.dims:
143
+ video = video.expand_dims({dim: 1})
144
+
145
+ # Resolve the per-axis scale, origin, and coordinate values. For
146
+ # each axis the scale and origin are either inferred from an
147
+ # existing coordinate (in which case any explicitly supplied value
148
+ # is validated against it), taken from the explicit parameter, or
149
+ # filled with a default. Time defaults to 60 fps (``1000 / 60`` ms);
150
+ # space defaults to 1 µm; origins default to zero.
151
+ axis_params = {
152
+ "T": (dt, t0, 1000.0 / 60.0, "ms"),
153
+ "Z": (dz, z0, 1.0, "µm"),
154
+ "Y": (dy, y0, 1.0, "µm"),
155
+ "X": (dx, x0, 1.0, "µm"),
156
+ }
157
+ resolved_scales: dict[str, float] = {}
158
+ for dim in ("T", "Z", "Y", "X"):
159
+ scale_param, origin_param, default_scale, units = axis_params[dim]
160
+ vals, scale = _resolve_axis(video, dim, scale_param, origin_param, default_scale)
161
+ coord = xr.DataArray(vals, dims=(dim,), attrs={"units": units})
162
+ video = video.assign_coords({dim: coord})
163
+ resolved_scales[dim] = scale
164
+
165
+ time_increment = resolved_scales["T"]
166
+ physical_size_z = resolved_scales["Z"]
167
+ physical_size_y = resolved_scales["Y"]
168
+ physical_size_x = resolved_scales["X"]
169
+
170
+ # Ensure the C axis exists. The channel axis is categorical and
171
+ # carries no physical units.
172
+ if "C" not in video.dims:
173
+ video = video.expand_dims({"C": 1})
174
+
175
+ # Derive the result dtype.
176
+ if dtype is None:
177
+ result_dtype = video.dtype
178
+ else:
179
+ result_dtype = np.dtype(dtype)
180
+
181
+ # If there is a S axis, merge its entries.
182
+ if "S" in video.dims:
183
+ video = video.mean("S", dtype=np.float32)
184
+
185
+ # Ensure the resulting video has the correct dtype.
186
+ if video.dtype != result_dtype:
187
+ video = video.astype(result_dtype)
188
+
189
+ # Ensure the correct ordering of axes.
190
+ axes = ("T", "C", "Z", "Y", "X")
191
+ video = video.transpose(*axes)
192
+
193
+ # Ensure each axis is as large as expected.
194
+ expected_shape = (T, C, Z, Y, X)
195
+ for axis, expected, actual in zip(axes, expected_shape, video.shape):
196
+ if not (expected is None or expected == actual):
197
+ raise RuntimeError(f"Expected {axis}-axis of size {expected}, but got {actual}")
198
+
199
+ # Ensure there is metadata attached.
200
+ if not hasattr(video, "processed"):
201
+ (size_t, size_c, size_z, size_y, size_x) = video.shape
202
+ pixels = Pixels(
203
+ type=_dtype_pixel_type(video.dtype),
204
+ big_endian=_dtype_is_big_endian(video.dtype),
205
+ dimension_order=Pixels_DimensionOrder.XYZCT,
206
+ size_t=size_t,
207
+ size_c=size_c,
208
+ size_z=size_z,
209
+ size_y=size_y,
210
+ size_x=size_x,
211
+ time_increment=time_increment,
212
+ physical_size_z=physical_size_z,
213
+ physical_size_y=physical_size_y,
214
+ physical_size_x=physical_size_x,
215
+ time_increment_unit=UnitsTime.MILLISECOND,
216
+ physical_size_z_unit=UnitsLength.MICROMETER,
217
+ physical_size_y_unit=UnitsLength.MICROMETER,
218
+ physical_size_x_unit=UnitsLength.MICROMETER,
219
+ )
220
+ image = Image(
221
+ acquisition_date=(acquisition_date or datetime.now(timezone.utc)),
222
+ description="Video with auto-generated metadata.",
223
+ pixels=pixels,
224
+ )
225
+ ome = OME(
226
+ images=[image],
227
+ creator=(creator or "MPL Erlangen, Sandoghdar Division, toolsandogh"),
228
+ )
229
+ video = video.assign_attrs({"processed": ome})
230
+
231
+ # Raise an exception if the video is still not in canonical form.
232
+ validate_video(video)
233
+
234
+ # Done.
235
+ return video
236
+
237
+
238
+ def _coerce_to_xarray(array: npt.ArrayLike) -> xr.DataArray:
239
+ """
240
+ Turn a supplied array into an xarray.
241
+
242
+ Parameters
243
+ ----------
244
+ array : npt.ArrayLike
245
+ An object designating an array.
246
+
247
+ Returns
248
+ -------
249
+ xr.DataArray
250
+ An xarray with the same content and dtype as the supplied array.
251
+ """
252
+ # Determine the Dask array holding the video's data.
253
+ if isinstance(array, da.Array):
254
+ data = array
255
+ else:
256
+ data = da.from_array(array)
257
+
258
+ # Determine the appropriate dims
259
+ rank = len(data.shape)
260
+ match rank:
261
+ case 0:
262
+ dims = ()
263
+ case 1:
264
+ dims = ("X",)
265
+ case 2:
266
+ dims = ("Y", "X")
267
+ case 3:
268
+ dims = ("T", "Y", "X")
269
+ case 4:
270
+ dims = ("T", "Z", "Y", "X")
271
+ case 5:
272
+ dims = ("T", "C", "Z", "Y", "X")
273
+ case 6:
274
+ dims = ("T", "C", "Z", "Y", "X", "S")
275
+ case _:
276
+ raise RuntimeError(f"Cannot interpret {rank}-dimensional data as a video.")
277
+
278
+ # Create the xarray.
279
+ return xr.DataArray(data=data, dims=dims)
280
+
281
+
282
+ def _axis_origin_and_step(
283
+ video: xr.DataArray,
284
+ dim: str,
285
+ *,
286
+ default_step: float = 1.0,
287
+ ) -> tuple[float, float]:
288
+ """
289
+ Return ``(origin, step)`` for a uniformly-spaced physical axis.
290
+
291
+ The values are read directly from the ``dim`` coordinate of
292
+ ``video``. For a size-1 axis (no spacing to infer) the supplied
293
+ ``default_step`` is returned. Uniform spacing is guaranteed by
294
+ :func:`validate_video` for canonical videos, so the step is read
295
+ from the first two coordinate values without averaging.
296
+ """
297
+ coord = np.asarray(video[dim].values, dtype=np.float64)
298
+ origin = float(coord[0])
299
+ step = float(coord[1] - coord[0]) if coord.size >= 2 else default_step
300
+ return origin, step
301
+
302
+
303
+ def _resolve_axis(
304
+ video: xr.DataArray,
305
+ dim: str,
306
+ scale: float | None,
307
+ origin: float | None,
308
+ default_scale: float,
309
+ ) -> tuple[np.ndarray, float]:
310
+ """
311
+ Resolve the coordinate array and physical scale for one TZYX axis.
312
+
313
+ When the video already carries a coordinate for ``dim``, its values
314
+ are retained and the scale and origin are inferred from them; any
315
+ explicitly supplied ``scale`` or ``origin`` is checked against the
316
+ inferred values and must agree to within a small tolerance, otherwise
317
+ a :class:`ValueError` is raised.
318
+
319
+ When the video does not carry a coordinate, one is generated from the
320
+ supplied scale and origin (falling back to ``default_scale`` and
321
+ ``0.0`` respectively).
322
+
323
+ Returns the coordinate values (as ``float64``) and the resolved
324
+ physical scale (for the OME metadata).
325
+ """
326
+ size = int(video.sizes[dim])
327
+ if dim in video.coords:
328
+ vals = np.asarray(video[dim].values, dtype=np.float64)
329
+ axis_origin, axis_step = _axis_origin_and_step(video, dim, default_step=default_scale)
330
+ if origin is not None and not np.isclose(origin, axis_origin, rtol=1e-6, atol=1e-9):
331
+ raise ValueError(
332
+ f"The supplied {dim.lower()}0={origin!r} does not match the "
333
+ f"{dim} coordinate origin {axis_origin!r}."
334
+ )
335
+ if size >= 2:
336
+ if scale is not None and not np.isclose(scale, axis_step, rtol=1e-6, atol=1e-9):
337
+ raise ValueError(
338
+ f"The supplied d{dim.lower()}={scale!r} does not match the "
339
+ f"{dim} coordinate spacing {axis_step!r}."
340
+ )
341
+ return vals, axis_step
342
+ # A size-1 axis has no spacing to infer; respect an explicit scale.
343
+ return vals, (scale if scale is not None else default_scale)
344
+ # No existing coordinate: generate one.
345
+ resolved_scale = scale if scale is not None else default_scale
346
+ resolved_origin = origin if origin is not None else 0.0
347
+ vals = resolved_origin + np.arange(size, dtype=np.float64) * resolved_scale
348
+ return vals, resolved_scale
349
+
350
+
351
+ def _dtype_pixel_type(dtype: npt.DTypeLike) -> PixelType:
352
+ """
353
+ Return the OME Pixel type corresponding to the supplied dtype.
354
+
355
+ Parameters
356
+ ----------
357
+ dtype : npt.DTypeLike
358
+ The Numpy dtype to be used for representing pixel data.
359
+
360
+ Returns
361
+ -------
362
+ ome_types.model.PixelType
363
+ A suitable OME pixel type.
364
+ """
365
+ match np.dtype(dtype):
366
+ case np.int8:
367
+ return PixelType.INT8
368
+ case np.int16:
369
+ return PixelType.INT16
370
+ case np.int32:
371
+ return PixelType.INT32
372
+ case np.uint8:
373
+ return PixelType.UINT8
374
+ case np.uint16:
375
+ return PixelType.UINT16
376
+ case np.uint32:
377
+ return PixelType.UINT32
378
+ case np.float32:
379
+ return PixelType.FLOAT
380
+ case np.float64:
381
+ return PixelType.DOUBLE
382
+ case np.complex64:
383
+ return PixelType.COMPLEXFLOAT
384
+ case np.complex128:
385
+ return PixelType.COMPLEXDOUBLE
386
+ # The dtypes np.int64 and np.uint64 have no OME equivalent.
387
+ case np.int64:
388
+ return PixelType.BIT
389
+ case np.uint64:
390
+ return PixelType.BIT
391
+ case _:
392
+ raise RuntimeError(f"Cannot interpret {dtype} as a OME pixel type.")
393
+
394
+
395
+ def _dtype_is_big_endian(dtype: npt.DTypeLike) -> bool:
396
+ """
397
+ Return whether the video's data is stored in big endian byte order.
398
+
399
+ Parameters
400
+ ----------
401
+ dtype : npt.DTypeLike
402
+ The Numpy dtype to be used for representing pixel data.
403
+
404
+ Returns
405
+ -------
406
+ bool
407
+ True when the dtype is big endian, False otherwise.
408
+ """
409
+ dtype = np.dtype(dtype)
410
+
411
+ if dtype.itemsize == 1:
412
+ return False
413
+
414
+ match np.dtype(dtype).byteorder:
415
+ case ">":
416
+ return True
417
+ case "<":
418
+ return False
419
+ case "=":
420
+ return sys.byteorder == "big"
421
+ case _:
422
+ raise RuntimeError(f"Cannot determine endianness of {dtype}.")
@@ -55,6 +55,10 @@ def generate_video(
55
55
  size=(T, C, Z, Y, X),
56
56
  dtype=dtype, # type: ignore
57
57
  ),
58
+ dt=dt,
59
+ dz=dz,
60
+ dy=dy,
61
+ dx=dx,
58
62
  )
59
63
 
60
64
  # Check that the video matches the supplied parameters.