omezarr-tilesource 0.5.0 → 0.7.0

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.
package/README.md CHANGED
@@ -38,33 +38,47 @@ import { OMEZarrTileSource } from "omezarr-tilesource";
38
38
 
39
39
  const url = ...;
40
40
 
41
- // configuration with URL (only works with zipped OME-Zarr URLs ending with .ozx)
41
+ // register the tile source and its data type converters with OpenSeadragon
42
+ // (required for inline configurations and URLs, see "Data pipeline" below)
43
+ OMEZarrTileSource.enable(OpenSeadragon);
44
+
45
+ // configuration with URL (only works with zipped OME-Zarr URLs, path ending in .ozx)
42
46
  const tileSource1 = url;
43
47
 
44
- // inline configuration with options object (requires prior enabling, see below)
45
- OMEZarrTileSource.enable(OpenSeadragon);
48
+ // inline configuration with options object
46
49
  const tileSource2 = {
47
50
  type: "ome-zarr",
48
51
  url: url,
49
- // zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx suffix
50
- // c: undefined, // undefined = composite of all active channels (requires dataType "context2d")
51
- // z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
52
+ // zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx path suffix
52
53
  // t: undefined, // undefined = omero rdefs default (middle timepoint if missing)
53
- // dataType: undefined, // "context2d" (default, rendered tiles) or "ome-zarr" (raw single-channel chunks)
54
+ // z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
55
+ // c: undefined, // channel index or array of indices; undefined = all active channels
56
+ // range: undefined, // contrast limits [min, max] or array thereof; undefined = omero windows
57
+ // color: undefined, // RGB color or array of colors; undefined = omero channel colors
58
+ // lutOrColorMap: undefined, // LUT/color map or array thereof; undefined = omero LUTs
59
+ // inverted: undefined, // boolean or array of booleans; undefined = omero inversion
54
60
  // autoBoost: undefined // boost brightness of dark tiles (default false)
55
61
  };
56
62
 
57
63
  // direct instantiation with URL (works with any OME-Zarr storage backend)
58
64
  const tileSource3 = new OMEZarrTileSource(url);
59
65
 
