lodstone 0.1.0a0__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.
- lodstone-0.1.0a0/.github/workflows/release.yml +47 -0
- lodstone-0.1.0a0/.github/workflows/test.yml +45 -0
- lodstone-0.1.0a0/.gitignore +7 -0
- lodstone-0.1.0a0/CHANGELOG.md +13 -0
- lodstone-0.1.0a0/LICENSE +21 -0
- lodstone-0.1.0a0/PKG-INFO +415 -0
- lodstone-0.1.0a0/README.md +361 -0
- lodstone-0.1.0a0/RELEASING.md +36 -0
- lodstone-0.1.0a0/examples/_ndv_block_overlay.py +134 -0
- lodstone-0.1.0a0/examples/napari_ome_zarr.py +63 -0
- lodstone-0.1.0a0/examples/napari_zebrahub.py +166 -0
- lodstone-0.1.0a0/examples/ndv_dense.py +232 -0
- lodstone-0.1.0a0/examples/stream_to_numpy.py +43 -0
- lodstone-0.1.0a0/pyproject.toml +48 -0
- lodstone-0.1.0a0/src/lodstone/__init__.py +123 -0
- lodstone-0.1.0a0/src/lodstone/adapters/__init__.py +21 -0
- lodstone-0.1.0a0/src/lodstone/adapters/napari.py +266 -0
- lodstone-0.1.0a0/src/lodstone/adapters/ndv.py +522 -0
- lodstone-0.1.0a0/src/lodstone/chunks.py +147 -0
- lodstone-0.1.0a0/src/lodstone/composition.py +143 -0
- lodstone-0.1.0a0/src/lodstone/datasets/__init__.py +453 -0
- lodstone-0.1.0a0/src/lodstone/datasets/generative.py +373 -0
- lodstone-0.1.0a0/src/lodstone/diagnostics.py +99 -0
- lodstone-0.1.0a0/src/lodstone/geometry.py +198 -0
- lodstone-0.1.0a0/src/lodstone/model.py +429 -0
- lodstone-0.1.0a0/src/lodstone/planner.py +722 -0
- lodstone-0.1.0a0/src/lodstone/py.typed +1 -0
- lodstone-0.1.0a0/src/lodstone/resident.py +411 -0
- lodstone-0.1.0a0/src/lodstone/runtime.py +107 -0
- lodstone-0.1.0a0/src/lodstone/source.py +21 -0
- lodstone-0.1.0a0/src/lodstone/sources/__init__.py +12 -0
- lodstone-0.1.0a0/src/lodstone/sources/array.py +141 -0
- lodstone-0.1.0a0/src/lodstone/sources/ome_zarr.py +258 -0
- lodstone-0.1.0a0/src/lodstone/sources/zarr.py +36 -0
- lodstone-0.1.0a0/src/lodstone/stream.py +892 -0
- lodstone-0.1.0a0/src/lodstone/target.py +91 -0
- lodstone-0.1.0a0/src/lodstone/testing.py +55 -0
- lodstone-0.1.0a0/src/lodstone/virtual.py +716 -0
- lodstone-0.1.0a0/tests/conftest.py +45 -0
- lodstone-0.1.0a0/tests/test_chunks.py +65 -0
- lodstone-0.1.0a0/tests/test_composition.py +74 -0
- lodstone-0.1.0a0/tests/test_datasets.py +213 -0
- lodstone-0.1.0a0/tests/test_diagnostics.py +52 -0
- lodstone-0.1.0a0/tests/test_generative.py +113 -0
- lodstone-0.1.0a0/tests/test_geometry.py +96 -0
- lodstone-0.1.0a0/tests/test_model.py +61 -0
- lodstone-0.1.0a0/tests/test_napari_adapter.py +163 -0
- lodstone-0.1.0a0/tests/test_ndv_adapter.py +352 -0
- lodstone-0.1.0a0/tests/test_ome_zarr.py +151 -0
- lodstone-0.1.0a0/tests/test_planner.py +595 -0
- lodstone-0.1.0a0/tests/test_resident.py +236 -0
- lodstone-0.1.0a0/tests/test_stream.py +834 -0
- lodstone-0.1.0a0/tests/test_virtual.py +333 -0
- lodstone-0.1.0a0/uv.lock +1184 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
15
|
+
with:
|
|
16
|
+
persist-credentials: false
|
|
17
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.13"
|
|
20
|
+
enable-cache: false
|
|
21
|
+
- name: Verify tag matches package version
|
|
22
|
+
shell: bash
|
|
23
|
+
run: |
|
|
24
|
+
package_version="$(uv version --short)"
|
|
25
|
+
test "${GITHUB_REF_NAME}" = "v${package_version}"
|
|
26
|
+
- run: uv build
|
|
27
|
+
- run: uvx twine check dist/*
|
|
28
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
29
|
+
with:
|
|
30
|
+
name: python-package-distributions
|
|
31
|
+
path: dist/
|
|
32
|
+
if-no-files-found: error
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment:
|
|
38
|
+
name: pypi
|
|
39
|
+
url: https://pypi.org/p/lodstone
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
44
|
+
with:
|
|
45
|
+
name: python-package-distributions
|
|
46
|
+
path: dist/
|
|
47
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest]
|
|
18
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
19
|
+
include:
|
|
20
|
+
- os: macos-latest
|
|
21
|
+
python-version: "3.13"
|
|
22
|
+
- os: windows-latest
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
26
|
+
with:
|
|
27
|
+
persist-credentials: false
|
|
28
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
- run: uv run --extra test pytest
|
|
32
|
+
|
|
33
|
+
quality:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
37
|
+
with:
|
|
38
|
+
persist-credentials: false
|
|
39
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.13"
|
|
42
|
+
- run: uv run --group dev ruff check .
|
|
43
|
+
- run: uv run --group dev ruff format --check .
|
|
44
|
+
- run: uv run --extra ome-zarr --group dev pyright src
|
|
45
|
+
- run: uv build
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Lodstone follows semantic versioning. Viewer adapters remain provisional during
|
|
4
|
+
the 0.1 alpha series and may change between prereleases.
|
|
5
|
+
|
|
6
|
+
## 0.1.0a0 - 2026-08-23
|
|
7
|
+
|
|
8
|
+
- Add renderer-neutral multiscale chunk planning and progressive streaming.
|
|
9
|
+
- Add bounded caches, cancellation, request reprioritization, and diagnostics.
|
|
10
|
+
- Add dense resident windows with coarse-to-fine composition.
|
|
11
|
+
- Add NumPy, Zarr, and OME-Zarr sources.
|
|
12
|
+
- Add experimental napari and ndv adapters.
|
|
13
|
+
- Support shared runtimes across layers and channels.
|
lodstone-0.1.0a0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kyle I S Harrington
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lodstone
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: Renderer-neutral multiscale chunk planning and streaming
|
|
5
|
+
Project-URL: Repository, https://github.com/kephale/lodstone
|
|
6
|
+
Project-URL: Issues, https://github.com/kephale/lodstone/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/kephale/lodstone/blob/main/CHANGELOG.md
|
|
8
|
+
Author: Kyle I S Harrington
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Kyle I S Harrington
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Classifier: Development Status :: 3 - Alpha
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
38
|
+
Classifier: Typing :: Typed
|
|
39
|
+
Requires-Python: >=3.11
|
|
40
|
+
Requires-Dist: numpy>=1.26
|
|
41
|
+
Provides-Extra: datasets
|
|
42
|
+
Requires-Dist: zarr>=3; extra == 'datasets'
|
|
43
|
+
Provides-Extra: ome-zarr
|
|
44
|
+
Requires-Dist: aiohttp>=3.10; extra == 'ome-zarr'
|
|
45
|
+
Requires-Dist: fsspec>=2024.2; extra == 'ome-zarr'
|
|
46
|
+
Requires-Dist: zarr>=3; extra == 'ome-zarr'
|
|
47
|
+
Provides-Extra: test
|
|
48
|
+
Requires-Dist: dask[array]>=2022.1.1; extra == 'test'
|
|
49
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
50
|
+
Requires-Dist: zarr>=3; extra == 'test'
|
|
51
|
+
Provides-Extra: zarr
|
|
52
|
+
Requires-Dist: zarr>=3; extra == 'zarr'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# Lodstone
|
|
56
|
+
|
|
57
|
+
Lodstone is a renderer-neutral engine for view-dependent streaming of
|
|
58
|
+
multiscale chunked arrays. It turns a multiscale `Source` and a camera `View`
|
|
59
|
+
into progressive array `Update`s accepted by a viewer-specific `Target`.
|
|
60
|
+
|
|
61
|
+
It is intended to be shared by clients such as ChimeraX, napari, and ndv.
|
|
62
|
+
Lodstone does not create windows, textures, shaders, layers, or viewer models.
|
|
63
|
+
|
|
64
|
+
Lodstone is currently an alpha. The renderer-neutral core is the compatibility
|
|
65
|
+
boundary for the 0.1 series; viewer adapters are experimental and may change
|
|
66
|
+
between prereleases as their host applications establish public streaming APIs.
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
Source + View + Target
|
|
70
|
+
│
|
|
71
|
+
Planner
|
|
72
|
+
│
|
|
73
|
+
Stream
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install lodstone
|
|
80
|
+
pip install "lodstone[ome-zarr]" # OME-Zarr and remote stores
|
|
81
|
+
pip install "lodstone[datasets]" # generative and local Zarr examples
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Lodstone supports Python 3.11 through 3.14. NumPy is its only required runtime
|
|
85
|
+
dependency.
|
|
86
|
+
|
|
87
|
+
## Example
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
import numpy as np
|
|
91
|
+
|
|
92
|
+
from lodstone import Layout, Stream, View
|
|
93
|
+
from lodstone.sources import ArrayPyramidSource
|
|
94
|
+
from lodstone.testing import RecordingTarget
|
|
95
|
+
|
|
96
|
+
fine = np.arange(256 * 256, dtype=np.uint16).reshape(256, 256)
|
|
97
|
+
coarse = fine[::4, ::4]
|
|
98
|
+
|
|
99
|
+
fine_transform = np.eye(3)
|
|
100
|
+
coarse_transform = np.diag([4.0, 4.0, 1.0])
|
|
101
|
+
source = ArrayPyramidSource(
|
|
102
|
+
[fine, coarse],
|
|
103
|
+
axes=("y", "x"),
|
|
104
|
+
transforms=[fine_transform, coarse_transform],
|
|
105
|
+
chunks=[(64, 64), (32, 32)],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# A real integration implements this protocol to upload updates into its
|
|
109
|
+
# renderer. RecordingTarget is useful for tests and examples.
|
|
110
|
+
target = RecordingTarget(Layout(kind="tiled", block_shape=(64, 64)))
|
|
111
|
+
|
|
112
|
+
# Map the 256 by 256 world extent into clip coordinates [-1, 1].
|
|
113
|
+
world_to_clip = np.array(
|
|
114
|
+
[
|
|
115
|
+
[2 / 256, 0, 0, -1],
|
|
116
|
+
[0, 2 / 256, 0, -1],
|
|
117
|
+
[0, 0, 1, 0],
|
|
118
|
+
[0, 0, 0, 1],
|
|
119
|
+
],
|
|
120
|
+
dtype=float,
|
|
121
|
+
)
|
|
122
|
+
view = View(
|
|
123
|
+
displayed_axes=(0, 1),
|
|
124
|
+
index=(None, None),
|
|
125
|
+
viewport=(800, 800),
|
|
126
|
+
world_to_clip=world_to_clip,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
with Stream(source, target) as stream:
|
|
130
|
+
stream.update(view)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Adapters that already have a renderer-specific region policy can call
|
|
134
|
+
``stream.submit(view, plan)``. This executes the supplied regions exactly while
|
|
135
|
+
retaining Lodstone's cancellation, native-chunk cache, batching, pacing, and
|
|
136
|
+
stale-generation rejection. This is how the napari adapter preserves PR
|
|
137
|
+
#9067's camera-bounded 3-D loading behavior.
|
|
138
|
+
|
|
139
|
+
``Planner.plan_region(...)`` provides an incremental migration path for those
|
|
140
|
+
adapters: the viewer may continue choosing the target level and bounded region
|
|
141
|
+
while Lodstone owns transform-aware ladder mapping, native-grid enumeration,
|
|
142
|
+
memory and axis limits, cache filtering, and delivery priority. Integrations
|
|
143
|
+
can compare its stable ``PlanTrace`` against an established planner and retain
|
|
144
|
+
their fallback whenever geometry differs.
|
|
145
|
+
|
|
146
|
+
Viewer integrations normally provide three small pieces:
|
|
147
|
+
|
|
148
|
+
1. Camera and dimension state converted into `View`.
|
|
149
|
+
2. A `Target` that applies array updates to the renderer.
|
|
150
|
+
3. A dispatcher that runs target methods on the viewer's render/UI thread.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
stream = Stream(
|
|
154
|
+
source,
|
|
155
|
+
target,
|
|
156
|
+
dispatch=run_on_viewer_thread,
|
|
157
|
+
)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Viewers with several layers or channels should share a `Runtime`. It owns one
|
|
161
|
+
asynchronous scheduler and a bounded CPU staging pool, while each stream keeps
|
|
162
|
+
its independent request state. Heavy `stage`, `stage_prepare`, and
|
|
163
|
+
`stage_phase` work runs in that pool instead of blocking I/O and cancellation:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from lodstone import Runtime, Stream
|
|
167
|
+
|
|
168
|
+
runtime = Runtime(compute_workers=2)
|
|
169
|
+
streams = [
|
|
170
|
+
Stream(source, target, runtime=runtime, dispatch=run_on_viewer_thread)
|
|
171
|
+
for source, target in channels
|
|
172
|
+
]
|
|
173
|
+
# Close streams first, then the shared runtime.
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## napari adapter
|
|
177
|
+
|
|
178
|
+
The optional adapter uses the rendering architecture from napari PR #9067: a
|
|
179
|
+
single multiscale layer backed by bounded resident intervals, camera-selected
|
|
180
|
+
2-D or 3-D tiles, and partial GPU texture uploads. It passes the source's
|
|
181
|
+
original lazy arrays through, so Zarr data is not materialized into dense
|
|
182
|
+
NumPy levels:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import napari
|
|
186
|
+
from lodstone.adapters.napari import NapariController
|
|
187
|
+
from lodstone.sources import OMEZarrSource
|
|
188
|
+
|
|
189
|
+
source = OMEZarrSource.open("https://example.org/image.zarr")
|
|
190
|
+
viewer = napari.Viewer()
|
|
191
|
+
controller = NapariController(viewer, source, fixed_index={0: 0})
|
|
192
|
+
napari.run()
|
|
193
|
+
controller.close()
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Pass ``layer_type="labels"`` for an integer segmentation pyramid. Image and
|
|
197
|
+
Labels layers use the same camera-driven Lodstone planner, cancellation,
|
|
198
|
+
caching, and delivery pipeline. Regular and rectilinear native chunk grids
|
|
199
|
+
are preserved rather than approximated by a single nominal chunk size.
|
|
200
|
+
|
|
201
|
+
This currently requires the `lodstone-integration` napari branch based on PR
|
|
202
|
+
#9067. Run
|
|
203
|
+
`examples/napari_ome_zarr.py` for a two-channel remote example. The core
|
|
204
|
+
package still has no napari or Qt dependency.
|
|
205
|
+
|
|
206
|
+
## ndv adapter
|
|
207
|
+
|
|
208
|
+
The ndv adapter presents immutable dense phase snapshots through ndv's common
|
|
209
|
+
`ArrayCanvas` API, so the same target works with its VisPy and pygfx renderers.
|
|
210
|
+
Create an empty `ndv.ArrayViewer`, pass it and a source to `NDVController`, then
|
|
211
|
+
submit renderer-neutral `View` snapshots:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
import ndv
|
|
215
|
+
|
|
216
|
+
from lodstone.adapters.ndv import NDVController
|
|
217
|
+
|
|
218
|
+
viewer = ndv.ArrayViewer()
|
|
219
|
+
controller = NDVController(viewer, source)
|
|
220
|
+
controller.update(view)
|
|
221
|
+
viewer.show()
|
|
222
|
+
ndv.run_app()
|
|
223
|
+
controller.close()
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The initial adapter supports translated dense 2-D and 3-D windows, hidden-axis
|
|
227
|
+
selections, camera-driven replanning, progressive phase replacement, independent
|
|
228
|
+
per-image world transforms, and shared runtimes. See `examples/ndv_dense.py`.
|
|
229
|
+
These capabilities currently require ndv's `lodstone-integration` branch until
|
|
230
|
+
its camera, dispatch, and image-transform APIs are released.
|
|
231
|
+
|
|
232
|
+
`examples/napari_zebrahub.py` opens one lazy timepoint from the public
|
|
233
|
+
ZSNS001 Zebrahub light-sheet series in 3-D. Its approximately 32 MiB native
|
|
234
|
+
chunks make it a useful stress test for cancellation, interaction holds, and
|
|
235
|
+
GPU upload pacing. The example exposes `--tile-mib`, `--interval-mib`, and
|
|
236
|
+
`--rate-mib` for tuning those constraints. Pass `--trace-chunks` to report the
|
|
237
|
+
exact desired/wanted tile counts alongside unique native chunks, cache hits,
|
|
238
|
+
joined in-flight reads, actual source reads, and evictions for every pass.
|
|
239
|
+
Pass `--diagnostic-levels` to perform the same real source reads while replacing
|
|
240
|
+
the returned pixels with solid categorical labels: magenta is missing content,
|
|
241
|
+
green is L0, yellow is L1, orange is L2, and deeper levels use additional
|
|
242
|
+
stable colors. This makes incomplete viewport coverage visible independently of
|
|
243
|
+
the image's contrast or texture values.
|
|
244
|
+
|
|
245
|
+
## Public concepts
|
|
246
|
+
|
|
247
|
+
- **Source** — pyramid metadata and asynchronous regional reads.
|
|
248
|
+
- **View** — displayed axes, hidden-axis selections, viewport, and camera matrix.
|
|
249
|
+
- **Target** — desired dense/tiled/bricked layout and update delivery.
|
|
250
|
+
- **Planner** — deterministic visible-tile and LOD selection.
|
|
251
|
+
- **PlanCoverage / PlanDelta** — stable coverage identity plus retained,
|
|
252
|
+
requested, reprioritized, and released work across camera changes.
|
|
253
|
+
- **Stream** — cancellation, priorities, native-chunk reuse, CPU caching,
|
|
254
|
+
batching, progressive delivery, and stale-generation rejection.
|
|
255
|
+
- **Composition** — transform-aware nearest-neighbor backdrop sampling and
|
|
256
|
+
unloaded-chunk filling for bounded dense targets.
|
|
257
|
+
- **VirtualData** — a lazy full-shape array view backed by one movable,
|
|
258
|
+
chunk-aligned in-memory interval; `MultiScaleVirtualData` coordinates these
|
|
259
|
+
intervals and coarse-to-fine backdrop composition across pyramid levels.
|
|
260
|
+
|
|
261
|
+
Storage chunks and display tiles are deliberately distinct. A target may ask
|
|
262
|
+
for 32-cubed bricks while the Zarr source stores 16 by 64 by 64 chunks.
|
|
263
|
+
Lodstone reads each overlapping native chunk once and assembles the requested
|
|
264
|
+
display updates from its decoded cache.
|
|
265
|
+
|
|
266
|
+
Progressive planning starts at the coarsest level by default. Renderer
|
|
267
|
+
integrations can set `Planner(max_initial_voxel_footprint=...)` to choose the
|
|
268
|
+
coarsest initial level whose projected voxels stay within that many screen
|
|
269
|
+
pixels; the normal target level and napari's default behavior are unchanged.
|
|
270
|
+
|
|
271
|
+
`stream.diagnostics` separates renderer tiles from native storage activity for
|
|
272
|
+
the current or most recent generation. `stream.cache_events` records recent
|
|
273
|
+
`queued`, `loading`, `ready`, `failed`, and `evicted` transitions, while
|
|
274
|
+
`stream.chunk_states` exposes the latest state per native chunk. Native chunks
|
|
275
|
+
required by a delivery batch remain pinned until all its display regions have
|
|
276
|
+
been assembled, preventing mid-request eviction and avoidable rereads.
|
|
277
|
+
|
|
278
|
+
## Source adapters
|
|
279
|
+
|
|
280
|
+
`ArrayPyramidSource` accepts NumPy, Dask, Zarr, or other indexable array-like
|
|
281
|
+
levels. `ZarrPyramidSource` opens explicitly named arrays in a Zarr group.
|
|
282
|
+
`OMEZarrSource` discovers nested pyramid levels, axes, and per-level scale and
|
|
283
|
+
translation transforms from OME-Zarr v0.1-v0.5 metadata. It also supports bare
|
|
284
|
+
array pyramids, bounded caches, remote storage options, level limits, and lazy
|
|
285
|
+
fixed-axis or singleton-axis selection.
|
|
286
|
+
|
|
287
|
+
The public chunk-grid utilities normalize NumPy, Dask, regular Zarr, and
|
|
288
|
+
rectilinear Zarr metadata into one exact grid used consistently by sources,
|
|
289
|
+
planners, resident buffers, and viewer adapters.
|
|
290
|
+
|
|
291
|
+
Zarr remains a lazy storage source. NumPy arrays are only the concrete buffers
|
|
292
|
+
delivered for requested regions.
|
|
293
|
+
|
|
294
|
+
`lodstone.datasets` provides reusable Mandelbrot and Mandelbulb pyramids,
|
|
295
|
+
including RGB variants, a local multiscale Zarr builder, and a convenience
|
|
296
|
+
loader for local or remote OME-Zarr data. These fixtures are renderer-neutral
|
|
297
|
+
and are shared by integration examples and LodStone's own source tests.
|
|
298
|
+
|
|
299
|
+
## Target contract
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
class Target:
|
|
303
|
+
def layout(self, view, pyramid) -> Layout: ...
|
|
304
|
+
def apply(self, updates) -> None: ...
|
|
305
|
+
def discard(self, keys) -> None: ...
|
|
306
|
+
def redraw(self) -> None: ...
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Targets with bounded resident windows may additionally implement
|
|
310
|
+
`prepare(view, plan)` and `complete(view, plan)`. Preparation runs on the
|
|
311
|
+
viewer thread before any updates for a pass; completion runs after refinement
|
|
312
|
+
and stale-residency retirement. A plan exposes both its complete `desired`
|
|
313
|
+
tile ladder and the cache-filtered `wanted` reads. Interactive viewers can
|
|
314
|
+
call `stream.pause()` and `stream.resume()` without discarding the active pass.
|
|
315
|
+
`bytes_per_second` can pace aggregate source reads when decoding or remote I/O
|
|
316
|
+
would otherwise compete with interaction and rendering.
|
|
317
|
+
|
|
318
|
+
`prepare` may return a residency lease with dynamic `available_keys` and
|
|
319
|
+
`pending_keys` sets plus `release(keys)`. A lease confirms which target storage
|
|
320
|
+
survives replanning, allowing the stream to retain delivered overlap while it
|
|
321
|
+
keeps loading native chunks shared by the old and new request. Queued work is
|
|
322
|
+
rebuilt in the newest priority order and work outside the new coverage is
|
|
323
|
+
canceled. Legacy targets that return no lease retain conservative pass
|
|
324
|
+
replacement behavior. `stream.delta` exposes the latest `PlanDelta`.
|
|
325
|
+
|
|
326
|
+
Viewers may attach `InteractionState` to a `View` to describe camera motion
|
|
327
|
+
and angular, translation, and zoom velocity. Existing integrations can omit
|
|
328
|
+
it and retain their current policy.
|
|
329
|
+
|
|
330
|
+
Targets that need an atomic presentation point between coarse-to-fine stages
|
|
331
|
+
may also implement `phase_complete(view, plan, phase)`. The hook is optional;
|
|
332
|
+
existing targets continue to receive the same prepare, apply, complete, and
|
|
333
|
+
redraw calls.
|
|
334
|
+
|
|
335
|
+
Dense targets can use `ResidentArrays` to avoid allocating complete pyramid
|
|
336
|
+
levels. It stages one full-ND bounding window per desired level, preserves
|
|
337
|
+
overlapping content when the camera moves, translates absolute updates into
|
|
338
|
+
window-relative writes, and retires coarse/replaced storage on completion.
|
|
339
|
+
The viewer still owns the corresponding grid, texture, or volume objects:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
from lodstone import Layout, ResidentArrays, ResidentLease
|
|
343
|
+
|
|
344
|
+
resident = ResidentArrays(source.pyramid, compose=True)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def layout(view, pyramid):
|
|
348
|
+
return Layout(kind="dense", memory_limit=512 * 1024**2, squeeze_hidden=False)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def prepare(view, plan):
|
|
352
|
+
transition = resident.prepare(plan)
|
|
353
|
+
# Create renderer resources for transition.prepared and remove
|
|
354
|
+
# transition.retired resources.
|
|
355
|
+
desired = plan.desired or plan.wanted
|
|
356
|
+
return ResidentLease(resident, frozenset(tile.key for tile in desired))
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def apply(updates):
|
|
360
|
+
for change in resident.apply(updates):
|
|
361
|
+
# Patch or invalidate change.regions in the renderer resource.
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def complete(view, plan):
|
|
366
|
+
transition = resident.complete(plan)
|
|
367
|
+
# Present resident.active[plan.target_level] and retire old resources.
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
With `compose=True`, coarse updates initialize and repair unloaded native
|
|
371
|
+
chunks in finer pending windows using the pyramid transforms. Directly loaded
|
|
372
|
+
fine chunks are never overwritten. Leaving composition disabled preserves the
|
|
373
|
+
original fill-value and same-level overlap behavior.
|
|
374
|
+
|
|
375
|
+
The initial expected layouts are:
|
|
376
|
+
|
|
377
|
+
| Client | Typical layout |
|
|
378
|
+
| --- | --- |
|
|
379
|
+
| ChimeraX | dense, uniform LOD |
|
|
380
|
+
| napari | tiled |
|
|
381
|
+
| ndv | dense initially |
|
|
382
|
+
|
|
383
|
+
Lodstone deliberately stops before physical GPU allocation. The target owns
|
|
384
|
+
textures, double buffering, shader indirection, and renderer invalidation.
|
|
385
|
+
|
|
386
|
+
## Viewer compatibility
|
|
387
|
+
|
|
388
|
+
The first alpha is intended for integration development. It does not make the
|
|
389
|
+
streaming paths available in unmodified stable releases of every viewer.
|
|
390
|
+
|
|
391
|
+
| Client | Initial support | Required host version | Status |
|
|
392
|
+
| --- | --- | --- | --- |
|
|
393
|
+
| ChimeraX OME-Zarr | 3-D images, channels, one selected timepoint | `chimerax-ome-zarr` PR 22 | Experimental |
|
|
394
|
+
| napari | 2-D/3-D Image and Labels layers | napari PR 34 based on PR 9067 | Experimental |
|
|
395
|
+
| ndv + VisPy | 2-D/3-D dense clipmaps and camera replanning | ndv `lodstone-integration` branch | Reference ndv backend |
|
|
396
|
+
| ndv + PyGFX | Same renderer-neutral data path | ndv `lodstone-integration` branch | Experimental visual parity |
|
|
397
|
+
|
|
398
|
+
Integrations should pin an exact Lodstone prerelease. Compatibility is only
|
|
399
|
+
claimed for combinations exercised by the integration's native tests and smoke
|
|
400
|
+
tests; adapters remain provisional throughout the 0.1 alpha series.
|
|
401
|
+
|
|
402
|
+
## Development
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
uv run --extra test pytest
|
|
406
|
+
uv run --group dev ruff check .
|
|
407
|
+
uv run --group dev pyright src
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
The test suite is network-independent. Remote opening and reading has also
|
|
411
|
+
been checked against the EBI IDR OME-Zarr v0.4 store used by
|
|
412
|
+
`chimerax-ome-zarr`.
|
|
413
|
+
|
|
414
|
+
Release maintainers should follow [`RELEASING.md`](RELEASING.md). Changes are
|
|
415
|
+
recorded in [`CHANGELOG.md`](CHANGELOG.md).
|