micro-reader 0.2.1__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.
- {micro_reader-0.2.1 → micro_reader-0.4.0}/.gitignore +2 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/PKG-INFO +92 -44
- {micro_reader-0.2.1 → micro_reader-0.4.0}/README.md +90 -42
- {micro_reader-0.2.1 → micro_reader-0.4.0}/pyproject.toml +2 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/__init__.py +2 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_axes.py +69 -6
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_base.py +131 -21
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_errors.py +6 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_fileset.py +77 -42
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_groups.py +28 -8
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_io.py +4 -2
- micro_reader-0.4.0/src/micro_reader/_lambda.py +147 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_memory.py +7 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_metadata.py +30 -3
- micro_reader-0.4.0/src/micro_reader/_naming.py +113 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_outline.py +4 -4
- micro_reader-0.4.0/src/micro_reader/_parts.py +366 -0
- micro_reader-0.4.0/src/micro_reader/_plate.py +558 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_pool.py +6 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_registry.py +123 -21
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_spatial.py +2 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_stack.py +5 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_version.py +1 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_czi_metadata.py +18 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_ets.py +8 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_hdf5.py +17 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_lif_metadata.py +3 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_nd2_legacy.py +11 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_nd2_metadata.py +45 -11
- micro_reader-0.4.0/src/micro_reader/backends/_nd2_run.py +266 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_nd2_structure.py +47 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_tiff_metadata.py +134 -17
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/bdv.py +21 -3
- micro_reader-0.4.0/src/micro_reader/backends/cellomics.py +228 -0
- micro_reader-0.4.0/src/micro_reader/backends/columbus.py +113 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/companion.py +223 -37
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/cv7000.py +30 -10
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/czi.py +23 -3
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/dv.py +4 -1
- micro_reader-0.4.0/src/micro_reader/backends/flex.py +351 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/harmony.py +51 -8
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/hdf5.py +6 -3
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/ims.py +47 -11
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/incell.py +15 -1
- micro_reader-0.4.0/src/micro_reader/backends/jdce.py +253 -0
- micro_reader-0.4.0/src/micro_reader/backends/klb.py +175 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/lif.py +67 -2
- micro_reader-0.4.0/src/micro_reader/backends/luxendo.py +718 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/metamorph.py +36 -1
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/metaxpress.py +42 -19
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/micromanager.py +41 -7
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/nd2.py +118 -11
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/oir.py +28 -2
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/raster.py +7 -0
- micro_reader-0.4.0/src/micro_reader/backends/scanr.py +491 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/tiff.py +127 -9
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/vsi.py +9 -4
- micro_reader-0.2.1/src/micro_reader/_parts.py +0 -188
- micro_reader-0.2.1/src/micro_reader/_plate.py +0 -195
- {micro_reader-0.2.1 → micro_reader-0.4.0}/LICENSE +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_cfb.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_codecs.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_eubi_guard.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_index.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_jpeg_restarts.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_mosaic.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/_stream.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/__init__.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_tiff_variants.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/_vsi_tree.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/ics.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/ilastik.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/mrc.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/ndpis.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/oif.py +0 -0
- {micro_reader-0.2.1 → micro_reader-0.4.0}/src/micro_reader/backends/omp2info.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: micro-reader
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Low-level, memory-bounded pixel reading for microscopy formats: read exactly the rectangle you ask for.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bugraoezdemir/micro-reader
|
|
6
6
|
Project-URL: Source, https://github.com/bugraoezdemir/micro-reader
|
|
@@ -9,7 +9,7 @@ Author-email: Bugra Oezdemir <bugraa.ozdemir@gmail.com>
|
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Keywords: bioimage,cryo-em,czi,eer,image-io,imaris,ims,leica,lif,lsm,memory-bounded,microscopy,mrc,nd2,oir,ome-tiff,tiff
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -56,9 +56,9 @@ Description-Content-Type: text/markdown
|
|
|
56
56
|
|
|
57
57
|
**Memory-bounded pixel reader for microscopy images.**
|
|
58
58
|
|
|
59
|
-
`micro-reader` reads regions of large microscopy files without loading the entire image into memory. It provides a common interface across TIFF, CZI, ND2, Leica, OIR, OIB / OIF, Olympus VSI, Imaris, DeltaVision, MRC, common raster formats, OME companion file sets, and high-content screening plates (Harmony, MetaXpress, IN Cell, CellVoyager).
|
|
59
|
+
`micro-reader` reads regions of large microscopy files without loading the entire image into memory. It provides a common interface across TIFF, CZI, ND2, Leica, OIR, OIB / OIF, Olympus VSI, Imaris, DeltaVision, MRC, KLB, common raster formats, OME companion file sets, and high-content screening plates (Harmony, Columbus, MetaXpress, ImageXpress HCS.ai, IN Cell, CellVoyager, CQ1, Opera Flex, scanR, Cellomics).
|
|
60
60
|
|
|
61
|
-
> **Status:
|
|
61
|
+
> **Status: alpha (0.x)**
|
|
62
62
|
>
|
|
63
63
|
> The API may still change.
|
|
64
64
|
|
|
@@ -77,14 +77,6 @@ pip install "micro-reader[ims]" # Imaris (.ims)
|
|
|
77
77
|
pip install "micro-reader[codecs]" # additional codecs
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
## Compatibility with eubi-bridge
|
|
81
|
-
|
|
82
|
-
micro-reader 0.1.0 renamed the image index `scene` to `series`. eubi-bridge 0.1.3 to 0.2.x and eubi-bridge-lite below 0.3.0 still read `scene`, so `import micro_reader` refuses to load next to them. It raises an `ImportError` that names the installed eubi version and the two ways out: upgrade eubi (`pip install -U 'eubi-bridge>=0.3.0'`, or the same for `eubi-bridge-lite`), or keep that eubi with the micro-reader it was built for (`pip install 'micro-reader<0.1'`). The check reads the versions of the installed packages; it never imports eubi. An editable install of eubi is checked like any other: its recorded version can be out of date, so the message then says to reinstall it.
|
|
83
|
-
|
|
84
|
-
To skip the check, set the environment variable `MICRO_READER_SKIP_EUBI_CHECK=1`. Only the value `1` skips it; any other value keeps the check. micro-reader then warns once that the check was skipped. This is meant for work on an eubi checkout, not for normal use.
|
|
85
|
-
|
|
86
|
-
The check will be removed when every eubi-bridge / eubi-bridge-lite line below 0.3.0 has left the support window, checked by the release version matrix; not before.
|
|
87
|
-
|
|
88
80
|
## Quick start
|
|
89
81
|
|
|
90
82
|
```python
|
|
@@ -197,6 +189,17 @@ f.select(series=1, view=0)
|
|
|
197
189
|
|
|
198
190
|
This returns the images matching the requested indices. For example, if a file contains several series, tiles, and views, selecting `series=1, view=0` returns the tiles belonging to that series and view.
|
|
199
191
|
|
|
192
|
+
### Image names
|
|
193
|
+
|
|
194
|
+
`image.name` follows one rule for every format, and is the same whichever file of a dataset you open:
|
|
195
|
+
|
|
196
|
+
1. A plate's image is named by its well and field: `"well B2 field 0"` (the field counted from 0, as in `select(field=0)`; the plate's name in front when the file holds several plates).
|
|
197
|
+
2. Otherwise the name the format gives the image: a Leica element's name, an OME `Image Name`, an Imaris image name, a slide's associated image by its role in lower case (`label`, `macro`, `thumbnail`). A slide's main scan, which its format does not name, takes the name of its file.
|
|
198
|
+
3. Otherwise, in a file of several images, a label the file records for each one (a CZI scene name, an ND2 point name, a Micro-Manager or MetaMorph position label), or else the index values that tell them apart: `"series 2"`, `"series 1, tile 3"`.
|
|
199
|
+
4. Otherwise (one image) the name of the dataset's main file without its extension: the index file of a set, the main file of a multi-file format, a Luxendo experiment's folder.
|
|
200
|
+
|
|
201
|
+
Two images left with one name get the index values that differ between them appended, for example `"scan, series 2"`. Resolution levels carry their image's name.
|
|
202
|
+
|
|
200
203
|
### Available index dimensions
|
|
201
204
|
|
|
202
205
|
`micro-reader` uses these index names when a format provides the corresponding dimensions:
|
|
@@ -233,7 +236,7 @@ f.path # the main file: "cells_R3D.dv"
|
|
|
233
236
|
f.opened # what was opened: "cells_R3D.dv.log"
|
|
234
237
|
```
|
|
235
238
|
|
|
236
|
-
You can also open the folder. A folder opens the image it belongs to (an NDTiff dataset folder, a Harmony or CellVoyager measurement folder), or the one image it holds. A folder with several images raises an error that lists them.
|
|
239
|
+
You can also open the folder. A folder opens the image it belongs to (an NDTiff dataset folder, a Harmony or CellVoyager measurement folder, a folder of Opera `.flex` files, a Luxendo experiment), or the one image it holds. A folder that holds no image itself, such as a downloaded sample folder with the export one or two levels down, opens the one dataset found in its sub-folders. An Opera Flex plate has no index file: any of its `.flex` files opens the whole folder as the plate (`multifile=False` reads only that file). A folder of one NIS-Elements well-plate run opens as that plate (with `plates=False`, its files are several images). A folder with several images or datasets raises an error that lists them; an OME-Zarr store is refused with a pointer to `ome_zarr_pyramid`, which reads it.
|
|
237
240
|
|
|
238
241
|
To sort many files at once, for example everything under a folder you want to convert, `group_files` says which files make one image:
|
|
239
242
|
|
|
@@ -246,7 +249,7 @@ for group in g.groups:
|
|
|
246
249
|
g.singles # every other file, as given
|
|
247
250
|
```
|
|
248
251
|
|
|
249
|
-
A file that a plate or companion index names is listed only in that index's group. When the main file of a multi-file image is missing, its group has `main=None`.
|
|
252
|
+
A file that a plate or companion index names is listed only in that index's group. When the main file of a multi-file image is missing, its group has `main=None`. It groups as `open()` reads, plates included (`plates=True`, the default): a folder of ND2 files from one NIS-Elements well-plate run is one group; `plates=False` groups as `open(..., plates=False)` reads.
|
|
250
253
|
|
|
251
254
|
## Axes
|
|
252
255
|
|
|
@@ -274,37 +277,47 @@ image.shape
|
|
|
274
277
|
# (1, 3, 40, 5120, 6144)
|
|
275
278
|
```
|
|
276
279
|
|
|
277
|
-
Other dimensions, such as illumination,
|
|
280
|
+
Other dimensions, such as illumination, excitation, tile, etc., are represented as image indices rather than array axes, as discussed above.
|
|
278
281
|
|
|
279
|
-
###
|
|
282
|
+
### Lambda scans
|
|
280
283
|
|
|
281
|
-
|
|
284
|
+
A lambda (spectral) scan is one image in every format. Its bands lie along `c`, a detector's bands next to each other in wavelength order, and each channel has its `emission_wavelength` and its wavelength in its name (`"HyD S 1 420 nm"`).
|
|
282
285
|
|
|
283
286
|
```python
|
|
284
|
-
|
|
287
|
+
f = micro_reader.open("scan.lif")
|
|
288
|
+
f.images[0].axes # 'tczyx'
|
|
289
|
+
[c.emission_wavelength for c in f.images[0].metadata.channels]
|
|
290
|
+
|
|
291
|
+
f = micro_reader.open("scan.lif", axes="native")
|
|
292
|
+
f.images[0].axes # 'tlczyx': a real lambda axis
|
|
293
|
+
f.images[0].metadata.index_values["emission"] # its wavelengths
|
|
285
294
|
```
|
|
286
295
|
|
|
287
|
-
|
|
296
|
+
Asking for axes that name `l`, such as `axes="tlczyx"`, also gives the axis. An LSM stores its bands as channels, so it has no `l` axis; its channels carry their wavelengths. Excitation scans stay an image index.
|
|
297
|
+
|
|
298
|
+
`open` gives every image in this canonical order by default, whatever order the file stores: axes the file lacks are added with size 1 (a 2-D TIFF is `(1, 1, 1, Y, X)`) and the others are reordered. Reading costs the same either way.
|
|
299
|
+
|
|
300
|
+
### Changing axis order
|
|
301
|
+
|
|
302
|
+
To keep the order the file stores (ICS `ctzyx`, Micro-Manager OME-TIFF `tzcyx`, a 2-D TIFF `yx`):
|
|
288
303
|
|
|
289
304
|
```python
|
|
290
|
-
f = micro_reader.open("image.
|
|
305
|
+
f = micro_reader.open("image.ics", axes="native")
|
|
291
306
|
```
|
|
292
307
|
|
|
293
|
-
You can also request
|
|
308
|
+
You can also request a specific axis order:
|
|
294
309
|
|
|
295
310
|
```python
|
|
296
|
-
|
|
311
|
+
image = f.images[0].as_axes("tczyx")
|
|
297
312
|
```
|
|
298
313
|
|
|
299
|
-
|
|
314
|
+
or when opening the file:
|
|
300
315
|
|
|
301
|
-
```
|
|
302
|
-
|
|
316
|
+
```python
|
|
317
|
+
f = micro_reader.open("image.tif", axes="zyx")
|
|
303
318
|
```
|
|
304
319
|
|
|
305
|
-
followed by `s` when RGB samples are
|
|
306
|
-
|
|
307
|
-
Missing axes are added with size 1 and existing axes are reordered.
|
|
320
|
+
`axes="canonical"` (the default) is `t c z y x`, followed by `s` when RGB samples are kept (`samples="keep"`).
|
|
308
321
|
|
|
309
322
|
### Removing singleton axes
|
|
310
323
|
|
|
@@ -324,18 +337,18 @@ Only axes with size 1 can be removed. `y` and `x` are never removed.
|
|
|
324
337
|
|
|
325
338
|
### RGB samples
|
|
326
339
|
|
|
327
|
-
|
|
340
|
+
`micro_reader.open` turns RGB samples into channels: an RGB image has 3 channels, named "<name> R", "G" and "B" and coloured red, green and blue (a file without a channel name gives three unnamed channels in those colours). An image that has channels already gets c × s of them: "<name> R" ... for each.
|
|
328
341
|
|
|
329
|
-
|
|
342
|
+
To keep RGB samples as their own `s` axis, the way the file stores them:
|
|
330
343
|
|
|
331
344
|
```python
|
|
332
345
|
f = micro_reader.open(
|
|
333
346
|
"image.tif",
|
|
334
|
-
samples="
|
|
347
|
+
samples="keep",
|
|
335
348
|
)
|
|
336
349
|
```
|
|
337
350
|
|
|
338
|
-
|
|
351
|
+
`image.source` is then not needed: with the default, it is the image as stored, with its `s` axis. `image.as_axes(...)` keeps `s` unless asked for `samples="channels"`.
|
|
339
352
|
|
|
340
353
|
|
|
341
354
|
## Ambiguous dimensions
|
|
@@ -358,8 +371,8 @@ print(len(f.images))
|
|
|
358
371
|
print(f.images[0].index)
|
|
359
372
|
# {"series": 0, "sequence": 0}
|
|
360
373
|
|
|
361
|
-
print(f.images[0].axes)
|
|
362
|
-
# yx
|
|
374
|
+
print(f.images[0].axes, f.images[0].shape)
|
|
375
|
+
# tczyx (1, 1, 1, 16, 16) (axes="canonical", the default; axes=None gives yx)
|
|
363
376
|
```
|
|
364
377
|
|
|
365
378
|
If you know that the pages represent `z`, you can tell the reader:
|
|
@@ -371,11 +384,11 @@ f = micro_reader.open(
|
|
|
371
384
|
)
|
|
372
385
|
```
|
|
373
386
|
|
|
374
|
-
The result is a single image with `
|
|
387
|
+
The result is a single image with the 80 pages along `z`.
|
|
375
388
|
|
|
376
389
|
```python
|
|
377
|
-
print(f.images[0].axes)
|
|
378
|
-
# zyx
|
|
390
|
+
print(f.images[0].axes, f.images[0].shape)
|
|
391
|
+
# tczyx (1, 1, 80, 16, 16) (axes=None gives zyx)
|
|
379
392
|
```
|
|
380
393
|
|
|
381
394
|
|
|
@@ -463,6 +476,8 @@ f = micro_reader.open(
|
|
|
463
476
|
|
|
464
477
|
CZI tiles are placed by the file's own stitching information, FluoView tiles by the tiling set's grid (where tiles overlap, the later tile is on top; areas without a tile read as 0). Other formats with tiles raise an error for `tiles="mosaic"`.
|
|
465
478
|
|
|
479
|
+
A format that has tiles it cannot place refuses `tiles="mosaic"` with an error. A file without tiles has nothing to place and opens as usual: Micro-Manager and MetaMorph stage positions are series, not tiles.
|
|
480
|
+
|
|
466
481
|
`tiles="stitched"`, the name before 0.1.0, still works and warns.
|
|
467
482
|
|
|
468
483
|
## Plates
|
|
@@ -475,21 +490,38 @@ The plate layout is available through:
|
|
|
475
490
|
plate = f.plates[0]
|
|
476
491
|
plate.wells[0].name
|
|
477
492
|
# "B2"
|
|
493
|
+
plate.row_names, plate.column_names # ('A', 'B', ...), ('1', '2', ...)
|
|
494
|
+
part = plate.rows[2:5].columns[3:7] # a part of the plate: still a plate
|
|
478
495
|
```
|
|
479
496
|
|
|
497
|
+
Every level knows what it is and what holds it: `.kind` is `"file"`, `"plate"`, `"well"`
|
|
498
|
+
or `"field"`, and `.parent` is the level above. Accessors go down only: a file holds
|
|
499
|
+
`.plates`, a plate `.wells`, a well `.fields`, a field `.images`. `plate.rows` and
|
|
500
|
+
`plate.columns` slice a plate by position, name or a list of them, as `f.select(row=...,
|
|
501
|
+
column=...)` does; rows and columns are a well's address, not a level.
|
|
502
|
+
|
|
480
503
|
Images can be reached through the wells, or selected by well:
|
|
481
504
|
|
|
482
505
|
```python
|
|
483
506
|
image = plate.wells[0].fields[0].image
|
|
484
|
-
f.select(well=plate.well_index("B2"))
|
|
507
|
+
f.select(well=plate.well_index("B2")) # by its place in plate.wells
|
|
508
|
+
f.select(well_name="B2") # by name
|
|
509
|
+
f.select(row_name="B", column=range(1, 4)) # rows and columns: names, or 0-based numbers
|
|
510
|
+
f.select(well=["B2", "C5"], field=[0, 1]) # a list, tuple, range or set: any of them
|
|
511
|
+
f.select(row=slice(2, None)) # a slice of positions: row C onward
|
|
485
512
|
```
|
|
486
513
|
|
|
514
|
+
A plate's places have a position and a name, a keyword each: `plate` / `plate_name`,
|
|
515
|
+
`acquisition` / `acquisition_name`, `well` / `well_name`, `row` / `row_name`, `column` /
|
|
516
|
+
`column_name` (the columns of ome-zarr-pyramid's table). A name given to a position keyword
|
|
517
|
+
is its name (`well="B2"` is `well_name="B2"`).
|
|
518
|
+
|
|
487
519
|
`f.outline()` shows the plates, wells and fields of a file.
|
|
488
520
|
|
|
489
|
-
|
|
521
|
+
A plate in a generic format (a CZI, an ND2, an OME-TIFF or a companion with wells) is read as a plate too. `plates=False` reads it by its series instead:
|
|
490
522
|
|
|
491
523
|
```python
|
|
492
|
-
f = micro_reader.open("plate.czi"
|
|
524
|
+
f = micro_reader.open("plate.czi")
|
|
493
525
|
```
|
|
494
526
|
|
|
495
527
|
Missing plane files are read as zeros, with a `MissingImagesWarning`. A field without any of its files is left out.
|
|
@@ -548,6 +580,7 @@ meta.channels[0]
|
|
|
548
580
|
`micro-reader` follows a few important rules:
|
|
549
581
|
|
|
550
582
|
* Units use OME-Zarr-compatible names such as `micrometer`, `nanometer`, and `millisecond`.
|
|
583
|
+
* Each value keeps the unit the file records (lengths above a millimetre become micrometres), so two formats can give the same quantity in different units, for example a time step in milliseconds (ND2, OIR) or in seconds (CZI, LIF). Convert to compare: `image.metadata.pixel_sizes["t"].to("s")`, `.to("um")`. Converting a length to a time, or to an unknown unit, raises `ValueError`.
|
|
551
584
|
* Missing metadata is left as `None` or absent.
|
|
552
585
|
* Pixel sizes are never invented as `1.0`.
|
|
553
586
|
* Colours are not invented when the file does not provide them.
|
|
@@ -559,7 +592,7 @@ meta.channels[0]
|
|
|
559
592
|
|
|
560
593
|
Where possible, format-specific coordinate conventions are converted to this common representation.
|
|
561
594
|
|
|
562
|
-
The position is omitted when it cannot be determined reliably.
|
|
595
|
+
The position is omitted when it cannot be determined reliably. Micro-Manager's separate files and MetaMorph record where the stage was without saying which point of the field that is: their value is in `image.metadata.stage_position`, as recorded, not in `position`.
|
|
563
596
|
|
|
564
597
|
## Supported formats
|
|
565
598
|
|
|
@@ -575,13 +608,14 @@ The position is omitted when it cannot be determined reliably.
|
|
|
575
608
|
| **VSI** | Olympus / Evident cellSens and VS120 / VS200 slide scanners |
|
|
576
609
|
| **NDPIS** | Hamamatsu NanoZoomer fluorescence sets (one NDPI per channel) |
|
|
577
610
|
| **Imaris** | `.ims`, with the `[hdf5]` extra |
|
|
578
|
-
| **HDF5** | ilastik, BigDataViewer / BigStitcher, Luxendo
|
|
611
|
+
| **HDF5** | ilastik, BigDataViewer / BigStitcher, Luxendo experiments and `.lux.h5`, Fiji HDF5, with the `[hdf5]` extra |
|
|
579
612
|
| **ICS / IDS** | Image Cytometry Standard 1.0 and 2.0 (SVI Huygens and others) |
|
|
580
613
|
| **MetaMorph** | `.nd` acquisitions and their TIFF / STK files |
|
|
581
614
|
| **Micro-Manager 1.4**| image-per-file acquisitions (`metadata.txt` + one TIFF per plane) |
|
|
582
615
|
| **DeltaVision** | `.dv`, `.r3d` (softWoRx, OMX, Priism) |
|
|
583
616
|
| **MRC** | Common MRC modes and extended headers |
|
|
584
|
-
| **
|
|
617
|
+
| **KLB** | Every time point and channel, bzip2 and zlib blocks |
|
|
618
|
+
| **Plates (HCS)** | Harmony, Columbus exports, MetaXpress, ImageXpress HCS.ai (`.jdce`), IN Cell, CellVoyager, Opera Flex, scanR and Cellomics (`.C01`) plates; also (by default; `plates=False` reads them by series) CZI plates, OME companion and OME-TIFF plates (Yokogawa CQ1), ND2 files whose XY points are named after wells, and folders of ND2 files from one NIS-Elements well-plate run |
|
|
585
619
|
| **JPEG / PNG / BMP** | Common raster image formats |
|
|
586
620
|
|
|
587
621
|
All formats use the same `File`, `Image`, axes, indexing, metadata, and reading interface wherever possible.
|
|
@@ -645,8 +679,14 @@ f = micro_reader.open("data_t001.ome.tif", multifile=False)
|
|
|
645
679
|
|
|
646
680
|
If the companion is missing or does not list the file, opening the whole set raises an error that names `multifile=False`. With `multifile=False` the file is then read without pixel sizes, with a `CompanionWarning`. A TIFF whose metadata lives in another OME-TIFF (instead of a `.companion.ome`) works the same way, with that OME-TIFF in the companion's place.
|
|
647
681
|
|
|
682
|
+
A companion that describes a plate (`Plate` / `Well` / `WellSample`) reads as one, as a CZI plate does: `f.plates` lists its plates and wells, and its images are indexed by plate, acquisition, well and field. RGB images in a companion have a sample axis (`t c z y x s`), as RGB OME-TIFFs do.
|
|
683
|
+
|
|
648
684
|
Some OME-TIFF sets keep the full OME-XML in their TIFF files instead of a companion. Opening one of those files also reads the whole set, and `multifile=False` reads only the planes stored in that file, with the set's metadata.
|
|
649
685
|
|
|
686
|
+
Any file of a plate works the same way: opening one plane file opens the plate, and `multifile=False` reads that file alone. Files that belong to the instrument and not to the plate's data, such as CellVoyager shading and dark-current images, open as themselves.
|
|
687
|
+
|
|
688
|
+
Leica's settings files (`FrameProperties`, `IOManagerConfiguation`) open their experiment. A `.xml` beside a `.lif` opens the `.lif`, and so does a `.lifext`; `multifile=False` reads the `.lifext` itself.
|
|
689
|
+
|
|
650
690
|
Micro-Manager stacks always open whole, also with `multifile=False`: they split their files by size, so one file does not hold a complete part of the image.
|
|
651
691
|
|
|
652
692
|
### CZI
|
|
@@ -744,7 +784,7 @@ These need the `[hdf5]` extra as well.
|
|
|
744
784
|
|
|
745
785
|
* **ilastik**: every dataset with ilastik's `axistags` (its exports, its input files, steinbock's `img`) is one image, in the dataset's own axis order. Pixel sizes are read where ilastik recorded units (`axis_units`, since 2025-09).
|
|
746
786
|
* **BigDataViewer / BigStitcher**: the XML with its HDF5 file (also partitioned). One image per tile, angle and illumination, channels on `c` and time points on `t`, with the stored resolution levels. Opening the HDF5 file opens its XML.
|
|
747
|
-
* **Luxendo / Bruker light-sheet
|
|
787
|
+
* **Luxendo / Bruker light-sheet**: an experiment folder (`raw/` with one folder of `.lux.h5` stacks per channel) opens as one image per stack and camera, with all time points and channels. Opening any part opens the whole experiment: the folder, a `.lux.h5`, or the `imaris.ims` / `bdv.xml` next to `raw/`. Files those link files name but `raw/` lacks read as zeros, with a warning. `multifile=False` reads one `.lux.h5` (or `.ims`) alone.
|
|
748
788
|
* **Fiji HDF5** (the Vibez plugin): datasets with `element_size_um`; `t<n>/channel<n>` datasets make one `t c z y x` image.
|
|
749
789
|
|
|
750
790
|
### ICS / IDS
|
|
@@ -910,6 +950,14 @@ Applications work with the same `File` and `Image` model described in [How it wo
|
|
|
910
950
|
|
|
911
951
|
The goal is that an application can open a microscopy file, identify the image it needs, read only the required pixels, and obtain standardized metadata without needing to understand the vendor-specific file structure.
|
|
912
952
|
|
|
953
|
+
## Compatibility with eubi-bridge
|
|
954
|
+
|
|
955
|
+
micro-reader 0.1.0 renamed the image index `scene` to `series`. eubi-bridge 0.1.3 to 0.2.x and eubi-bridge-lite below 0.3.0 still read `scene`, so `import micro_reader` refuses to load next to them. It raises an `ImportError` that names the installed eubi version and the two ways out: upgrade eubi (`pip install -U 'eubi-bridge>=0.3.0'`, or the same for `eubi-bridge-lite`), or keep that eubi with the micro-reader it was built for (`pip install 'micro-reader<0.1'`). The check reads the versions of the installed packages; it never imports eubi. An editable install of eubi is checked like any other: its recorded version can be out of date, so the message then says to reinstall it.
|
|
956
|
+
|
|
957
|
+
To skip the check, set the environment variable `MICRO_READER_SKIP_EUBI_CHECK=1`. Only the value `1` skips it; any other value keeps the check. micro-reader then warns once that the check was skipped. This is meant for work on an eubi checkout, not for normal use.
|
|
958
|
+
|
|
959
|
+
The check will be removed when every eubi-bridge / eubi-bridge-lite line below 0.3.0 has left the support window, checked by the release version matrix; not before.
|
|
960
|
+
|
|
913
961
|
## License
|
|
914
962
|
|
|
915
963
|
MIT
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
**Memory-bounded pixel reader for microscopy images.**
|
|
4
4
|
|
|
5
|
-
`micro-reader` reads regions of large microscopy files without loading the entire image into memory. It provides a common interface across TIFF, CZI, ND2, Leica, OIR, OIB / OIF, Olympus VSI, Imaris, DeltaVision, MRC, common raster formats, OME companion file sets, and high-content screening plates (Harmony, MetaXpress, IN Cell, CellVoyager).
|
|
5
|
+
`micro-reader` reads regions of large microscopy files without loading the entire image into memory. It provides a common interface across TIFF, CZI, ND2, Leica, OIR, OIB / OIF, Olympus VSI, Imaris, DeltaVision, MRC, KLB, common raster formats, OME companion file sets, and high-content screening plates (Harmony, Columbus, MetaXpress, ImageXpress HCS.ai, IN Cell, CellVoyager, CQ1, Opera Flex, scanR, Cellomics).
|
|
6
6
|
|
|
7
|
-
> **Status:
|
|
7
|
+
> **Status: alpha (0.x)**
|
|
8
8
|
>
|
|
9
9
|
> The API may still change.
|
|
10
10
|
|
|
@@ -23,14 +23,6 @@ pip install "micro-reader[ims]" # Imaris (.ims)
|
|
|
23
23
|
pip install "micro-reader[codecs]" # additional codecs
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
## Compatibility with eubi-bridge
|
|
27
|
-
|
|
28
|
-
micro-reader 0.1.0 renamed the image index `scene` to `series`. eubi-bridge 0.1.3 to 0.2.x and eubi-bridge-lite below 0.3.0 still read `scene`, so `import micro_reader` refuses to load next to them. It raises an `ImportError` that names the installed eubi version and the two ways out: upgrade eubi (`pip install -U 'eubi-bridge>=0.3.0'`, or the same for `eubi-bridge-lite`), or keep that eubi with the micro-reader it was built for (`pip install 'micro-reader<0.1'`). The check reads the versions of the installed packages; it never imports eubi. An editable install of eubi is checked like any other: its recorded version can be out of date, so the message then says to reinstall it.
|
|
29
|
-
|
|
30
|
-
To skip the check, set the environment variable `MICRO_READER_SKIP_EUBI_CHECK=1`. Only the value `1` skips it; any other value keeps the check. micro-reader then warns once that the check was skipped. This is meant for work on an eubi checkout, not for normal use.
|
|
31
|
-
|
|
32
|
-
The check will be removed when every eubi-bridge / eubi-bridge-lite line below 0.3.0 has left the support window, checked by the release version matrix; not before.
|
|
33
|
-
|
|
34
26
|
## Quick start
|
|
35
27
|
|
|
36
28
|
```python
|
|
@@ -143,6 +135,17 @@ f.select(series=1, view=0)
|
|
|
143
135
|
|
|
144
136
|
This returns the images matching the requested indices. For example, if a file contains several series, tiles, and views, selecting `series=1, view=0` returns the tiles belonging to that series and view.
|
|
145
137
|
|
|
138
|
+
### Image names
|
|
139
|
+
|
|
140
|
+
`image.name` follows one rule for every format, and is the same whichever file of a dataset you open:
|
|
141
|
+
|
|
142
|
+
1. A plate's image is named by its well and field: `"well B2 field 0"` (the field counted from 0, as in `select(field=0)`; the plate's name in front when the file holds several plates).
|
|
143
|
+
2. Otherwise the name the format gives the image: a Leica element's name, an OME `Image Name`, an Imaris image name, a slide's associated image by its role in lower case (`label`, `macro`, `thumbnail`). A slide's main scan, which its format does not name, takes the name of its file.
|
|
144
|
+
3. Otherwise, in a file of several images, a label the file records for each one (a CZI scene name, an ND2 point name, a Micro-Manager or MetaMorph position label), or else the index values that tell them apart: `"series 2"`, `"series 1, tile 3"`.
|
|
145
|
+
4. Otherwise (one image) the name of the dataset's main file without its extension: the index file of a set, the main file of a multi-file format, a Luxendo experiment's folder.
|
|
146
|
+
|
|
147
|
+
Two images left with one name get the index values that differ between them appended, for example `"scan, series 2"`. Resolution levels carry their image's name.
|
|
148
|
+
|
|
146
149
|
### Available index dimensions
|
|
147
150
|
|
|
148
151
|
`micro-reader` uses these index names when a format provides the corresponding dimensions:
|
|
@@ -179,7 +182,7 @@ f.path # the main file: "cells_R3D.dv"
|
|
|
179
182
|
f.opened # what was opened: "cells_R3D.dv.log"
|
|
180
183
|
```
|
|
181
184
|
|
|
182
|
-
You can also open the folder. A folder opens the image it belongs to (an NDTiff dataset folder, a Harmony or CellVoyager measurement folder), or the one image it holds. A folder with several images raises an error that lists them.
|
|
185
|
+
You can also open the folder. A folder opens the image it belongs to (an NDTiff dataset folder, a Harmony or CellVoyager measurement folder, a folder of Opera `.flex` files, a Luxendo experiment), or the one image it holds. A folder that holds no image itself, such as a downloaded sample folder with the export one or two levels down, opens the one dataset found in its sub-folders. An Opera Flex plate has no index file: any of its `.flex` files opens the whole folder as the plate (`multifile=False` reads only that file). A folder of one NIS-Elements well-plate run opens as that plate (with `plates=False`, its files are several images). A folder with several images or datasets raises an error that lists them; an OME-Zarr store is refused with a pointer to `ome_zarr_pyramid`, which reads it.
|
|
183
186
|
|
|
184
187
|
To sort many files at once, for example everything under a folder you want to convert, `group_files` says which files make one image:
|
|
185
188
|
|
|
@@ -192,7 +195,7 @@ for group in g.groups:
|
|
|
192
195
|
g.singles # every other file, as given
|
|
193
196
|
```
|
|
194
197
|
|
|
195
|
-
A file that a plate or companion index names is listed only in that index's group. When the main file of a multi-file image is missing, its group has `main=None`.
|
|
198
|
+
A file that a plate or companion index names is listed only in that index's group. When the main file of a multi-file image is missing, its group has `main=None`. It groups as `open()` reads, plates included (`plates=True`, the default): a folder of ND2 files from one NIS-Elements well-plate run is one group; `plates=False` groups as `open(..., plates=False)` reads.
|
|
196
199
|
|
|
197
200
|
## Axes
|
|
198
201
|
|
|
@@ -220,37 +223,47 @@ image.shape
|
|
|
220
223
|
# (1, 3, 40, 5120, 6144)
|
|
221
224
|
```
|
|
222
225
|
|
|
223
|
-
Other dimensions, such as illumination,
|
|
226
|
+
Other dimensions, such as illumination, excitation, tile, etc., are represented as image indices rather than array axes, as discussed above.
|
|
224
227
|
|
|
225
|
-
###
|
|
228
|
+
### Lambda scans
|
|
226
229
|
|
|
227
|
-
|
|
230
|
+
A lambda (spectral) scan is one image in every format. Its bands lie along `c`, a detector's bands next to each other in wavelength order, and each channel has its `emission_wavelength` and its wavelength in its name (`"HyD S 1 420 nm"`).
|
|
228
231
|
|
|
229
232
|
```python
|
|
230
|
-
|
|
233
|
+
f = micro_reader.open("scan.lif")
|
|
234
|
+
f.images[0].axes # 'tczyx'
|
|
235
|
+
[c.emission_wavelength for c in f.images[0].metadata.channels]
|
|
236
|
+
|
|
237
|
+
f = micro_reader.open("scan.lif", axes="native")
|
|
238
|
+
f.images[0].axes # 'tlczyx': a real lambda axis
|
|
239
|
+
f.images[0].metadata.index_values["emission"] # its wavelengths
|
|
231
240
|
```
|
|
232
241
|
|
|
233
|
-
|
|
242
|
+
Asking for axes that name `l`, such as `axes="tlczyx"`, also gives the axis. An LSM stores its bands as channels, so it has no `l` axis; its channels carry their wavelengths. Excitation scans stay an image index.
|
|
243
|
+
|
|
244
|
+
`open` gives every image in this canonical order by default, whatever order the file stores: axes the file lacks are added with size 1 (a 2-D TIFF is `(1, 1, 1, Y, X)`) and the others are reordered. Reading costs the same either way.
|
|
245
|
+
|
|
246
|
+
### Changing axis order
|
|
247
|
+
|
|
248
|
+
To keep the order the file stores (ICS `ctzyx`, Micro-Manager OME-TIFF `tzcyx`, a 2-D TIFF `yx`):
|
|
234
249
|
|
|
235
250
|
```python
|
|
236
|
-
f = micro_reader.open("image.
|
|
251
|
+
f = micro_reader.open("image.ics", axes="native")
|
|
237
252
|
```
|
|
238
253
|
|
|
239
|
-
You can also request
|
|
254
|
+
You can also request a specific axis order:
|
|
240
255
|
|
|
241
256
|
```python
|
|
242
|
-
|
|
257
|
+
image = f.images[0].as_axes("tczyx")
|
|
243
258
|
```
|
|
244
259
|
|
|
245
|
-
|
|
260
|
+
or when opening the file:
|
|
246
261
|
|
|
247
|
-
```
|
|
248
|
-
|
|
262
|
+
```python
|
|
263
|
+
f = micro_reader.open("image.tif", axes="zyx")
|
|
249
264
|
```
|
|
250
265
|
|
|
251
|
-
followed by `s` when RGB samples are
|
|
252
|
-
|
|
253
|
-
Missing axes are added with size 1 and existing axes are reordered.
|
|
266
|
+
`axes="canonical"` (the default) is `t c z y x`, followed by `s` when RGB samples are kept (`samples="keep"`).
|
|
254
267
|
|
|
255
268
|
### Removing singleton axes
|
|
256
269
|
|
|
@@ -270,18 +283,18 @@ Only axes with size 1 can be removed. `y` and `x` are never removed.
|
|
|
270
283
|
|
|
271
284
|
### RGB samples
|
|
272
285
|
|
|
273
|
-
|
|
286
|
+
`micro_reader.open` turns RGB samples into channels: an RGB image has 3 channels, named "<name> R", "G" and "B" and coloured red, green and blue (a file without a channel name gives three unnamed channels in those colours). An image that has channels already gets c × s of them: "<name> R" ... for each.
|
|
274
287
|
|
|
275
|
-
|
|
288
|
+
To keep RGB samples as their own `s` axis, the way the file stores them:
|
|
276
289
|
|
|
277
290
|
```python
|
|
278
291
|
f = micro_reader.open(
|
|
279
292
|
"image.tif",
|
|
280
|
-
samples="
|
|
293
|
+
samples="keep",
|
|
281
294
|
)
|
|
282
295
|
```
|
|
283
296
|
|
|
284
|
-
|
|
297
|
+
`image.source` is then not needed: with the default, it is the image as stored, with its `s` axis. `image.as_axes(...)` keeps `s` unless asked for `samples="channels"`.
|
|
285
298
|
|
|
286
299
|
|
|
287
300
|
## Ambiguous dimensions
|
|
@@ -304,8 +317,8 @@ print(len(f.images))
|
|
|
304
317
|
print(f.images[0].index)
|
|
305
318
|
# {"series": 0, "sequence": 0}
|
|
306
319
|
|
|
307
|
-
print(f.images[0].axes)
|
|
308
|
-
# yx
|
|
320
|
+
print(f.images[0].axes, f.images[0].shape)
|
|
321
|
+
# tczyx (1, 1, 1, 16, 16) (axes="canonical", the default; axes=None gives yx)
|
|
309
322
|
```
|
|
310
323
|
|
|
311
324
|
If you know that the pages represent `z`, you can tell the reader:
|
|
@@ -317,11 +330,11 @@ f = micro_reader.open(
|
|
|
317
330
|
)
|
|
318
331
|
```
|
|
319
332
|
|
|
320
|
-
The result is a single image with `
|
|
333
|
+
The result is a single image with the 80 pages along `z`.
|
|
321
334
|
|
|
322
335
|
```python
|
|
323
|
-
print(f.images[0].axes)
|
|
324
|
-
# zyx
|
|
336
|
+
print(f.images[0].axes, f.images[0].shape)
|
|
337
|
+
# tczyx (1, 1, 80, 16, 16) (axes=None gives zyx)
|
|
325
338
|
```
|
|
326
339
|
|
|
327
340
|
|
|
@@ -409,6 +422,8 @@ f = micro_reader.open(
|
|
|
409
422
|
|
|
410
423
|
CZI tiles are placed by the file's own stitching information, FluoView tiles by the tiling set's grid (where tiles overlap, the later tile is on top; areas without a tile read as 0). Other formats with tiles raise an error for `tiles="mosaic"`.
|
|
411
424
|
|
|
425
|
+
A format that has tiles it cannot place refuses `tiles="mosaic"` with an error. A file without tiles has nothing to place and opens as usual: Micro-Manager and MetaMorph stage positions are series, not tiles.
|
|
426
|
+
|
|
412
427
|
`tiles="stitched"`, the name before 0.1.0, still works and warns.
|
|
413
428
|
|
|
414
429
|
## Plates
|
|
@@ -421,21 +436,38 @@ The plate layout is available through:
|
|
|
421
436
|
plate = f.plates[0]
|
|
422
437
|
plate.wells[0].name
|
|
423
438
|
# "B2"
|
|
439
|
+
plate.row_names, plate.column_names # ('A', 'B', ...), ('1', '2', ...)
|
|
440
|
+
part = plate.rows[2:5].columns[3:7] # a part of the plate: still a plate
|
|
424
441
|
```
|
|
425
442
|
|
|
443
|
+
Every level knows what it is and what holds it: `.kind` is `"file"`, `"plate"`, `"well"`
|
|
444
|
+
or `"field"`, and `.parent` is the level above. Accessors go down only: a file holds
|
|
445
|
+
`.plates`, a plate `.wells`, a well `.fields`, a field `.images`. `plate.rows` and
|
|
446
|
+
`plate.columns` slice a plate by position, name or a list of them, as `f.select(row=...,
|
|
447
|
+
column=...)` does; rows and columns are a well's address, not a level.
|
|
448
|
+
|
|
426
449
|
Images can be reached through the wells, or selected by well:
|
|
427
450
|
|
|
428
451
|
```python
|
|
429
452
|
image = plate.wells[0].fields[0].image
|
|
430
|
-
f.select(well=plate.well_index("B2"))
|
|
453
|
+
f.select(well=plate.well_index("B2")) # by its place in plate.wells
|
|
454
|
+
f.select(well_name="B2") # by name
|
|
455
|
+
f.select(row_name="B", column=range(1, 4)) # rows and columns: names, or 0-based numbers
|
|
456
|
+
f.select(well=["B2", "C5"], field=[0, 1]) # a list, tuple, range or set: any of them
|
|
457
|
+
f.select(row=slice(2, None)) # a slice of positions: row C onward
|
|
431
458
|
```
|
|
432
459
|
|
|
460
|
+
A plate's places have a position and a name, a keyword each: `plate` / `plate_name`,
|
|
461
|
+
`acquisition` / `acquisition_name`, `well` / `well_name`, `row` / `row_name`, `column` /
|
|
462
|
+
`column_name` (the columns of ome-zarr-pyramid's table). A name given to a position keyword
|
|
463
|
+
is its name (`well="B2"` is `well_name="B2"`).
|
|
464
|
+
|
|
433
465
|
`f.outline()` shows the plates, wells and fields of a file.
|
|
434
466
|
|
|
435
|
-
|
|
467
|
+
A plate in a generic format (a CZI, an ND2, an OME-TIFF or a companion with wells) is read as a plate too. `plates=False` reads it by its series instead:
|
|
436
468
|
|
|
437
469
|
```python
|
|
438
|
-
f = micro_reader.open("plate.czi"
|
|
470
|
+
f = micro_reader.open("plate.czi")
|
|
439
471
|
```
|
|
440
472
|
|
|
441
473
|
Missing plane files are read as zeros, with a `MissingImagesWarning`. A field without any of its files is left out.
|
|
@@ -494,6 +526,7 @@ meta.channels[0]
|
|
|
494
526
|
`micro-reader` follows a few important rules:
|
|
495
527
|
|
|
496
528
|
* Units use OME-Zarr-compatible names such as `micrometer`, `nanometer`, and `millisecond`.
|
|
529
|
+
* Each value keeps the unit the file records (lengths above a millimetre become micrometres), so two formats can give the same quantity in different units, for example a time step in milliseconds (ND2, OIR) or in seconds (CZI, LIF). Convert to compare: `image.metadata.pixel_sizes["t"].to("s")`, `.to("um")`. Converting a length to a time, or to an unknown unit, raises `ValueError`.
|
|
497
530
|
* Missing metadata is left as `None` or absent.
|
|
498
531
|
* Pixel sizes are never invented as `1.0`.
|
|
499
532
|
* Colours are not invented when the file does not provide them.
|
|
@@ -505,7 +538,7 @@ meta.channels[0]
|
|
|
505
538
|
|
|
506
539
|
Where possible, format-specific coordinate conventions are converted to this common representation.
|
|
507
540
|
|
|
508
|
-
The position is omitted when it cannot be determined reliably.
|
|
541
|
+
The position is omitted when it cannot be determined reliably. Micro-Manager's separate files and MetaMorph record where the stage was without saying which point of the field that is: their value is in `image.metadata.stage_position`, as recorded, not in `position`.
|
|
509
542
|
|
|
510
543
|
## Supported formats
|
|
511
544
|
|
|
@@ -521,13 +554,14 @@ The position is omitted when it cannot be determined reliably.
|
|
|
521
554
|
| **VSI** | Olympus / Evident cellSens and VS120 / VS200 slide scanners |
|
|
522
555
|
| **NDPIS** | Hamamatsu NanoZoomer fluorescence sets (one NDPI per channel) |
|
|
523
556
|
| **Imaris** | `.ims`, with the `[hdf5]` extra |
|
|
524
|
-
| **HDF5** | ilastik, BigDataViewer / BigStitcher, Luxendo
|
|
557
|
+
| **HDF5** | ilastik, BigDataViewer / BigStitcher, Luxendo experiments and `.lux.h5`, Fiji HDF5, with the `[hdf5]` extra |
|
|
525
558
|
| **ICS / IDS** | Image Cytometry Standard 1.0 and 2.0 (SVI Huygens and others) |
|
|
526
559
|
| **MetaMorph** | `.nd` acquisitions and their TIFF / STK files |
|
|
527
560
|
| **Micro-Manager 1.4**| image-per-file acquisitions (`metadata.txt` + one TIFF per plane) |
|
|
528
561
|
| **DeltaVision** | `.dv`, `.r3d` (softWoRx, OMX, Priism) |
|
|
529
562
|
| **MRC** | Common MRC modes and extended headers |
|
|
530
|
-
| **
|
|
563
|
+
| **KLB** | Every time point and channel, bzip2 and zlib blocks |
|
|
564
|
+
| **Plates (HCS)** | Harmony, Columbus exports, MetaXpress, ImageXpress HCS.ai (`.jdce`), IN Cell, CellVoyager, Opera Flex, scanR and Cellomics (`.C01`) plates; also (by default; `plates=False` reads them by series) CZI plates, OME companion and OME-TIFF plates (Yokogawa CQ1), ND2 files whose XY points are named after wells, and folders of ND2 files from one NIS-Elements well-plate run |
|
|
531
565
|
| **JPEG / PNG / BMP** | Common raster image formats |
|
|
532
566
|
|
|
533
567
|
All formats use the same `File`, `Image`, axes, indexing, metadata, and reading interface wherever possible.
|
|
@@ -591,8 +625,14 @@ f = micro_reader.open("data_t001.ome.tif", multifile=False)
|
|
|
591
625
|
|
|
592
626
|
If the companion is missing or does not list the file, opening the whole set raises an error that names `multifile=False`. With `multifile=False` the file is then read without pixel sizes, with a `CompanionWarning`. A TIFF whose metadata lives in another OME-TIFF (instead of a `.companion.ome`) works the same way, with that OME-TIFF in the companion's place.
|
|
593
627
|
|
|
628
|
+
A companion that describes a plate (`Plate` / `Well` / `WellSample`) reads as one, as a CZI plate does: `f.plates` lists its plates and wells, and its images are indexed by plate, acquisition, well and field. RGB images in a companion have a sample axis (`t c z y x s`), as RGB OME-TIFFs do.
|
|
629
|
+
|
|
594
630
|
Some OME-TIFF sets keep the full OME-XML in their TIFF files instead of a companion. Opening one of those files also reads the whole set, and `multifile=False` reads only the planes stored in that file, with the set's metadata.
|
|
595
631
|
|
|
632
|
+
Any file of a plate works the same way: opening one plane file opens the plate, and `multifile=False` reads that file alone. Files that belong to the instrument and not to the plate's data, such as CellVoyager shading and dark-current images, open as themselves.
|
|
633
|
+
|
|
634
|
+
Leica's settings files (`FrameProperties`, `IOManagerConfiguation`) open their experiment. A `.xml` beside a `.lif` opens the `.lif`, and so does a `.lifext`; `multifile=False` reads the `.lifext` itself.
|
|
635
|
+
|
|
596
636
|
Micro-Manager stacks always open whole, also with `multifile=False`: they split their files by size, so one file does not hold a complete part of the image.
|
|
597
637
|
|
|
598
638
|
### CZI
|
|
@@ -690,7 +730,7 @@ These need the `[hdf5]` extra as well.
|
|
|
690
730
|
|
|
691
731
|
* **ilastik**: every dataset with ilastik's `axistags` (its exports, its input files, steinbock's `img`) is one image, in the dataset's own axis order. Pixel sizes are read where ilastik recorded units (`axis_units`, since 2025-09).
|
|
692
732
|
* **BigDataViewer / BigStitcher**: the XML with its HDF5 file (also partitioned). One image per tile, angle and illumination, channels on `c` and time points on `t`, with the stored resolution levels. Opening the HDF5 file opens its XML.
|
|
693
|
-
* **Luxendo / Bruker light-sheet
|
|
733
|
+
* **Luxendo / Bruker light-sheet**: an experiment folder (`raw/` with one folder of `.lux.h5` stacks per channel) opens as one image per stack and camera, with all time points and channels. Opening any part opens the whole experiment: the folder, a `.lux.h5`, or the `imaris.ims` / `bdv.xml` next to `raw/`. Files those link files name but `raw/` lacks read as zeros, with a warning. `multifile=False` reads one `.lux.h5` (or `.ims`) alone.
|
|
694
734
|
* **Fiji HDF5** (the Vibez plugin): datasets with `element_size_um`; `t<n>/channel<n>` datasets make one `t c z y x` image.
|
|
695
735
|
|
|
696
736
|
### ICS / IDS
|
|
@@ -856,6 +896,14 @@ Applications work with the same `File` and `Image` model described in [How it wo
|
|
|
856
896
|
|
|
857
897
|
The goal is that an application can open a microscopy file, identify the image it needs, read only the required pixels, and obtain standardized metadata without needing to understand the vendor-specific file structure.
|
|
858
898
|
|
|
899
|
+
## Compatibility with eubi-bridge
|
|
900
|
+
|
|
901
|
+
micro-reader 0.1.0 renamed the image index `scene` to `series`. eubi-bridge 0.1.3 to 0.2.x and eubi-bridge-lite below 0.3.0 still read `scene`, so `import micro_reader` refuses to load next to them. It raises an `ImportError` that names the installed eubi version and the two ways out: upgrade eubi (`pip install -U 'eubi-bridge>=0.3.0'`, or the same for `eubi-bridge-lite`), or keep that eubi with the micro-reader it was built for (`pip install 'micro-reader<0.1'`). The check reads the versions of the installed packages; it never imports eubi. An editable install of eubi is checked like any other: its recorded version can be out of date, so the message then says to reinstall it.
|
|
902
|
+
|
|
903
|
+
To skip the check, set the environment variable `MICRO_READER_SKIP_EUBI_CHECK=1`. Only the value `1` skips it; any other value keeps the check. micro-reader then warns once that the check was skipped. This is meant for work on an eubi checkout, not for normal use.
|
|
904
|
+
|
|
905
|
+
The check will be removed when every eubi-bridge / eubi-bridge-lite line below 0.3.0 has left the support window, checked by the release version matrix; not before.
|
|
906
|
+
|
|
859
907
|
## License
|
|
860
908
|
|
|
861
909
|
MIT
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "micro-reader"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.4.0"
|
|
10
10
|
description = "Low-level, memory-bounded pixel reading for microscopy formats: read exactly the rectangle you ask for."
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.11"
|
|
@@ -16,7 +16,7 @@ authors = [{ name = "Bugra Oezdemir", email = "bugraa.ozdemir@gmail.com" }]
|
|
|
16
16
|
keywords = ["microscopy", "bioimage", "image-io", "memory-bounded", "tiff", "ome-tiff", "czi", "nd2",
|
|
17
17
|
"lif", "leica", "oir", "imaris", "ims", "mrc", "lsm", "eer", "cryo-em"]
|
|
18
18
|
classifiers = [
|
|
19
|
-
"Development Status ::
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
20
|
"Intended Audience :: Science/Research",
|
|
21
21
|
"Programming Language :: Python :: 3",
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|