micro-reader 0.1.0__tar.gz → 0.2.1__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.1.0 → micro_reader-0.2.1}/PKG-INFO +1 -1
- {micro_reader-0.1.0 → micro_reader-0.2.1}/pyproject.toml +1 -1
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/__init__.py +7 -4
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_base.py +73 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_errors.py +11 -2
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_metadata.py +15 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_plate.py +21 -0
- micro_reader-0.2.1/src/micro_reader/_spatial.py +517 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_stack.py +1 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_version.py +1 -1
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_czi_metadata.py +10 -1
- micro_reader-0.2.1/src/micro_reader/backends/_lif_metadata.py +307 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_nd2_metadata.py +60 -23
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_tiff_metadata.py +89 -15
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/bdv.py +323 -46
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/companion.py +69 -4
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/cv7000.py +116 -42
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/czi.py +26 -7
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/harmony.py +57 -18
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/ims.py +42 -10
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/lif.py +98 -11
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/metamorph.py +19 -6
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/metaxpress.py +77 -39
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/micromanager.py +50 -13
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/nd2.py +7 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/ndpis.py +2 -1
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/oif.py +59 -15
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/omp2info.py +35 -14
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/tiff.py +21 -1
- micro_reader-0.1.0/src/micro_reader/backends/_lif_metadata.py +0 -131
- {micro_reader-0.1.0 → micro_reader-0.2.1}/.gitignore +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/LICENSE +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/README.md +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_axes.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_cfb.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_codecs.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_eubi_guard.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_fileset.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_groups.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_index.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_io.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_jpeg_restarts.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_memory.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_mosaic.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_outline.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_parts.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_pool.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_registry.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/_stream.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/__init__.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_ets.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_hdf5.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_nd2_legacy.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_nd2_structure.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_tiff_variants.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/_vsi_tree.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/dv.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/hdf5.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/ics.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/ilastik.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/incell.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/mrc.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/oir.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/raster.py +0 -0
- {micro_reader-0.1.0 → micro_reader-0.2.1}/src/micro_reader/backends/vsi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: micro-reader
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "micro-reader"
|
|
9
|
-
version = "0.1
|
|
9
|
+
version = "0.2.1"
|
|
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"
|
|
@@ -10,9 +10,10 @@ from ._version import __version__
|
|
|
10
10
|
from ._base import INDEX_NAMES, PLATE_INDICES, SAMPLED_INDICES, File, Image, ReadStats
|
|
11
11
|
from ._metadata import Channel, Metadata, Quantity
|
|
12
12
|
from ._plate import Acquisition, Field, Plate, Well
|
|
13
|
-
from .
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
from ._spatial import Axis, Collection, CoordinateSystem, Transformation
|
|
14
|
+
from ._errors import (CompanionWarning, CompositeWarning, CorruptFileError, InvariantError,
|
|
15
|
+
MicroReaderError, MissingImagesWarning, PlateLayoutWarning,
|
|
16
|
+
SkewedGridWarning, UnsupportedFormatError)
|
|
16
17
|
from ._memory import MemoryLimitError, memory_limit, set_memory_limit
|
|
17
18
|
from ._io import cache_limit, cached_bytes, set_cache_limit
|
|
18
19
|
from ._pool import decode_threads, set_decode_threads
|
|
@@ -23,9 +24,11 @@ from .backends.czi import DamagedFileWarning
|
|
|
23
24
|
__all__ = [
|
|
24
25
|
"open", "group_files", "FileGroup", "Grouping", "File", "Image", "INDEX_NAMES", "PLATE_INDICES", "SAMPLED_INDICES", "ReadStats",
|
|
25
26
|
"BACKENDS", "Metadata", "Channel", "Quantity", "Plate", "Well", "Field", "Acquisition",
|
|
27
|
+
"Axis", "CoordinateSystem", "Transformation", "Collection",
|
|
26
28
|
"decode_threads", "set_decode_threads", "memory_limit", "set_memory_limit",
|
|
27
29
|
"cache_limit", "set_cache_limit", "cached_bytes",
|
|
28
|
-
"MicroReaderError", "UnsupportedFormatError", "CorruptFileError", "
|
|
30
|
+
"MicroReaderError", "UnsupportedFormatError", "CorruptFileError", "InvariantError",
|
|
31
|
+
"MemoryLimitError", "DamagedFileWarning",
|
|
29
32
|
"MissingImagesWarning", "CompanionWarning", "CompositeWarning", "PlateLayoutWarning",
|
|
30
33
|
"SkewedGridWarning", "__version__",
|
|
31
34
|
]
|
|
@@ -367,6 +367,10 @@ class File:
|
|
|
367
367
|
backend's own class is an implementation detail."""
|
|
368
368
|
|
|
369
369
|
backend: str = "base"
|
|
370
|
+
#: (coordinate_systems, coordinate_transformations) once declared and checked
|
|
371
|
+
_graph: Optional[tuple] = None
|
|
372
|
+
#: (the images it was built from, collection) once built (``collection``)
|
|
373
|
+
_tree: Optional[tuple] = None
|
|
370
374
|
|
|
371
375
|
def __init__(self, path: str, images: Sequence[Image], plates: Sequence = ()):
|
|
372
376
|
#: the file read: for a composite format its main file (docs/COMPOSITE.md)
|
|
@@ -453,6 +457,75 @@ class File:
|
|
|
453
457
|
out.append(image)
|
|
454
458
|
return out
|
|
455
459
|
|
|
460
|
+
# The spatial model (plan section 3; _spatial.py): the shared coordinate
|
|
461
|
+
# systems the file's positions are in, the other edges between them, and
|
|
462
|
+
# the tree of what contains what. A backend declares the systems from
|
|
463
|
+
# what opening the file already read; an image names its system beside
|
|
464
|
+
# its position (``metadata.coordinate_system``).
|
|
465
|
+
|
|
466
|
+
def _declare_coordinate_systems(self) -> Sequence:
|
|
467
|
+
"""The shared coordinate systems this file's positions are in (plan
|
|
468
|
+
4.2), declared from what opening the file already read -- the
|
|
469
|
+
indices, the plates, the file's own header -- never from an image's
|
|
470
|
+
``metadata`` (no ``_read_metadata``: ``coordinate_systems`` must stay
|
|
471
|
+
as cheap as opening). Backends override it; the default declares
|
|
472
|
+
none. ``(CoordinateSystem(system_id("stage")),)`` is the usual
|
|
473
|
+
answer; one per well (``system_id("well", well.name)``, 4.9) or per
|
|
474
|
+
series (``system_id("stage", f"s{n}")``, 4.15) where the plan says so;
|
|
475
|
+
``axes=None`` (the writer fills them, 3.12). Called once, on the first
|
|
476
|
+
access of ``coordinate_systems``; the result is checked (I1)."""
|
|
477
|
+
return ()
|
|
478
|
+
|
|
479
|
+
def _declare_coordinate_transformations(self) -> Sequence:
|
|
480
|
+
"""The edges of the graph other than positions (plan 3.3): only
|
|
481
|
+
BigDataViewer has some (affine views and registrations, 9.16). Each a
|
|
482
|
+
``Transformation`` between declared systems and image keys (I5); the
|
|
483
|
+
same rules as ``_declare_coordinate_systems``."""
|
|
484
|
+
return ()
|
|
485
|
+
|
|
486
|
+
def _declared(self) -> tuple:
|
|
487
|
+
if self._graph is None:
|
|
488
|
+
from ._spatial import check_declared_systems, check_edges, image_key
|
|
489
|
+
systems = check_declared_systems(self._declare_coordinate_systems(), where=self.path)
|
|
490
|
+
edges = self._declare_coordinate_transformations()
|
|
491
|
+
keys = {image_key(image) for image in self.images} if edges else ()
|
|
492
|
+
self._graph = (systems, check_edges(edges, systems, keys, where=self.path))
|
|
493
|
+
return self._graph
|
|
494
|
+
|
|
495
|
+
@property
|
|
496
|
+
def coordinate_systems(self) -> tuple:
|
|
497
|
+
"""The shared coordinate systems the backend declares for this file
|
|
498
|
+
(a tuple of ``CoordinateSystem``, plan 3.3, 4.2): the systems its
|
|
499
|
+
images' positions are in, named by ``metadata.coordinate_system``.
|
|
500
|
+
Empty for a backend that declares none. Declared when first read,
|
|
501
|
+
without reading any image's metadata."""
|
|
502
|
+
return self._declared()[0]
|
|
503
|
+
|
|
504
|
+
@property
|
|
505
|
+
def coordinate_transformations(self) -> tuple:
|
|
506
|
+
"""The edges of the file's transformation graph other than positions
|
|
507
|
+
(a tuple of ``Transformation``, plan 3.3): empty for every backend
|
|
508
|
+
but BigDataViewer, whose affine views and registrations are edges
|
|
509
|
+
from an image into ``global`` and ``global-registered`` (9.16)."""
|
|
510
|
+
return self._declared()[1]
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
def collection(self):
|
|
514
|
+
"""The tree of what contains what (a ``Collection``, plan 3.1, 3.17):
|
|
515
|
+
a root named after the file's stem holding plate > well > field, or
|
|
516
|
+
series, each holding its full-resolution images. A field or series
|
|
517
|
+
of one image collapses into it; tiles and the orthogonal indices
|
|
518
|
+
(view, illumination, ...) make no collection. The leaves are the
|
|
519
|
+
objects of ``images`` (after ``open(axes=...)`` too), and the tree
|
|
520
|
+
is built from the indices and ``plates`` alone: no metadata is read.
|
|
521
|
+
Built on first access, again when ``images`` has changed
|
|
522
|
+
(``_spatial.file_collection``)."""
|
|
523
|
+
images = tuple(self.images)
|
|
524
|
+
if self._tree is None or self._tree[0] != images: # identity: Image has no __eq__
|
|
525
|
+
from ._spatial import file_collection
|
|
526
|
+
self._tree = (images, file_collection(self))
|
|
527
|
+
return self._tree[1]
|
|
528
|
+
|
|
456
529
|
def _member_files(self) -> set:
|
|
457
530
|
"""The other files this file's images are read from -- a file set's
|
|
458
531
|
members -- for ``group_files``. Empty for a file that reads only itself."""
|
|
@@ -14,6 +14,14 @@ class CorruptFileError(MicroReaderError):
|
|
|
14
14
|
"""The file is recognised but inconsistent, e.g. truncated."""
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
class InvariantError(MicroReaderError):
|
|
18
|
+
"""The spatial model's invariants (plan 3.22) do not hold for a file: a
|
|
19
|
+
coordinate system id that is not one micro-reader makes or not unique
|
|
20
|
+
(I1), a position in a system the file does not declare or an estimated
|
|
21
|
+
one (I2), a position and a coordinate system given without each other
|
|
22
|
+
(I3), or an edge whose end is neither (I5). A backend bug."""
|
|
23
|
+
|
|
24
|
+
|
|
17
25
|
class OpenInstead(Exception):
|
|
18
26
|
"""Internal, not an error: a backend found that *path* is a member of a set
|
|
19
27
|
and that the set's metadata file (``.path``) is what opens it (``open``'s
|
|
@@ -37,8 +45,9 @@ class CompositeWarning(UserWarning):
|
|
|
37
45
|
|
|
38
46
|
class CompanionWarning(UserWarning):
|
|
39
47
|
"""An OME companion set's metadata came from somewhere other than the file
|
|
40
|
-
opened,
|
|
41
|
-
|
|
48
|
+
opened, a member path had to be resolved differently than written, or the
|
|
49
|
+
companion's bytes had to be read in another encoding than its XML
|
|
50
|
+
declares (docs/COMPANION.md)."""
|
|
42
51
|
|
|
43
52
|
|
|
44
53
|
class SkewedGridWarning(UserWarning):
|
|
@@ -146,6 +146,10 @@ class Metadata:
|
|
|
146
146
|
is absent / None."""
|
|
147
147
|
pixel_sizes: dict = field(default_factory=dict) # axis -> Quantity (x y z t)
|
|
148
148
|
position: dict = field(default_factory=dict) # axis -> Quantity
|
|
149
|
+
#: the id of the shared coordinate system ``position`` is in: one of
|
|
150
|
+
#: ``File.coordinate_systems`` (nominal, ``estimated=False``), set exactly
|
|
151
|
+
#: when the position is not empty (plan I3); None for an unplaced image
|
|
152
|
+
coordinate_system: Optional[str] = None
|
|
149
153
|
channels: tuple = () # Channel per index of c
|
|
150
154
|
#: this image's value of its indices that have one (D5): {"emission":
|
|
151
155
|
#: Quantity(510.0, "nanometer"), "lifetime": Quantity(2.5, "nanosecond")}
|
|
@@ -156,6 +160,17 @@ class Metadata:
|
|
|
156
160
|
#: a ``position``: axis -> Quantity (Bugra, 2026-10-19; MetaMorph)
|
|
157
161
|
stage_position: dict = field(default_factory=dict)
|
|
158
162
|
|
|
163
|
+
def __post_init__(self):
|
|
164
|
+
if self.coordinate_system is not None and not self.position:
|
|
165
|
+
raise ValueError(f"Metadata: coordinate_system={self.coordinate_system!r} with an "
|
|
166
|
+
"empty position: an image is placed exactly when it has both "
|
|
167
|
+
"(plan I3); give the position, or coordinate_system=None")
|
|
168
|
+
|
|
169
|
+
def unplaced(self) -> "Metadata":
|
|
170
|
+
"""This metadata without a position: ``position`` empty and
|
|
171
|
+
``coordinate_system`` None, the rest kept."""
|
|
172
|
+
return replace(self, position={}, coordinate_system=None)
|
|
173
|
+
|
|
159
174
|
def scaled(self, factors: dict) -> "Metadata":
|
|
160
175
|
"""This metadata for a resolution level whose pixels are *factors*
|
|
161
176
|
(axis -> float) times larger than level 0's."""
|
|
@@ -57,6 +57,27 @@ def layout(rows: int, columns: int, wells, where: str) -> tuple:
|
|
|
57
57
|
return tuple(row_label(r) for r in range(rows)), tuple(str(c + 1) for c in range(columns))
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
def well_system(plates, plate: int, well: int) -> str:
|
|
61
|
+
"""The id of the coordinate system of well *well* of plate *plate*
|
|
62
|
+
(indices into *plates* and its ``wells``), for backends whose positions
|
|
63
|
+
are relative to the well's centre (plan 4.9; ids 3.23): ``well-B2``, or
|
|
64
|
+
``well-p1-B2`` when *plates* holds several (``p`` + the plate index)."""
|
|
65
|
+
from ._spatial import system_id
|
|
66
|
+
name = plates[plate].wells[well].name
|
|
67
|
+
return system_id("well", *((f"p{plate}", name) if len(plates) > 1 else (name,)))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def well_systems(plates) -> tuple:
|
|
71
|
+
"""One ``CoordinateSystem`` per well with images of *plates*, plate by
|
|
72
|
+
plate in well order (``well_system``'s ids; names "well B2", or "plate 1
|
|
73
|
+
well B2" with several plates): what a backend of plan 4.9 declares."""
|
|
74
|
+
from ._spatial import CoordinateSystem
|
|
75
|
+
several = len(plates) > 1
|
|
76
|
+
return tuple(CoordinateSystem(well_system(plates, p, w),
|
|
77
|
+
f"{f'plate {p} ' if several else ''}well {well.name}")
|
|
78
|
+
for p, plate in enumerate(plates) for w, well in enumerate(plate.wells))
|
|
79
|
+
|
|
80
|
+
|
|
60
81
|
@dataclass(frozen=True, eq=False)
|
|
61
82
|
class Field:
|
|
62
83
|
"""One field of a well: its field index and its images (full resolution,
|