60
- // direct instantiation with options object (no prior enabling required)
61
- const tileSource4 = new OMEZarrTileSource({
66
+ // direct instantiation with a File (or any Blob) holding a zipped OME-Zarr file,
67
+ // e.g. from an <input type="file"> or a drop event; also accepted as `url` in
68
+ // inline configurations and options objects (but not directly as a tile source)
69
+ const tileSource4 = new OMEZarrTileSource(file);
70
+
71
+ // direct instantiation with options object
72
+ const tileSource5 = new OMEZarrTileSource({
62
73
  url: url,
63
- // zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx suffix
64
- // c: undefined, // undefined = composite of all active channels (requires dataType "context2d")
65
- // z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
74
+ // zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx path suffix
66
75
  // t: undefined, // undefined = omero rdefs default (middle timepoint if missing)
67
- // dataType: undefined, // "context2d" (default, rendered tiles) or "ome-zarr" (raw single-channel chunks)
76
+ // z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
77
+ // c: undefined, // channel index or array of indices; undefined = all active channels
78
+ // range: undefined, // contrast limits [min, max] or array thereof; undefined = omero windows
79
+ // color: undefined, // RGB color or array of colors; undefined = omero channel colors
80
+ // lutOrColorMap: undefined, // LUT/color map or array thereof; undefined = omero LUTs
81
+ // inverted: undefined, // boolean or array of booleans; undefined = omero inversion
68
82
  // autoBoost: undefined // boost brightness of dark tiles (default false)
69
83
  });
70
84
 
@@ -74,86 +88,202 @@ const viewer = OpenSeadragon({
74
88
  tileSource1,
75
89
  tileSource2,
76
90
  tileSource3,
77
- tileSource4
91
+ tileSource4,
92
+ tileSource5
78
93
  ]
79
94
  });
80
95
  ```
81
96
 
97
+ ### Options
98
+
99
+ | Option | Type | Default | Description |
100
+ | --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | `url` | `string \| URL \| Blob` | (required) | URL of the OME-Zarr image (group) or of a zipped OME-Zarr file, relative to the document base URL, or a `Blob` (e.g. a `File`) holding a zipped OME-Zarr file |
102
+ | `zip` | `boolean` | `true` if the `url` path ends with `.ozx` or `url` is a `Blob` | Whether `url` points to a zipped OME-Zarr file; must not be `false` for a `Blob` |
103
+ | `t` | `number` | omero `rdefs.defaultT`, else middle plane | Timepoint index (0-based) |
104
+ | `z` | `number` | omero `rdefs.defaultZ`, else middle plane | Z-slice index (0-based) |
105
+ | `c` | `number \| number[]` | all channels marked active in omero | Channel index or indices (0-based) to render, composited in the given order. Arrays must be non-empty |
106
+ | `range` | `[number, number] \| ([number, number] \| undefined)[]` | omero channel windows (`window.start`, `window.end`), else the data type range | Contrast limits (`[min, max]`) to render the channels with. Ignored for channels rendered with a color map |
107
+ | `color` | `Color \| (Color \| undefined)[]` | colors of the rendered omero channels, else white | RGB color or colors (`[r, g, b]`, 0-255) to render the channels with. Ignored for channels rendered with a LUT or color map |
108
+ | `lutOrColorMap` | `LUTOrColorMap \| (LUTOrColorMap \| undefined)[]` | omero channel LUTs and color maps, else none | Color LUT (a color per scaled value) or color map (a color per raw value, `Map`) to render the channels with; colors may carry alpha. A color map also overrides `range` and `inverted` |
109
+ | `inverted` | `boolean \| (boolean \| undefined)[]` | omero channel inversion, else `false` | Whether to invert the channels. Ignored for channels rendered with a color map |
110
+ | `autoBoost` | `boolean` | `false` | Boost the brightness of dark tiles (ome-zarr.js `renderChunks` option) |
111
+
112
+ `range`, `color`, `lutOrColorMap` and `inverted` take a single value, which
113
+ applies to all rendered channels, or one entry per rendered channel. An
114
+ `undefined` entry falls back to the omero metadata of that channel, as does the
115
+ option as a whole. The `ranges`, `colors`, `lutsOrColorMaps` and `inverteds`
116
+ getters return the resolved per-channel arrays.
117
+
118
+ `t`, `z` and `c` are validated against the image shape when the OME-Zarr
119
+ metadata is loaded; invalid indices fail with `open-failed`. Images without
120
+ `c` fail if the omero metadata marks no channel as active. An invalid `c`,
121
+ `url` or rendering setting is rejected by the constructor, which throws
122
+ synchronously before any request is made; the length of a rendering setting
123
+ array is only checked against `c` if that is configured too.
124
+
125
+ `url` is resolved against the document base URL when the tile source is
126
+ created; the resolved absolute URL is available as `tileSource.url` (always a
127
+ string) and is used for loading, for the tile cache keys and for comparing tile
128
+ sources.
129
+
130
+ A `Blob` (e.g. a `File` picked by the user) is read with the zarrita
131
+ `ZipFileStore.fromBlob` store, so it must hold a zipped OME-Zarr file
132
+ (`zip: false` is rejected by the constructor). The `Blob` is available as
133
+ `tileSource.blob`, and `tileSource.url` is an object URL created once per
134
+ `Blob` instance (never revoked), so tile sources for the same `Blob` share
135
+ cached tiles. OpenSeadragon only passes strings and plain objects to
136
+ `supports`, so a `File` must be wrapped in an inline configuration
137
+ (`{ type: "ome-zarr", url: file }`) or passed to the constructor rather than
138
+ given to the viewer directly.
139
+
82
140
  ### Accessing OME-Zarr metadata
83
141
 
84
142
  Directly instantiated tile sources start loading the OME-Zarr metadata
85
143
  immediately. Await `whenReady()` (or use the `OMEZarrTileSource.open` shortcut)
86
- to access the `NgffImage` instance (ome-zarr.js) and the opened zarrita arrays
87
- (one per resolution level) before adding the tile source to a viewer:
144
+ to access the loaded image (`loaded.image`, an ome-zarr.js `NgffImage`) and the
145
+ opened zarrita arrays (`loaded.arrays`, one per resolution level, highest
146
+ resolution first) before adding the tile source to a viewer:
88
147
 
89
148
  ```javascript
90
149
  const tileSource = await OMEZarrTileSource.open({ url: url, c: 0 });
91
150
  // equivalent: await new OMEZarrTileSource({ url: url, c: 0 }).whenReady();
92
151
 
93
- console.log(tileSource.image.getAxesNames()); // e.g. ["t", "z", "c", "y", "x"]
94
- console.log(tileSource.image.omero?.channels); // omero channel metadata
95
- console.log(tileSource.arrays[0].shape); // full-resolution array shape
152
+ console.log(tileSource.loaded.image.getAxesNames()); // e.g. ["t", "c", "z", "y", "x"]
153
+ console.log(tileSource.loaded.image.omero?.channels); // omero channel metadata
154
+ console.log(tileSource.loaded.arrays[0].shape); // full-resolution array shape
155
+
156
+ console.log(tileSource.t, tileSource.z, tileSource.cs); // resolved indices
157
+ console.log(tileSource.channels); // omero channels of the rendered channel indices
158
+ console.log(tileSource.ranges, tileSource.colors); // resolved rendering settings
159
+ console.log(tileSource.lutsOrColorMaps, tileSource.inverteds);
160
+ console.log(tileSource.getWidth(), tileSource.getHeight()); // full resolution
161
+ console.log(tileSource.getWidth(0), tileSource.getHeight(0)); // lowest resolution
96
162
 
97
163
  viewer.addTiledImage({ tileSource: tileSource }); // no second metadata request
98
164
  ```
99
165
 
100
- The metadata is loaded once per tile source instance: OpenSeadragon reuses a
101
- tile source instance passed to it as-is (waiting for it to become ready if
102
- necessary), whereas a URL or an inline configuration object makes OpenSeadragon
103
- create (and load) a new instance. `whenReady()` rejects (and `image`/`arrays`
104
- throw) if loading fails.
166
+ `OMEZarrTileSource.open` accepts an `AbortSignal` as `signal` in its third
167
+ argument to cancel loading. The metadata is loaded once per tile source
168
+ instance: OpenSeadragon reuses a tile source instance passed to it as-is
169
+ (waiting for it to become ready if necessary), whereas a URL or an inline
170
+ configuration object makes OpenSeadragon create (and load) a new instance.
171
+ `whenReady()` rejects (and `loaded` throws) if loading or validation fails.
172
+
173
+ The `t`, `z` and `cs` getters return the resolved values (the omero defaults or
174
+ active channels if the option was not given); `cs` is always an array (the
175
+ `c` option may be a single index), and `channels` returns the omero channel
176
+ objects for `cs`. Before the tile source is ready, they return the configured
177
+ options. The `ranges`, `colors`, `lutsOrColorMaps` and `inverteds` getters
178
+ return the resolved rendering settings of the rendered channels, merging the
179
+ configured values with the omero metadata per channel: `undefined` before the
180
+ tile source is ready or if the image has no omero metadata, like `channels`.
181
+ Entries that are still `undefined` fall back at render time to the data type
182
+ range, white, no LUT and not inverted.
105
183
 
106
184
  ### Sharing OME-Zarr metadata between tile sources
107
185
 
108
- A loaded `NgffImage` can be reused by other directly instantiated tile sources
109
- for the same URL (e.g. one tile source per channel) by passing it as the second
110
- constructor argument (also supported by `OMEZarrTileSource.open`). This skips
111
- loading the OME-Zarr metadata (the `zip` option is ignored) and reuses the
112
- opened zarrita arrays, which ome-zarr.js caches on the `NgffImage` instance.
113
- The tile source never modifies the `NgffImage`, so sharing is safe:
186
+ The loaded image and arrays (`OMEZarr`) can be reused by other directly
187
+ instantiated tile sources for the same URL (e.g. one tile source per channel)
188
+ by passing them as the second constructor argument (also supported by
189
+ `OMEZarrTileSource.open`). This skips loading the OME-Zarr metadata (the `zip`
190
+ option is ignored) and reuses the opened zarrita arrays. The tile source never
191
+ modifies the loaded image, so sharing is safe:
114
192
 
115
193
  ```javascript
116
- import { NgffImage } from "ome-zarr.js";
194
+ // load once with the tile source's loader ...
195
+ const loaded = await OMEZarrTileSource.loadOMEZarr(url);
196
+ const tileSources = loaded.image.omero.channels.map(
197
+ (_, c) => new OMEZarrTileSource({ url: url, c: c }, loaded),
198
+ );
117
199
 
118
- // reuse the image loaded by a first tile source
200
+ // ... or reuse what a first tile source has loaded
119
201
  const tileSource1 = await OMEZarrTileSource.open({ url: url, c: 0 });
120
202
  const tileSource2 = new OMEZarrTileSource(
121
203
  { url: url, c: 1 },
122
- tileSource1.image,
204
+ tileSource1.loaded,
123
205
  );
206
+ ```
207
+
208
+ `loadOMEZarr(url, zip?, { signal })` loads the metadata with ome-zarr.js and
209
+ opens the arrays of all resolution levels; `url` may also be a `Blob` holding a
210
+ zipped OME-Zarr file. An `OMEZarr` can also be assembled
211
+ from an `NgffImage` loaded by the app, as long as `arrays` lists the
212
+ opened arrays of `image.paths` in order. It is not checked against the URL of
213
+ the tile source it is passed to.
214
+
215
+ ### Loading chunks directly
124
216
 
125
- // or load the image with the app's own ome-zarr.js import
126
- const image = await NgffImage.load(url);
127
- const tileSource3 = new OMEZarrTileSource({ url: url, c: 2 }, image);
217
+ `loadChunks(level, tile, { signal })` returns the (y, x) zarrita chunks of the
218
+ rendered channels at the rendered timepoint and z-slice, either for one tile
219
+ (`{ x, y }` tile coordinates, as passed to OpenSeadragon) or for the whole
220
+ level plane (`tile` `undefined`):
221
+
222
+ ```javascript
223
+ const chunks = await tileSource.loadChunks(tileSource.maxLevel, { x: 0, y: 0 });
224
+ const plane = await tileSource.loadChunks(0, undefined); // lowest resolution
225
+ ```
226
+
227
+ `OMEZarrTileSource.render(tileData)` composites such chunks into a 2D canvas
228
+ context, the same way the `ome-zarr` to `context2d` converter does:
229
+
230
+ ```javascript
231
+ import { getDataTypeRange } from "omezarr-tilesource";
232
+
233
+ const ctx = OMEZarrTileSource.render({
234
+ chunks: plane,
235
+ // the tile data holds the renderChunks arguments, one per chunk, with the
236
+ // defaults of the tile source getters applied
237
+ ranges: plane.map(
238
+ (chunk, c) => tileSource.ranges?.[c] ?? getDataTypeRange(chunk),
239
+ ),
240
+ colors: plane.map((_, c) => tileSource.colors?.[c] ?? [255, 255, 255]),
241
+ lutsOrColorMaps: plane.map((_, c) => tileSource.lutsOrColorMaps?.[c]),
242
+ inverteds: plane.map((_, c) => tileSource.inverteds?.[c] ?? false),
243
+ autoBoost: tileSource.autoBoost,
244
+ });
128
245
  ```
129
246
 
130
247
  ## Data pipeline
131
248
 
132
- By default (`dataType: "context2d"`), tiles are rendered by the tile source and
133
- passed to OpenSeadragon as 2D canvas contexts, using the rendering settings
134
- (color, color LUT/map, contrast limits, inversion) from the omero metadata. For
135
- multi-channel images without `c`, all active channels are rendered into a
136
- composite image.
137
-
138
- Contrast limits are taken from the omero channel windows (`window.start`,
139
- `window.end`). Channels without them are rendered using the data type range
140
- for integer types (e.g. `[0, 65535]` for `uint16`) and `[0, 1]` for floating
141
- point types.
142
-
143
- With `dataType: "ome-zarr"`, tiles are instead downloaded as raw single-channel
144
- zarrita chunks and passed to OpenSeadragon with the data type `ome-zarr` (see
145
- the `OMEZarrTileData` type). Multi-channel images therefore require the `c`
146
- option. A converter from `ome-zarr` to `context2d` is registered on
147
- `OpenSeadragon.converter` when the module is imported (and by
148
- `OMEZarrTileSource.enable`). It reads the rendering settings at conversion time
149
- from the omero channel referenced by the tile data (`OMEZarrTileData.channel`).
150
- The channel object is shared by all tiles of a tile source, so advanced users
151
- may modify it (e.g. from a `tile-invalidated` handler) and re-render the cached
152
- tiles using `viewer.requestInvalidate()`.
249
+ Tiles are downloaded as raw zarrita chunks, one per rendered channel, together
250
+ with the rendering settings resolved for that tile, and passed to OpenSeadragon
251
+ with the data type `ome-zarr` (see the `OMEZarrTileData` type: `chunks`,
252
+ `ranges`, `colors`, `lutsOrColorMaps`, `inverteds` and `autoBoost` — the
253
+ arguments of ome-zarr.js `renderChunks`, one entry per chunk). A converter from
254
+ `ome-zarr` to `context2d` calls `OMEZarrTileSource.render` on demand, which
255
+ composites them into a 2D canvas context. Because the raw chunks stay in the
256
+ tile cache, tiles can be re-rendered without re-downloading them.
257
+
258
+ Settings that are neither configured nor in the omero metadata are resolved
259
+ when the tile is downloaded: to the data type range for integer types (e.g.
260
+ `[0, 65535]` for `uint16`) and `[0, 1]` for floating point types, to white, to
261
+ no LUT and to not inverted — so an image without omero metadata is rendered in
262
+ white over its full data type range.
263
+
264
+ The converters are registered on `OpenSeadragon.converter` when the module is
265
+ imported (for the OpenSeadragon instance it imports, and for a global
266
+ `OpenSeadragon` if present) and by `OMEZarrTileSource.enable`. Tiles cannot be
267
+ rendered without them, so call `enable` if the app uses a different
268
+ OpenSeadragon instance than the one resolved by this module (e.g. a separately
269
+ bundled copy).
270
+
271
+ All rendering settings in `OMEZarrTileData` are plain values, resolved from the
272
+ tile source configuration and the omero metadata when the tile is downloaded.
273
+ Editing the omero channels of a loaded image therefore does not affect tiles
274
+ that are already cached, with or without `viewer.requestInvalidate()`; render
275
+ them differently by creating a tile source with the corresponding options
276
+ instead. Tile sources that configure rendering settings do not share cached
277
+ tiles with tile sources that configure different ones.
153
278
 
154
279
  ## Example
155
280
 
156
- [Example](https://tissuumaps.github.io/OMEZarrTileSource)
281
+ [https://tissuumaps.github.io/OMEZarrTileSource?url=https://livingobjects.ebi.ac.uk/idr/zarr/v0.5/idr0062A/6001240_labels.zarr](https://tissuumaps.github.io/OMEZarrTileSource?url=https%3A%2F%2Flivingobjects.ebi.ac.uk%2Fidr%2Fzarr%2Fv0.5%2Fidr0062A%2F6001240_labels.zarr)
282
+
283
+ The example page takes the URL of the OME-Zarr image from the mandatory `url`
284
+ query parameter, and the timepoint index, the z-slice index and the channel
285
+ indices from the optional `t`, `z` and `c` query parameters (repeat `c` for
286
+ multiple channels).
157
287
 
158
288
  [Source code](index.html)
159
289