toolsandogh 0.2.0__tar.gz → 0.3.2__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 (50) hide show
  1. toolsandogh-0.3.2/.github/workflows/ci.yml +78 -0
  2. toolsandogh-0.3.2/.github/workflows/release.yml +60 -0
  3. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/.gitignore +3 -0
  4. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/.pre-commit-config.yaml +29 -27
  5. toolsandogh-0.3.2/DESIGN.md +255 -0
  6. toolsandogh-0.3.2/PKG-INFO +102 -0
  7. toolsandogh-0.3.2/README.md +64 -0
  8. toolsandogh-0.3.2/demos/demo_iscat_50nm_bead.py +193 -0
  9. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/pyproject.toml +41 -48
  10. toolsandogh-0.3.2/src/toolsandogh/__init__.py +70 -0
  11. toolsandogh-0.3.2/src/toolsandogh/_link.py +223 -0
  12. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_load_video.py +5 -2
  13. toolsandogh-0.3.2/src/toolsandogh/_locate.py +723 -0
  14. toolsandogh-0.3.2/src/toolsandogh/_simulate_particles.py +227 -0
  15. toolsandogh-0.3.2/src/toolsandogh/_simulate_psf.py +1418 -0
  16. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_store_video.py +7 -14
  17. toolsandogh-0.3.2/src/toolsandogh/tests/conftest.py +49 -0
  18. toolsandogh-0.3.2/src/toolsandogh/tests/test_link.py +299 -0
  19. toolsandogh-0.3.2/src/toolsandogh/tests/test_locate.py +549 -0
  20. toolsandogh-0.3.2/src/toolsandogh/tests/test_simulate_particles.py +202 -0
  21. toolsandogh-0.3.2/src/toolsandogh/tests/test_simulate_psf.py +979 -0
  22. toolsandogh-0.3.2/src/toolsandogh.egg-info/PKG-INFO +102 -0
  23. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/SOURCES.txt +13 -2
  24. toolsandogh-0.3.2/src/toolsandogh.egg-info/requires.txt +24 -0
  25. toolsandogh-0.3.2/src/toolsandogh.egg-info/scm_file_list.json +35 -0
  26. toolsandogh-0.3.2/src/toolsandogh.egg-info/scm_version.json +8 -0
  27. toolsandogh-0.3.2/uv.lock +3696 -0
  28. toolsandogh-0.2.0/DESIGN.md +0 -150
  29. toolsandogh-0.2.0/PKG-INFO +0 -81
  30. toolsandogh-0.2.0/README.md +0 -40
  31. toolsandogh-0.2.0/src/toolsandogh/__init__.py +0 -23
  32. toolsandogh-0.2.0/src/toolsandogh/_plot_video.py +0 -0
  33. toolsandogh-0.2.0/src/toolsandogh/_render.py +0 -15
  34. toolsandogh-0.2.0/src/toolsandogh/tests/conftest.py +0 -8
  35. toolsandogh-0.2.0/src/toolsandogh.egg-info/PKG-INFO +0 -81
  36. toolsandogh-0.2.0/src/toolsandogh.egg-info/requires.txt +0 -27
  37. toolsandogh-0.2.0/uv.lock +0 -3637
  38. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/LICENSE +0 -0
  39. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/setup.cfg +0 -0
  40. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_canonicalize_video.py +0 -0
  41. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_generate_video.py +0 -0
  42. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_rolling.py +0 -0
  43. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_rvt.py +0 -0
  44. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/_validate_video.py +0 -0
  45. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/__init__.py +0 -0
  46. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/test_io.py +0 -0
  47. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/test_rolling.py +0 -0
  48. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh/tests/testfile.tiff +0 -0
  49. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/dependency_links.txt +0 -0
  50. {toolsandogh-0.2.0 → toolsandogh-0.3.2}/src/toolsandogh.egg-info/top_level.txt +0 -0
@@ -0,0 +1,78 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ # Run the full pre-commit suite (ruff, pyright, validate-pyproject, uv-lock,
14
+ # and the local pytest hook) exactly as developers do locally. Uses the
15
+ # committed uv.lock via --frozen, so any lockfile drift fails loudly.
16
+ pre-commit:
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 8
19
+ env:
20
+ # Uniform cache path across OSes so the actions/cache `path` below
21
+ # matches what conftest.py writes (it honors this env var via
22
+ # expanduser). Local dev uses the platform-canonical default instead.
23
+ JAX_COMPILATION_CACHE_DIR: ~/.cache/jax-compilation-cache
24
+ steps:
25
+ - uses: actions/checkout@main
26
+ - uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
27
+ with:
28
+ enable-cache: true
29
+ python-version: "3.12"
30
+ - name: Cache JAX compilations
31
+ uses: actions/cache@v5
32
+ with:
33
+ path: ~/.cache/jax-compilation-cache
34
+ key: jax-cc-precommit-py3.12-${{ hashFiles('uv.lock') }}
35
+ restore-keys: |
36
+ jax-cc-precommit-py3.12-
37
+ - run: uv run --frozen pre-commit run --all-files --show-diff-on-failure
38
+
39
+ # Portability matrix: prove the declared floors and the latest versions both
40
+ # work across the supported Python versions and operating systems. Each cell
41
+ # re-resolves dependencies directly from pyproject.toml (--no-project bypasses
42
+ # uv.lock), so the lowest-direct cell exercises the real lower bounds.
43
+ test:
44
+ runs-on: ${{ matrix.os }}
45
+ timeout-minutes: 8
46
+ env:
47
+ JAX_COMPILATION_CACHE_DIR: ~/.cache/jax-compilation-cache
48
+ # Self-diagnosing safety net for hangs: dump all thread stacks on
49
+ # SIGABRT/crash, and log every JAX JIT compilation with its timing so a
50
+ # stuck job shows exactly which compile/test was in flight.
51
+ PYTHONFAULTHANDLER: "1"
52
+ JAX_LOG_COMPILES: "1"
53
+ # Disable BLAS threads for now because they have shown to cause deadlocks
54
+ # in our test suite.
55
+ OMP_NUM_THREADS: "1"
56
+ OPENBLAS_NUM_THREADS: "1"
57
+ MKL_NUM_THREADS: "1"
58
+ strategy:
59
+ fail-fast: false
60
+ matrix:
61
+ os: [ubuntu-latest, windows-latest, macos-latest]
62
+ python: ["3.12", "3.13"]
63
+ resolution: [highest, lowest-direct]
64
+ steps:
65
+ - uses: actions/checkout@main
66
+ - uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
67
+ with:
68
+ enable-cache: true
69
+ python-version: ${{ matrix.python }}
70
+ - name: Cache JAX compilations
71
+ uses: actions/cache@v5
72
+ with:
73
+ path: ~/.cache/jax-compilation-cache
74
+ key: jax-cc-${{ runner.os }}-py${{ matrix.python }}-${{ matrix.resolution }}-${{ hashFiles('uv.lock') }}
75
+ restore-keys: |
76
+ jax-cc-${{ runner.os }}-py${{ matrix.python }}-${{ matrix.resolution }}-
77
+ jax-cc-${{ runner.os }}-py${{ matrix.python }}-
78
+ - run: uv run --no-project --with-editable . --with "pytest>=8.0" --with "pytest-timeout>=2.4" --python ${{ matrix.python }} --resolution ${{ matrix.resolution }} pytest --timeout=180 --timeout-method=thread -v --durations=20
@@ -0,0 +1,60 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ # Never cancel an in-flight release; only one release per tag at a time.
9
+ concurrency:
10
+ group: release-${{ github.ref }}
11
+ cancel-in-progress: false
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ # Build the sdist and wheel from the tagged commit. setuptools-scm derives
18
+ # the version from git, so a full history checkout is required.
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 10
22
+ steps:
23
+ - uses: actions/checkout@main
24
+ with:
25
+ fetch-depth: 0
26
+ - uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3
27
+ with:
28
+ enable-cache: true
29
+ python-version: "3.12"
30
+ - run: uv build -o dist
31
+ - uses: actions/upload-artifact@v7
32
+ with:
33
+ name: dist
34
+ path: dist/
35
+
36
+ # Publish to PyPI via Trusted Publishing (OIDC, no stored tokens) and create
37
+ # a GitHub Release with the built artifacts attached. Requires a one-time
38
+ # Trusted Publisher registration on pypi.org pointing at this workflow file
39
+ # (release.yml), the `pypi` environment, and this repository.
40
+ publish:
41
+ needs: build
42
+ runs-on: ubuntu-latest
43
+ timeout-minutes: 5
44
+ environment: pypi
45
+ permissions:
46
+ id-token: write # PyPI Trusted Publishing (OIDC)
47
+ contents: write # create GitHub Release
48
+ steps:
49
+ - uses: actions/download-artifact@v8
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+ - name: Publish to PyPI
54
+ uses: pypa/gh-action-pypi-publish@release/v1
55
+ - name: Create GitHub Release
56
+ uses: softprops/action-gh-release@v3
57
+ with:
58
+ files: dist/*
59
+ name: ${{ github.ref_name }}
60
+ generate_release_notes: true
@@ -122,6 +122,9 @@ tmp/
122
122
  *.tsv
123
123
  *.parquet
124
124
  *.feather
125
+ *.png
126
+ *.jpg
127
+ *.jpeg
125
128
 
126
129
  # Cache files generated by Dask (e.g., temporary spill to disk)
127
130
  .dask/
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
3
- rev: v5.0.0
3
+ rev: v6.0.0
4
4
  hooks:
5
5
  - id: check-case-conflict
6
6
  - id: check-illegal-windows-names
@@ -12,39 +12,41 @@ repos:
12
12
  - id: check-added-large-files
13
13
 
14
14
  - repo: https://github.com/astral-sh/uv-pre-commit
15
- rev: 0.9.5
15
+ rev: 0.11.26
16
16
  hooks:
17
17
  - id: uv-lock
18
18
 
19
- - repo: https://github.com/astral-sh/ruff-pre-commit
20
- rev: v0.14.0
21
- hooks:
22
- - id: ruff-check
23
- args: [ --fix ]
24
- - id: ruff-format
25
-
26
- - repo: https://github.com/numpy/numpydoc
27
- rev: v1.9.0
28
- hooks:
29
- - id: numpydoc-validation
30
-
31
19
  - repo: https://github.com/abravalheri/validate-pyproject
32
- rev: v0.24.1
20
+ rev: v0.25
33
21
  hooks:
34
22
  - id: validate-pyproject
35
23
  additional_dependencies: ["validate-pyproject-schema-store[all]"]
36
24
 
37
- - repo: https://github.com/RobertCraigie/pyright-python
38
- rev: v1.1.406
39
- hooks:
40
- - id: pyright
41
-
42
25
  - repo: local
43
26
  hooks:
44
- - id: pytest
45
- name: pytest
46
- entry: uv run ./.venv/bin/pytest
47
- language: system
48
- types: [python]
49
- pass_filenames: false
50
- always_run: true
27
+ - id: ruff-check
28
+ name: ruff check
29
+ entry: uv run ruff check --fix --exit-non-zero-on-fix
30
+ language: system
31
+ types: [python]
32
+ require_serial: true
33
+ - id: ruff-format
34
+ name: ruff format
35
+ entry: uv run ruff format
36
+ language: system
37
+ types: [python]
38
+ require_serial: true
39
+ - id: pyright
40
+ name: pyright
41
+ entry: uv run pyright
42
+ language: system
43
+ types: [python]
44
+ pass_filenames: false
45
+ always_run: true
46
+ - id: pytest
47
+ name: pytest
48
+ entry: uv run pytest
49
+ language: system
50
+ types: [python]
51
+ pass_filenames: false
52
+ always_run: true
@@ -0,0 +1,255 @@
1
+ # Design & Architecture Overview for the toolsandogh package
2
+
3
+ *Last updated: 2026-07-02*
4
+
5
+ ---
6
+
7
+ ## Project Overview
8
+
9
+ toolsandogh is a Python library (>= 3.12) for processing large-scale scientific data sets (e.g., microscopy volumes, multi-modal imaging, high-throughput experiments).
10
+
11
+ The library embraces **lazy, parallel computation** via Dask, **labeled, N-dimensional arrays** via xarray, and **type-safe, well-documented** code.
12
+
13
+ ### Demos
14
+
15
+ Standalone demos live in the top-level `demos/` directory. They import `toolsandogh` and exercise its public API. Demos should keep dependencies to a minimum (ideally only the library itself plus a rendering backend like Matplotlib), so they remain runnable without pulling in heavy dev dependencies.
16
+
17
+ ---
18
+
19
+ ## Repository Layout
20
+
21
+ ```
22
+ toolsandogh/
23
+ |
24
+ |-- DESIGN.md # <- THIS FILE (design & conventions)
25
+ |-- README.md # User-oriented introduction, installation, examples
26
+ |-- pyproject.toml # Build system, metadata, dependencies, ruff config
27
+ |-- .pre-commit-config.yaml # Pre-commit hooks (ruff, isort, pyright, etc.)
28
+ |-- .gitignore # Files/folders that Git should ignore
29
+ |-- uv.lock # Deterministic lockfile generated by uv
30
+ |
31
+ |-- demos/ # Standalone demo scripts (import toolsandogh; keep deps light)
32
+ | `-- demo_iscat_50nm_bead.py
33
+ |
34
+ `-- src/
35
+ `-- toolsandogh/ # The actual Python package
36
+ |-- __init__.py
37
+ |-- _canonicalize_video.py # All file names start with an underscore
38
+ |-- _generate_video.py
39
+ |-- _link.py
40
+ |-- _load_video.py
41
+ |-- _locate.py
42
+ |-- _rolling.py
43
+ |-- _rvt.py
44
+ |-- _simulate_particles.py
45
+ |-- _simulate_psf.py
46
+ |-- _store_video.py
47
+ |-- _validate_video.py
48
+ |
49
+ `-- tests/ # Tests for all toolsandogh functionality
50
+ |-- __init__.py
51
+ |-- conftest.py
52
+ |-- test_io.py
53
+ |-- test_link.py
54
+ |-- test_locate.py
55
+ |-- test_rolling.py
56
+ |-- test_simulate_particles.py
57
+ `-- test_simulate_psf.py
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Coding Conventions
63
+
64
+ ### 1. Coding Guidelines
65
+
66
+ The following guidelines apply to all code in the toolsandogh package. They are intended to keep the codebase consistent, readable, and easy to maintain for both human contributors and AI assistants. Where a guideline and a deadline conflict, correctness always wins.
67
+
68
+ #### 1.1 Naming and module layout
69
+
70
+ * Use ``snake_case`` for functions and variables, ``PascalCase`` for classes, and ``UPPER_CASE`` for module-level constants.
71
+ * Every module file name starts with an underscore (e.g. ``_locate.py``). Modules are private by default; only objects explicitly re-exported from ``toolsandogh/__init__.py`` form the public API. Importing from a private module directly is unsupported and may break without notice.
72
+ * New code should live in a new module when it addresses a distinct concern; otherwise it belongs in the existing module that covers the same concern. As a guideline rather than a hard rule, prefer one file per feature or workflow stage.
73
+
74
+ #### 1.2 Type hints
75
+
76
+ * All public functions, methods, and class attributes carry full type hints. Private helpers may omit them only when the annotation would be redundant with the surrounding context.
77
+ * Hints should be as specific as possible without becoming brittle: prefer ``Literal["r", "w"]`` over ``str`` for closed sets of options; prefer concrete container types over bare ``Any``; avoid ``Optional`` where ``X | None`` reads more clearly.
78
+ * Document variance explicitly when it matters (e.g., on protocols or abstract base classes).
79
+
80
+ #### 1.3 Inputs and outputs
81
+
82
+ * Publicly visible functions should be liberal in what they accept and conservative in what they emit.
83
+ * Accept the widest reasonable input: file paths, raw arrays, tuples, or plain lists where convenient, and let the function coerce them into the canonical representation.
84
+ * Always emit stable, well-typed values (typically ``xarray.DataArray`` or ``polars.DataFrame``). Do not leak implementation types such as NumPy arrays or pandas DataFrames through the public API.
85
+
86
+ #### 1.4 Data structures
87
+
88
+ * Array data is encoded as ``xarray.DataArray``, ideally lazily backed by Dask so that operations stay parallel and out-of-core.
89
+ * Tabular data is encoded as ``polars.DataFrame``.
90
+
91
+ #### 1.5 Simplicity
92
+
93
+ * Prefer free functions over classes. Functionality should be provided mainly through free functions, with classes reserved for genuine stateful objects.
94
+ * Each function should have one clear purpose that can be summed up in a single-line docstring. If a function's purpose resists a one-line summary, it is probably doing too much.
95
+ * Keep classes small and focused. Prefer composition over inheritance.
96
+
97
+ #### 1.6 Error handling
98
+
99
+ * Raise specific, informative exceptions that explain what went wrong and why. Include the offending value where helpful.
100
+ * Never silently coerce types or swallow exceptions. A bare ``except`` or an unchecked type coercion hides bugs and should be avoided.
101
+ * Validate inputs at the boundary of the public API; prefer explicit checks over defensive programming scattered through the internals.
102
+
103
+ ### 2. Documentation -- numpydoc
104
+
105
+ All public objects (functions, classes, methods) must be documented using the **numpydoc** style. This format is machine-parsable (e.g., for Sphinx) and familiar to scientific Python users.
106
+
107
+ #### 2.1 Required and optional sections
108
+
109
+ Every public docstring must contain the following sections **in this exact order**. Optional sections may be omitted when they carry no content, but if present they must appear at the position shown:
110
+
111
+ 1. **Short summary** -- a single, imperative line ending with a period. No blank line between the signature and the summary. Avoid restating the function name.
112
+ 2. *(blank line)*
113
+ 3. **Extended summary** -- one or more paragraphs giving context, assumptions, and edge cases. May be omitted if the short summary is self-explanatory.
114
+ 4. **Parameters** -- always present for any object that accepts arguments.
115
+ 5. **Returns** -- always present for any object that returns a value other than `None`.
116
+ 6. *(optional)* **Yields** -- for generators, instead of (not in addition to) **Returns**.
117
+ 7. *(optional)* **Raises** -- one ``exception_type`` per entry, followed by a description of the condition that triggers it. List every exception the function may raise intentionally.
118
+ 8. *(optional)* **Notes** -- implementation details, algorithmic notes, performance caveats, and limitations.
119
+ 9. *(optional)* **References** -- numbered citations using the ``.. [1]`` reST footnote syntax. Each reference is cited in the body text as ``[1]_``.
120
+ 10. *(optional)* **Examples** -- runnable code in doctest form.
121
+
122
+ Do **not** invent sections outside this list (no *Todo*, no *Author*, no *Version*). Use the project changelog and metadata for that information.
123
+
124
+ #### 2.2 Section headers
125
+
126
+ Each section header is a single line of the form
127
+
128
+ ```rst
129
+ Parameters
130
+ ----------
131
+ ```
132
+
133
+ i.e. the title (capitalised as shown above, with the exact spelling and singular/plural form from the numpydoc spec) followed by a line of hyphens of matching length. Section titles are not underlined with ``=`` or ``~``. There is exactly one blank line before and after each header.
134
+
135
+ #### 2.3 Parameters section
136
+
137
+ * One entry per parameter, in the same order as the function signature.
138
+ * Each entry begins with ``name`` followed by `` : `` and a type description, then a newline and an indented description.
139
+ * The type uses the **runtime expression**, not the annotation verbatim: write ``int or tuple of int`` rather than ``int | tuple[int, ...]``. Reserved words ``True``, ``False``, ``None`` are written in full, not as types.
140
+ * Append ``, optional`` to the type for any parameter that has a default value (including keyword-only and positional-or-keyword parameters with a default). Do **not** append it for required parameters, not even required keyword-only ones.
141
+ * The description states the **default** in prose, e.g. "The default is ``3``." For boolean defaults, write the behaviour out: "If ``True`` (default), ...".
142
+ * Parameters that accept ``*args`` / ``**kwargs`` are documented with the names ``*args`` and ``**kwargs`` respectively.
143
+
144
+ #### 2.4 Returns section
145
+
146
+ * For a single return value: a type line, then an indented description. Do not invent a name.
147
+ * For multiple named return values (e.g. a ``NamedTuple``), document each component with its name, type, and description, mirroring the Parameters layout.
148
+ * If the function returns `None` as a meaningful sentinel, document it. If it returns nothing, the **Returns** section is omitted entirely.
149
+
150
+ #### 2.5 Types and type hints
151
+
152
+ * Type hints live in the signature and should be as specific as possible. The docstring repeats the type in prose for the benefit of readers and Sphinx, but does not introduce new type information.
153
+ * Use `Literal` types for closed sets of string options (e.g. ``Literal["r", "w"]``) and document the accepted values explicitly in the parameter description.
154
+ * Write collection types out in prose (``list of str``) rather than ``list[str]`` in the docstring.
155
+
156
+ #### 2.6 Cross-references and formatting
157
+
158
+ * Use reST roles for cross-references: ``:func:`package.module.func``` for functions, ``:class:`package.module.Class``` for classes, ``:meth:`` for methods, ``:attr:`` for attributes, ``:mod:`` for modules. Always give the fully qualified path.
159
+ * Inline code is written as double backticks (```` ``code`` ````), not single backticks.
160
+ * Math may be written with ``:math:`expr``` inline or the ``.. math::`` directive for display equations.
161
+ * Keep paragraphs wrapped at a reasonable column (around 79 characters) and separated by a single blank line.
162
+
163
+ #### 2.7 Examples section
164
+
165
+ * Examples use the doctest ``>>>`` prompt and must be **runnable** in isolation (all imports shown, no reliance on prior cells).
166
+ * Prefer showing the expected output as a comment or repr block, so the example doubles as a regression test when run with ``pytest --doctest-modules``.
167
+ * Avoid side effects that modify global state or the filesystem. Where unavoidable, clean up within the example.
168
+ * Each example should illustrate one feature; do not bundle unrelated demonstrations.
169
+
170
+ #### 2.8 Comprehensive example
171
+
172
+ ```python
173
+ def median_filter(
174
+ data: xr.DataArray,
175
+ size: int | tuple[int, ...] = 3,
176
+ *,
177
+ preserve_nan: bool = True,
178
+ ) -> xr.DataArray:
179
+ """
180
+ Apply a median filter to an ``xarray.DataArray`` using a sliding window.
181
+
182
+ Parameters
183
+ ----------
184
+ data : xarray.DataArray
185
+ The input array. The array can be lazily backed by Dask; the
186
+ operation will therefore be executed in parallel when the result
187
+ is computed.
188
+
189
+ size : int or tuple of int, optional
190
+ The size of the filtering window along each dimension. If a
191
+ single integer is supplied it is used for all dimensions. The
192
+ default is ``3`` (a 3x3x... window).
193
+
194
+ preserve_nan : bool, optional
195
+ If ``True`` (default), NaN values are ignored when computing the
196
+ median, i.e. they act as "missing data". If ``False`` any window
197
+ containing a NaN will produce a NaN in the output.
198
+
199
+ Returns
200
+ -------
201
+ xarray.DataArray
202
+ A new ``DataArray`` with the same coordinates and attributes as
203
+ ``data`` but containing the filtered values. The returned object
204
+ retains the original chunking layout, so the computation remains
205
+ lazy until ``compute()`` or ``persist()`` is called.
206
+
207
+ Notes
208
+ -----
209
+ * The implementation uses :func:`dask.array.map_overlap` under the
210
+ hood, which means that the filter works on arbitrarily large
211
+ datasets that do not fit into memory.
212
+ * For integer ``size`` values larger than the chunk size along a
213
+ dimension, the function will rechunk automatically. This may
214
+ trigger a temporary increase in memory usage.
215
+
216
+ References
217
+ ----------
218
+ .. [1] J. R. Smith, *Median filtering of large image stacks*, J. Imaging,
219
+ 2023, https://doi.org/10.1234/jim.2023.001
220
+
221
+ Examples
222
+ --------
223
+ >>> import xarray as xr, numpy as np
224
+ >>> arr = xr.DataArray(np.random.rand(100, 100), dims=('y', 'x'))
225
+ >>> filtered = median_filter(arr, size=5)
226
+ >>> filtered.compute() # triggers Dask execution
227
+ <xarray.DataArray (y: 100, x: 100)>
228
+ """
229
+ # implementation follows ...
230
+ ```
231
+
232
+ ### 3. Testing
233
+
234
+ Tests live in ``src/toolsandogh/tests/`` and use **pytest** as the sole test runner. The project keeps the testing story simple: plain pytest tests, no property-based testing frameworks, no separate test harnesses.
235
+
236
+ #### 3.1 Test files and naming
237
+
238
+ * One test file per feature or workflow stage under test (e.g. ``test_locate.py``, ``test_link.py``, ``test_io.py``), mirroring the module layout.
239
+ * Test functions are named ``test_<unit>_<scenario>`` so that names describe what is being verified and under which conditions (e.g. ``test_locate_empty_image_returns_empty_dataframe``).
240
+ * Shared fixtures live in ``conftest.py``; keep fixtures small, focused, and reusable across files.
241
+
242
+ #### 3.2 Writing tests
243
+
244
+ * Write tests that exercise correctness, not coverage numbers. Coverage is a useful signal but never a goal in itself: a passing test that proves the right behaviour is worth more than a percentage point.
245
+ * Cover edge cases explicitly: empty arrays, single-element arrays, NaN inputs, mismatched shapes, and boundary conditions.
246
+ * Prefer deterministic inputs. Where randomness is unavoidable, set a fixed seed within the test so that runs are reproducible.
247
+ * Exercise both lazy and computed paths where relevant: assert that an operation returns a lazy result and that calling ``.compute()`` produces the expected values.
248
+
249
+ #### 3.3 Doctests
250
+
251
+ * Docstring examples (see section 2.7) double as regression tests when run with ``pytest --doctest-modules``. Keep them runnable in isolation and free of side effects that could break the suite.
252
+
253
+ ### 4. Linting
254
+
255
+ Formatting and import style are enforced by **ruff**, configured in ``pyproject.toml``. In short: code must be free of pycodestyle and pyflakes errors and follow PEP-8, with imports sorted as with **isort**. Refer to the ruff configuration for the authoritative list of rules.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: toolsandogh
3
+ Version: 0.3.2
4
+ Summary: A collection of Python scripts for iSCAT microscopy data analysis
5
+ Author-email: Marco Heisig <marco.heisig@mpl.mpg.de>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Repository, https://github.com/SandoghdarLab/toolsandogh
8
+ Project-URL: Issues, https://github.com/SandoghdarLab/toolsandogh/issues
9
+ Keywords: tools,utilities,python,microscopy
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: bioio>=3.4
14
+ Requires-Dist: bioio-dv>=1.2
15
+ Requires-Dist: bioio-imageio>=1.3
16
+ Requires-Dist: bioio-lif>=1.3
17
+ Requires-Dist: bioio-nd2>=2.0
18
+ Requires-Dist: bioio-ome-tiff>=1.4
19
+ Requires-Dist: bioio-ome-zarr>=3.5
20
+ Requires-Dist: bioio-tifffile>=1.3
21
+ Requires-Dist: bioio-tiff-glob>=1.2
22
+ Requires-Dist: dask>=2026.1
23
+ Requires-Dist: fsspec>=2025.3
24
+ Requires-Dist: imageio[ffmpeg]>=2.37
25
+ Requires-Dist: imgrvt>=1.0
26
+ Requires-Dist: jax>=0.10.2
27
+ Requires-Dist: jaxtyping>=0.3
28
+ Requires-Dist: numpy>=2.3
29
+ Requires-Dist: ome-types>=0.6
30
+ Requires-Dist: polars>=1.0
31
+ Requires-Dist: pyarrow>=22.0
32
+ Requires-Dist: scikit-image>=0.23
33
+ Requires-Dist: scipy>=1.17
34
+ Requires-Dist: tifffile>=2026.5
35
+ Requires-Dist: tqdm>=4.66
36
+ Requires-Dist: xarray>=2026.1
37
+ Dynamic: license-file
38
+
39
+ # toolsandogh
40
+
41
+ A Python toolbox for iSCAT microscopy data analysis, developed by the Sandoghdar Division of the Max Planck Institute for the Science of Light (MPL).
42
+
43
+ ## Overview
44
+
45
+ `toolsandogh` provides a small, reusable library for loading, processing, analyzing, and storing microscopy videos. It is designed around a canonical 5D data model---`T` (time), `C` (channel), `Z` (slice), `Y` (row), `X` (column)---implemented as lazy [xarray](https://docs.xarray.dev/) arrays backed by [Dask](https://www.dask.org/).
46
+
47
+ ## Features
48
+
49
+ - **Lazy, out-of-core computation**: Videos are kept as Dask arrays so you can work with datasets larger than memory.
50
+ - **Labeled, metadata-rich arrays**: Results are `xarray.DataArray` objects with `TCZYX` dimensions and OME metadata.
51
+ - **Multiple microscopy formats**: Read and write RAW/BIN, TIFF, OME-TIFF, OME-Zarr, ND2, and other formats supported by [bioio](https://github.com/bioio-devs/bioio).
52
+ - **Reusable analysis primitives**: Rolling statistics, radial variance transform, synthetic data generation, and more.
53
+
54
+ ## Installation
55
+
56
+ This project uses the [uv package manager](https://docs.astral.sh/uv/). Clone the repository and run:
57
+
58
+ ```bash
59
+ uv sync
60
+ ```
61
+
62
+ To install in editable mode for development:
63
+
64
+ ```bash
65
+ uv pip install -e ".[dev]"
66
+ ```
67
+
68
+ ## Quick start
69
+
70
+ ```python
71
+ import toolsandogh as sand
72
+
73
+ # Load a video (any bioio-supported format or raw BIN/RAW file).
74
+ video = sand.load_video("movie.nd2")
75
+
76
+ # Apply a rolling average along the time axis.
77
+ smoothed = sand.rolling_average(video, window_size=5, dim="T")
78
+
79
+ # Save the result back to disk.
80
+ sand.store_video(smoothed, "smoothed.ome.tiff")
81
+ ```
82
+
83
+ Use `help(sand.load_video)` or `help(sand.store_video)` for details on the available arguments.
84
+
85
+ ## Repository layout
86
+
87
+ - `src/toolsandogh/` --- the main Python package.
88
+ - `src/toolsandogh/tests/` --- `pytest` test suite.
89
+ - `DESIGN.md` --- design conventions and coding standards.
90
+
91
+ ## Contributing
92
+
93
+ Please see `DESIGN.md` for coding conventions, documentation standards, and architecture decisions. In brief:
94
+
95
+ - All public functions must be type-hinted and documented in numpydoc style.
96
+ - Code is linted with `ruff` and type-checked with `pyright`.
97
+ - Prefer free functions; keep data in `xarray.DataArray`/`Dataset` form.
98
+ - Tests go in `src/toolsandogh/tests/`.
99
+
100
+ ## License
101
+
102
+ GPL-3.0-or-later.
@@ -0,0 +1,64 @@
1
+ # toolsandogh
2
+
3
+ A Python toolbox for iSCAT microscopy data analysis, developed by the Sandoghdar Division of the Max Planck Institute for the Science of Light (MPL).
4
+
5
+ ## Overview
6
+
7
+ `toolsandogh` provides a small, reusable library for loading, processing, analyzing, and storing microscopy videos. It is designed around a canonical 5D data model---`T` (time), `C` (channel), `Z` (slice), `Y` (row), `X` (column)---implemented as lazy [xarray](https://docs.xarray.dev/) arrays backed by [Dask](https://www.dask.org/).
8
+
9
+ ## Features
10
+
11
+ - **Lazy, out-of-core computation**: Videos are kept as Dask arrays so you can work with datasets larger than memory.
12
+ - **Labeled, metadata-rich arrays**: Results are `xarray.DataArray` objects with `TCZYX` dimensions and OME metadata.
13
+ - **Multiple microscopy formats**: Read and write RAW/BIN, TIFF, OME-TIFF, OME-Zarr, ND2, and other formats supported by [bioio](https://github.com/bioio-devs/bioio).
14
+ - **Reusable analysis primitives**: Rolling statistics, radial variance transform, synthetic data generation, and more.
15
+
16
+ ## Installation
17
+
18
+ This project uses the [uv package manager](https://docs.astral.sh/uv/). Clone the repository and run:
19
+
20
+ ```bash
21
+ uv sync
22
+ ```
23
+
24
+ To install in editable mode for development:
25
+
26
+ ```bash
27
+ uv pip install -e ".[dev]"
28
+ ```
29
+
30
+ ## Quick start
31
+
32
+ ```python
33
+ import toolsandogh as sand
34
+
35
+ # Load a video (any bioio-supported format or raw BIN/RAW file).
36
+ video = sand.load_video("movie.nd2")
37
+
38
+ # Apply a rolling average along the time axis.
39
+ smoothed = sand.rolling_average(video, window_size=5, dim="T")
40
+
41
+ # Save the result back to disk.
42
+ sand.store_video(smoothed, "smoothed.ome.tiff")
43
+ ```
44
+
45
+ Use `help(sand.load_video)` or `help(sand.store_video)` for details on the available arguments.
46
+
47
+ ## Repository layout
48
+
49
+ - `src/toolsandogh/` --- the main Python package.
50
+ - `src/toolsandogh/tests/` --- `pytest` test suite.
51
+ - `DESIGN.md` --- design conventions and coding standards.
52
+
53
+ ## Contributing
54
+
55
+ Please see `DESIGN.md` for coding conventions, documentation standards, and architecture decisions. In brief:
56
+
57
+ - All public functions must be type-hinted and documented in numpydoc style.
58
+ - Code is linted with `ruff` and type-checked with `pyright`.
59
+ - Prefer free functions; keep data in `xarray.DataArray`/`Dataset` form.
60
+ - Tests go in `src/toolsandogh/tests/`.
61
+
62
+ ## License
63
+
64
+ GPL-3.0-or-later.