ngio 0.2.1__py3-none-any.whl → 0.2.3__py3-none-any.whl
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.
- ngio/__init__.py +20 -2
- ngio/common/_pyramid.py +5 -1
- ngio/common/_roi.py +2 -2
- ngio/hcs/__init__.py +16 -2
- ngio/hcs/plate.py +496 -18
- ngio/images/abstract_image.py +11 -0
- ngio/images/create.py +25 -36
- ngio/images/image.py +80 -6
- ngio/images/label.py +38 -9
- ngio/images/ome_zarr_container.py +70 -33
- ngio/ome_zarr_meta/__init__.py +5 -3
- ngio/ome_zarr_meta/ngio_specs/__init__.py +10 -2
- ngio/ome_zarr_meta/ngio_specs/_axes.py +90 -65
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +46 -8
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +242 -70
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +49 -11
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +28 -11
- ngio/ome_zarr_meta/v04/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +2 -2
- ngio/tables/_validators.py +1 -83
- ngio/tables/backends/__init__.py +27 -1
- ngio/tables/backends/_abstract_backend.py +207 -22
- ngio/tables/backends/_anndata_utils.py +3 -109
- ngio/tables/backends/_anndata_v1.py +43 -46
- ngio/tables/backends/_csv_v1.py +162 -0
- ngio/tables/backends/_json_v1.py +54 -18
- ngio/tables/backends/_table_backends.py +98 -18
- ngio/tables/backends/_utils.py +458 -0
- ngio/tables/tables_container.py +3 -1
- ngio/tables/v1/_feature_table.py +20 -11
- ngio/tables/v1/_generic_table.py +20 -15
- ngio/tables/v1/_roi_table.py +7 -9
- ngio/utils/_zarr_utils.py +46 -32
- {ngio-0.2.1.dist-info → ngio-0.2.3.dist-info}/METADATA +3 -1
- ngio-0.2.3.dist-info/RECORD +57 -0
- ngio-0.2.1.dist-info/RECORD +0 -54
- {ngio-0.2.1.dist-info → ngio-0.2.3.dist-info}/WHEEL +0 -0
- {ngio-0.2.1.dist-info → ngio-0.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ from typing import Literal, overload
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
|
-
from ngio.images.create import
|
|
8
|
+
from ngio.images.create import create_empty_image_container
|
|
9
9
|
from ngio.images.image import Image, ImagesContainer
|
|
10
10
|
from ngio.images.label import Label, LabelsContainer
|
|
11
11
|
from ngio.images.masked_image import MaskedImage, MaskedLabel
|
|
@@ -13,7 +13,14 @@ from ngio.ome_zarr_meta import (
|
|
|
13
13
|
NgioImageMeta,
|
|
14
14
|
PixelSize,
|
|
15
15
|
)
|
|
16
|
-
from ngio.ome_zarr_meta.ngio_specs import
|
|
16
|
+
from ngio.ome_zarr_meta.ngio_specs import (
|
|
17
|
+
DefaultNgffVersion,
|
|
18
|
+
DefaultSpaceUnit,
|
|
19
|
+
DefaultTimeUnit,
|
|
20
|
+
NgffVersions,
|
|
21
|
+
SpaceUnits,
|
|
22
|
+
TimeUnits,
|
|
23
|
+
)
|
|
17
24
|
from ngio.tables import (
|
|
18
25
|
FeatureTable,
|
|
19
26
|
GenericRoiTable,
|
|
@@ -152,7 +159,17 @@ class OmeZarrContainer:
|
|
|
152
159
|
"""Return True if the image is multichannel."""
|
|
153
160
|
return self.get_image().is_multi_channels
|
|
154
161
|
|
|
155
|
-
|
|
162
|
+
@property
|
|
163
|
+
def space_unit(self) -> str | None:
|
|
164
|
+
"""Return the space unit of the image."""
|
|
165
|
+
return self.image_meta.space_unit
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def time_unit(self) -> str | None:
|
|
169
|
+
"""Return the time unit of the image."""
|
|
170
|
+
return self.image_meta.time_unit
|
|
171
|
+
|
|
172
|
+
def set_channel_meta(
|
|
156
173
|
self,
|
|
157
174
|
labels: Collection[str] | int | None = None,
|
|
158
175
|
wavelength_id: Collection[str] | None = None,
|
|
@@ -162,7 +179,7 @@ class OmeZarrContainer:
|
|
|
162
179
|
**omero_kwargs: dict,
|
|
163
180
|
) -> None:
|
|
164
181
|
"""Create a ChannelsMeta object with the default unit."""
|
|
165
|
-
self._images_container.
|
|
182
|
+
self._images_container.set_channel_meta(
|
|
166
183
|
labels=labels,
|
|
167
184
|
wavelength_id=wavelength_id,
|
|
168
185
|
percentiles=percentiles,
|
|
@@ -171,16 +188,36 @@ class OmeZarrContainer:
|
|
|
171
188
|
**omero_kwargs,
|
|
172
189
|
)
|
|
173
190
|
|
|
174
|
-
def
|
|
191
|
+
def set_channel_percentiles(
|
|
175
192
|
self,
|
|
176
193
|
start_percentile: float = 0.1,
|
|
177
194
|
end_percentile: float = 99.9,
|
|
178
195
|
) -> None:
|
|
179
196
|
"""Update the percentiles of the image."""
|
|
180
|
-
self._images_container.
|
|
197
|
+
self._images_container.set_channel_percentiles(
|
|
181
198
|
start_percentile=start_percentile, end_percentile=end_percentile
|
|
182
199
|
)
|
|
183
200
|
|
|
201
|
+
def set_axes_units(
|
|
202
|
+
self,
|
|
203
|
+
space_unit: SpaceUnits = DefaultSpaceUnit,
|
|
204
|
+
time_unit: TimeUnits = DefaultTimeUnit,
|
|
205
|
+
set_labels: bool = True,
|
|
206
|
+
) -> None:
|
|
207
|
+
"""Set the units of the image.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
space_unit (SpaceUnits): The unit of space.
|
|
211
|
+
time_unit (TimeUnits): The unit of time.
|
|
212
|
+
set_labels (bool): Whether to set the units for the labels as well.
|
|
213
|
+
"""
|
|
214
|
+
self._images_container.set_axes_unit(space_unit=space_unit, time_unit=time_unit)
|
|
215
|
+
if not set_labels:
|
|
216
|
+
return
|
|
217
|
+
for label_name in self.list_labels():
|
|
218
|
+
label = self.get_label(label_name)
|
|
219
|
+
label.set_axes_unit(space_unit=space_unit, time_unit=time_unit)
|
|
220
|
+
|
|
184
221
|
def get_image(
|
|
185
222
|
self,
|
|
186
223
|
path: str | None = None,
|
|
@@ -247,6 +284,7 @@ class OmeZarrContainer:
|
|
|
247
284
|
labels: Collection[str] | None = None,
|
|
248
285
|
pixel_size: PixelSize | None = None,
|
|
249
286
|
axes_names: Collection[str] | None = None,
|
|
287
|
+
name: str | None = None,
|
|
250
288
|
chunks: Collection[int] | None = None,
|
|
251
289
|
dtype: str | None = None,
|
|
252
290
|
copy_labels: bool = False,
|
|
@@ -265,6 +303,7 @@ class OmeZarrContainer:
|
|
|
265
303
|
axes_names (Collection[str] | None): The axes names of the new image.
|
|
266
304
|
chunks (Collection[int] | None): The chunk shape of the new image.
|
|
267
305
|
dtype (str | None): The data type of the new image.
|
|
306
|
+
name (str | None): The name of the new image.
|
|
268
307
|
copy_labels (bool): Whether to copy the labels from the reference image.
|
|
269
308
|
copy_tables (bool): Whether to copy the tables from the reference image.
|
|
270
309
|
overwrite (bool): Whether to overwrite an existing image.
|
|
@@ -280,6 +319,7 @@ class OmeZarrContainer:
|
|
|
280
319
|
labels=labels,
|
|
281
320
|
pixel_size=pixel_size,
|
|
282
321
|
axes_names=axes_names,
|
|
322
|
+
name=name,
|
|
283
323
|
chunks=chunks,
|
|
284
324
|
dtype=dtype,
|
|
285
325
|
overwrite=overwrite,
|
|
@@ -462,7 +502,7 @@ class OmeZarrContainer:
|
|
|
462
502
|
def derive_label(
|
|
463
503
|
self,
|
|
464
504
|
name: str,
|
|
465
|
-
ref_image: Image | None = None,
|
|
505
|
+
ref_image: Image | Label | None = None,
|
|
466
506
|
shape: Collection[int] | None = None,
|
|
467
507
|
pixel_size: PixelSize | None = None,
|
|
468
508
|
axes_names: Collection[str] | None = None,
|
|
@@ -475,9 +515,9 @@ class OmeZarrContainer:
|
|
|
475
515
|
And add the label to the /labels group.
|
|
476
516
|
|
|
477
517
|
Args:
|
|
478
|
-
store (StoreOrGroup): The Zarr store or group to create the image in.
|
|
479
|
-
ref_image (Image): The reference image.
|
|
480
518
|
name (str): The name of the new image.
|
|
519
|
+
ref_image (Image | Label | None): A reference image that will be used
|
|
520
|
+
to create the new image.
|
|
481
521
|
shape (Collection[int] | None): The shape of the new image.
|
|
482
522
|
pixel_size (PixelSize | None): The pixel size of the new image.
|
|
483
523
|
axes_names (Collection[str] | None): The axes names of the new image.
|
|
@@ -557,19 +597,18 @@ def create_empty_ome_zarr(
|
|
|
557
597
|
levels: int | list[str] = 5,
|
|
558
598
|
xy_scaling_factor: float = 2,
|
|
559
599
|
z_scaling_factor: float = 1.0,
|
|
560
|
-
space_unit: SpaceUnits
|
|
561
|
-
time_unit: TimeUnits
|
|
600
|
+
space_unit: SpaceUnits = DefaultSpaceUnit,
|
|
601
|
+
time_unit: TimeUnits = DefaultTimeUnit,
|
|
562
602
|
axes_names: Collection[str] | None = None,
|
|
563
603
|
name: str | None = None,
|
|
564
604
|
chunks: Collection[int] | None = None,
|
|
565
605
|
dtype: str = "uint16",
|
|
566
606
|
channel_labels: list[str] | None = None,
|
|
567
607
|
channel_wavelengths: list[str] | None = None,
|
|
568
|
-
percentiles: tuple[float, float] | None = None,
|
|
569
608
|
channel_colors: Collection[str] | None = None,
|
|
570
609
|
channel_active: Collection[bool] | None = None,
|
|
571
610
|
overwrite: bool = False,
|
|
572
|
-
version:
|
|
611
|
+
version: NgffVersions = DefaultNgffVersion,
|
|
573
612
|
) -> OmeZarrContainer:
|
|
574
613
|
"""Create an empty OME-Zarr image with the given shape and metadata.
|
|
575
614
|
|
|
@@ -586,10 +625,10 @@ def create_empty_ome_zarr(
|
|
|
586
625
|
dimensions. Defaults to 2.0.
|
|
587
626
|
z_scaling_factor (float, optional): The down-scaling factor in z dimension.
|
|
588
627
|
Defaults to 1.0.
|
|
589
|
-
space_unit (SpaceUnits
|
|
590
|
-
|
|
591
|
-
time_unit (TimeUnits
|
|
592
|
-
|
|
628
|
+
space_unit (SpaceUnits, optional): The unit of space. Defaults to
|
|
629
|
+
DefaultSpaceUnit.
|
|
630
|
+
time_unit (TimeUnits, optional): The unit of time. Defaults to
|
|
631
|
+
DefaultTimeUnit.
|
|
593
632
|
axes_names (Collection[str] | None, optional): The names of the axes.
|
|
594
633
|
If None the canonical names are used. Defaults to None.
|
|
595
634
|
name (str | None, optional): The name of the image. Defaults to None.
|
|
@@ -600,8 +639,6 @@ def create_empty_ome_zarr(
|
|
|
600
639
|
Defaults to None.
|
|
601
640
|
channel_wavelengths (list[str] | None, optional): The wavelengths of the
|
|
602
641
|
channels. Defaults to None.
|
|
603
|
-
percentiles (tuple[float, float] | None, optional): The percentiles of the
|
|
604
|
-
channels. Defaults to None.
|
|
605
642
|
channel_colors (Collection[str] | None, optional): The colors of the channels.
|
|
606
643
|
Defaults to None.
|
|
607
644
|
channel_active (Collection[bool] | None, optional): Whether the channels are
|
|
@@ -609,9 +646,9 @@ def create_empty_ome_zarr(
|
|
|
609
646
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
610
647
|
Defaults to True.
|
|
611
648
|
version (NgffVersion, optional): The version of the OME-Zarr specification.
|
|
612
|
-
Defaults to
|
|
649
|
+
Defaults to DefaultNgffVersion.
|
|
613
650
|
"""
|
|
614
|
-
handler =
|
|
651
|
+
handler = create_empty_image_container(
|
|
615
652
|
store=store,
|
|
616
653
|
shape=shape,
|
|
617
654
|
pixelsize=xy_pixelsize,
|
|
@@ -631,10 +668,10 @@ def create_empty_ome_zarr(
|
|
|
631
668
|
)
|
|
632
669
|
|
|
633
670
|
ome_zarr = OmeZarrContainer(group_handler=handler)
|
|
634
|
-
ome_zarr.
|
|
671
|
+
ome_zarr.set_channel_meta(
|
|
635
672
|
labels=channel_labels,
|
|
636
673
|
wavelength_id=channel_wavelengths,
|
|
637
|
-
percentiles=
|
|
674
|
+
percentiles=None,
|
|
638
675
|
colors=channel_colors,
|
|
639
676
|
active=channel_active,
|
|
640
677
|
)
|
|
@@ -650,8 +687,8 @@ def create_ome_zarr_from_array(
|
|
|
650
687
|
levels: int | list[str] = 5,
|
|
651
688
|
xy_scaling_factor: float = 2.0,
|
|
652
689
|
z_scaling_factor: float = 1.0,
|
|
653
|
-
space_unit: SpaceUnits
|
|
654
|
-
time_unit: TimeUnits
|
|
690
|
+
space_unit: SpaceUnits = DefaultSpaceUnit,
|
|
691
|
+
time_unit: TimeUnits = DefaultTimeUnit,
|
|
655
692
|
axes_names: Collection[str] | None = None,
|
|
656
693
|
channel_labels: list[str] | None = None,
|
|
657
694
|
channel_wavelengths: list[str] | None = None,
|
|
@@ -661,7 +698,7 @@ def create_ome_zarr_from_array(
|
|
|
661
698
|
name: str | None = None,
|
|
662
699
|
chunks: Collection[int] | None = None,
|
|
663
700
|
overwrite: bool = False,
|
|
664
|
-
version:
|
|
701
|
+
version: NgffVersions = DefaultNgffVersion,
|
|
665
702
|
) -> OmeZarrContainer:
|
|
666
703
|
"""Create an OME-Zarr image from a numpy array.
|
|
667
704
|
|
|
@@ -678,10 +715,10 @@ def create_ome_zarr_from_array(
|
|
|
678
715
|
dimensions. Defaults to 2.0.
|
|
679
716
|
z_scaling_factor (float, optional): The down-scaling factor in z dimension.
|
|
680
717
|
Defaults to 1.0.
|
|
681
|
-
space_unit (SpaceUnits
|
|
682
|
-
|
|
683
|
-
time_unit (TimeUnits
|
|
684
|
-
|
|
718
|
+
space_unit (SpaceUnits, optional): The unit of space. Defaults to
|
|
719
|
+
DefaultSpaceUnit.
|
|
720
|
+
time_unit (TimeUnits, optional): The unit of time. Defaults to
|
|
721
|
+
DefaultTimeUnit.
|
|
685
722
|
axes_names (Collection[str] | None, optional): The names of the axes.
|
|
686
723
|
If None the canonical names are used. Defaults to None.
|
|
687
724
|
name (str | None, optional): The name of the image. Defaults to None.
|
|
@@ -700,9 +737,9 @@ def create_ome_zarr_from_array(
|
|
|
700
737
|
overwrite (bool, optional): Whether to overwrite an existing image.
|
|
701
738
|
Defaults to True.
|
|
702
739
|
version (str, optional): The version of the OME-Zarr specification.
|
|
703
|
-
Defaults to
|
|
740
|
+
Defaults to DefaultNgffVersion.
|
|
704
741
|
"""
|
|
705
|
-
handler =
|
|
742
|
+
handler = create_empty_image_container(
|
|
706
743
|
store=store,
|
|
707
744
|
shape=array.shape,
|
|
708
745
|
pixelsize=xy_pixelsize,
|
|
@@ -725,7 +762,7 @@ def create_ome_zarr_from_array(
|
|
|
725
762
|
image = ome_zarr.get_image()
|
|
726
763
|
image.set_array(array)
|
|
727
764
|
image.consolidate()
|
|
728
|
-
ome_zarr.
|
|
765
|
+
ome_zarr.set_channel_meta(
|
|
729
766
|
labels=channel_labels,
|
|
730
767
|
wavelength_id=channel_wavelengths,
|
|
731
768
|
percentiles=percentiles,
|
ngio/ome_zarr_meta/__init__.py
CHANGED
|
@@ -16,12 +16,13 @@ from ngio.ome_zarr_meta.ngio_specs import (
|
|
|
16
16
|
AxesMapper,
|
|
17
17
|
Dataset,
|
|
18
18
|
ImageInWellPath,
|
|
19
|
-
|
|
19
|
+
NgffVersions,
|
|
20
20
|
NgioImageMeta,
|
|
21
21
|
NgioLabelMeta,
|
|
22
22
|
NgioPlateMeta,
|
|
23
23
|
NgioWellMeta,
|
|
24
24
|
PixelSize,
|
|
25
|
+
path_in_well_validation,
|
|
25
26
|
)
|
|
26
27
|
|
|
27
28
|
__all__ = [
|
|
@@ -32,8 +33,8 @@ __all__ = [
|
|
|
32
33
|
"ImageMetaHandler",
|
|
33
34
|
"LabelMetaHandler",
|
|
34
35
|
"LabelMetaHandler",
|
|
35
|
-
"
|
|
36
|
-
"
|
|
36
|
+
"NgffVersions",
|
|
37
|
+
"NgffVersions",
|
|
37
38
|
"NgioImageMeta",
|
|
38
39
|
"NgioLabelMeta",
|
|
39
40
|
"NgioPlateMeta",
|
|
@@ -47,4 +48,5 @@ __all__ = [
|
|
|
47
48
|
"get_label_meta_handler",
|
|
48
49
|
"get_plate_meta_handler",
|
|
49
50
|
"get_well_meta_handler",
|
|
51
|
+
"path_in_well_validation",
|
|
50
52
|
]
|
|
@@ -15,6 +15,8 @@ from ngio.ome_zarr_meta.ngio_specs._axes import (
|
|
|
15
15
|
AxesTranspose,
|
|
16
16
|
Axis,
|
|
17
17
|
AxisType,
|
|
18
|
+
DefaultSpaceUnit,
|
|
19
|
+
DefaultTimeUnit,
|
|
18
20
|
SpaceUnits,
|
|
19
21
|
TimeUnits,
|
|
20
22
|
canonical_axes_order,
|
|
@@ -32,10 +34,12 @@ from ngio.ome_zarr_meta.ngio_specs._ngio_hcs import (
|
|
|
32
34
|
ImageInWellPath,
|
|
33
35
|
NgioPlateMeta,
|
|
34
36
|
NgioWellMeta,
|
|
37
|
+
path_in_well_validation,
|
|
35
38
|
)
|
|
36
39
|
from ngio.ome_zarr_meta.ngio_specs._ngio_image import (
|
|
40
|
+
DefaultNgffVersion,
|
|
37
41
|
ImageLabelSource,
|
|
38
|
-
|
|
42
|
+
NgffVersions,
|
|
39
43
|
NgioImageLabelMeta,
|
|
40
44
|
NgioImageMeta,
|
|
41
45
|
NgioLabelMeta,
|
|
@@ -55,9 +59,12 @@ __all__ = [
|
|
|
55
59
|
"ChannelVisualisation",
|
|
56
60
|
"ChannelsMeta",
|
|
57
61
|
"Dataset",
|
|
62
|
+
"DefaultNgffVersion",
|
|
63
|
+
"DefaultSpaceUnit",
|
|
64
|
+
"DefaultTimeUnit",
|
|
58
65
|
"ImageInWellPath",
|
|
59
66
|
"ImageLabelSource",
|
|
60
|
-
"
|
|
67
|
+
"NgffVersions",
|
|
61
68
|
"NgioColors",
|
|
62
69
|
"NgioImageLabelMeta",
|
|
63
70
|
"NgioImageMeta",
|
|
@@ -70,4 +77,5 @@ __all__ = [
|
|
|
70
77
|
"canonical_axes_order",
|
|
71
78
|
"canonical_label_axes_order",
|
|
72
79
|
"default_channel_name",
|
|
80
|
+
"path_in_well_validation",
|
|
73
81
|
]
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from collections.abc import Collection
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from logging import Logger
|
|
6
|
-
from typing import TypeVar
|
|
6
|
+
from typing import Literal, TypeVar
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from pydantic import BaseModel, ConfigDict, Field
|
|
@@ -32,98 +32,108 @@ class AxisType(str, Enum):
|
|
|
32
32
|
space = "space"
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
35
|
+
SpaceUnits = Literal[
|
|
36
|
+
"micrometer",
|
|
37
|
+
"nanometer",
|
|
38
|
+
"angstrom",
|
|
39
|
+
"picometer",
|
|
40
|
+
"millimeter",
|
|
41
|
+
"centimeter",
|
|
42
|
+
"decimeter",
|
|
43
|
+
"meter",
|
|
44
|
+
"inch",
|
|
45
|
+
"foot",
|
|
46
|
+
"yard",
|
|
47
|
+
"mile",
|
|
48
|
+
"kilometer",
|
|
49
|
+
"hectometer",
|
|
50
|
+
"megameter",
|
|
51
|
+
"gigameter",
|
|
52
|
+
"terameter",
|
|
53
|
+
"petameter",
|
|
54
|
+
"exameter",
|
|
55
|
+
"parsec",
|
|
56
|
+
"femtometer",
|
|
57
|
+
"attometer",
|
|
58
|
+
"zeptometer",
|
|
59
|
+
"yoctometer",
|
|
60
|
+
"zettameter",
|
|
61
|
+
"yottameter",
|
|
62
|
+
]
|
|
63
|
+
DefaultSpaceUnit = "micrometer"
|
|
64
|
+
|
|
65
|
+
TimeUnits = Literal[
|
|
66
|
+
"attosecond",
|
|
67
|
+
"centisecond",
|
|
68
|
+
"day",
|
|
69
|
+
"decisecond",
|
|
70
|
+
"exasecond",
|
|
71
|
+
"femtosecond",
|
|
72
|
+
"gigasecond",
|
|
73
|
+
"hectosecond",
|
|
74
|
+
"hour",
|
|
75
|
+
"kilosecond",
|
|
76
|
+
"megasecond",
|
|
77
|
+
"microsecond",
|
|
78
|
+
"millisecond",
|
|
79
|
+
"minute",
|
|
80
|
+
"nanosecond",
|
|
81
|
+
"petasecond",
|
|
82
|
+
"picosecond",
|
|
83
|
+
"second",
|
|
84
|
+
"terasecond",
|
|
85
|
+
"yoctosecond",
|
|
86
|
+
"yottasecond",
|
|
87
|
+
"zeptosecond",
|
|
88
|
+
"zettasecond",
|
|
89
|
+
]
|
|
90
|
+
DefaultTimeUnit = "second"
|
|
61
91
|
|
|
62
92
|
|
|
63
93
|
class Axis(BaseModel):
|
|
64
94
|
"""Axis infos model."""
|
|
65
95
|
|
|
66
96
|
on_disk_name: str
|
|
67
|
-
unit:
|
|
97
|
+
unit: str | None = None
|
|
68
98
|
axis_type: AxisType | None = None
|
|
69
99
|
|
|
70
100
|
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
71
101
|
|
|
72
102
|
def implicit_type_cast(self, cast_type: AxisType) -> "Axis":
|
|
103
|
+
unit = self.unit
|
|
73
104
|
if self.axis_type != cast_type:
|
|
74
105
|
logger.warning(
|
|
75
106
|
f"Axis {self.on_disk_name} has type {self.axis_type}. "
|
|
76
107
|
f"Casting to {cast_type}."
|
|
77
108
|
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
if cast_type == AxisType.time and not isinstance(self.unit, TimeUnits):
|
|
109
|
+
|
|
110
|
+
if cast_type == AxisType.time and unit is None:
|
|
82
111
|
logger.warning(
|
|
83
112
|
f"Time axis {self.on_disk_name} has unit {self.unit}. "
|
|
84
|
-
f"Casting to {
|
|
85
|
-
)
|
|
86
|
-
new_axis = Axis(
|
|
87
|
-
on_disk_name=self.on_disk_name,
|
|
88
|
-
axis_type=AxisType.time,
|
|
89
|
-
unit=TimeUnits.default(),
|
|
90
|
-
)
|
|
91
|
-
elif cast_type == AxisType.space and not isinstance(self.unit, SpaceUnits):
|
|
92
|
-
logger.warning(
|
|
93
|
-
f"Space axis {self.on_disk_name} has unit {self.unit}. "
|
|
94
|
-
f"Casting to {SpaceUnits.default()}."
|
|
113
|
+
f"Casting to {DefaultSpaceUnit}."
|
|
95
114
|
)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
unit=SpaceUnits.default(),
|
|
100
|
-
)
|
|
101
|
-
elif cast_type == AxisType.channel and self.unit is not None:
|
|
115
|
+
unit = DefaultTimeUnit
|
|
116
|
+
|
|
117
|
+
if cast_type == AxisType.space and unit is None:
|
|
102
118
|
logger.warning(
|
|
103
|
-
f"
|
|
104
|
-
|
|
105
|
-
new_axis = Axis(
|
|
106
|
-
on_disk_name=self.on_disk_name,
|
|
107
|
-
axis_type=AxisType.channel,
|
|
108
|
-
unit=None,
|
|
119
|
+
f"Space axis {self.on_disk_name} has unit {unit}. "
|
|
120
|
+
f"Casting to {DefaultSpaceUnit}."
|
|
109
121
|
)
|
|
110
|
-
|
|
122
|
+
unit = DefaultSpaceUnit
|
|
123
|
+
|
|
124
|
+
return Axis(on_disk_name=self.on_disk_name, axis_type=cast_type, unit=unit)
|
|
111
125
|
|
|
112
126
|
def canonical_axis_cast(self, canonical_name: str) -> "Axis":
|
|
113
127
|
"""Cast the implicit axis to the correct type."""
|
|
114
128
|
match canonical_name:
|
|
115
129
|
case "t":
|
|
116
|
-
if self.axis_type != AxisType.time or
|
|
117
|
-
self.unit, TimeUnits
|
|
118
|
-
):
|
|
130
|
+
if self.axis_type != AxisType.time or self.unit is None:
|
|
119
131
|
return self.implicit_type_cast(AxisType.time)
|
|
120
132
|
case "c":
|
|
121
|
-
if self.axis_type != AxisType.channel
|
|
133
|
+
if self.axis_type != AxisType.channel:
|
|
122
134
|
return self.implicit_type_cast(AxisType.channel)
|
|
123
135
|
case "z" | "y" | "x":
|
|
124
|
-
if self.axis_type != AxisType.space or
|
|
125
|
-
self.unit, SpaceUnits
|
|
126
|
-
):
|
|
136
|
+
if self.axis_type != AxisType.space or self.unit is None:
|
|
127
137
|
return self.implicit_type_cast(AxisType.space)
|
|
128
138
|
return self
|
|
129
139
|
|
|
@@ -345,6 +355,11 @@ class AxesMapper:
|
|
|
345
355
|
_index_mapping[canonical_key] = None
|
|
346
356
|
return _index_mapping
|
|
347
357
|
|
|
358
|
+
@property
|
|
359
|
+
def axes_setup(self) -> AxesSetup:
|
|
360
|
+
"""Return the axes setup."""
|
|
361
|
+
return self._axes_setup
|
|
362
|
+
|
|
348
363
|
@property
|
|
349
364
|
def on_disk_axes(self) -> list[Axis]:
|
|
350
365
|
return list(self._on_disk_axes)
|
|
@@ -353,6 +368,16 @@ class AxesMapper:
|
|
|
353
368
|
def on_disk_axes_names(self) -> list[str]:
|
|
354
369
|
return [ax.on_disk_name for ax in self._on_disk_axes]
|
|
355
370
|
|
|
371
|
+
@property
|
|
372
|
+
def allow_non_canonical_axes(self) -> bool:
|
|
373
|
+
"""Return if non canonical axes are allowed."""
|
|
374
|
+
return self._allow_non_canonical_axes
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def strict_canonical_order(self) -> bool:
|
|
378
|
+
"""Return if strict canonical order is enforced."""
|
|
379
|
+
return self._strict_canonical_order
|
|
380
|
+
|
|
356
381
|
def get_index(self, name: str) -> int | None:
|
|
357
382
|
"""Get the index of the axis by name."""
|
|
358
383
|
if name not in self._index_mapping.keys():
|
|
@@ -443,8 +468,8 @@ class AxesMapper:
|
|
|
443
468
|
|
|
444
469
|
def canonical_axes(
|
|
445
470
|
axes_names: Collection[str],
|
|
446
|
-
space_units: SpaceUnits | None =
|
|
447
|
-
time_units: TimeUnits | None =
|
|
471
|
+
space_units: SpaceUnits | None = DefaultSpaceUnit,
|
|
472
|
+
time_units: TimeUnits | None = DefaultTimeUnit,
|
|
448
473
|
) -> list[Axis]:
|
|
449
474
|
"""Create a new canonical axes mapper.
|
|
450
475
|
|
|
@@ -6,6 +6,9 @@ from ngio.ome_zarr_meta.ngio_specs._axes import (
|
|
|
6
6
|
AxesMapper,
|
|
7
7
|
AxesSetup,
|
|
8
8
|
Axis,
|
|
9
|
+
AxisType,
|
|
10
|
+
DefaultSpaceUnit,
|
|
11
|
+
DefaultTimeUnit,
|
|
9
12
|
SpaceUnits,
|
|
10
13
|
TimeUnits,
|
|
11
14
|
)
|
|
@@ -86,7 +89,7 @@ class Dataset:
|
|
|
86
89
|
return self._path
|
|
87
90
|
|
|
88
91
|
@property
|
|
89
|
-
def space_unit(self) ->
|
|
92
|
+
def space_unit(self) -> str | None:
|
|
90
93
|
"""Return the space unit for a given axis."""
|
|
91
94
|
x_axis = self._axes_mapper.get_axis("x")
|
|
92
95
|
y_axis = self._axes_mapper.get_axis("y")
|
|
@@ -97,8 +100,6 @@ class Dataset:
|
|
|
97
100
|
)
|
|
98
101
|
|
|
99
102
|
if x_axis.unit == y_axis.unit:
|
|
100
|
-
if not isinstance(x_axis.unit, SpaceUnits):
|
|
101
|
-
raise NgioValidationError("The space unit must be of type SpaceUnits.")
|
|
102
103
|
return x_axis.unit
|
|
103
104
|
else:
|
|
104
105
|
raise NgioValidationError(
|
|
@@ -107,13 +108,11 @@ class Dataset:
|
|
|
107
108
|
)
|
|
108
109
|
|
|
109
110
|
@property
|
|
110
|
-
def time_unit(self) ->
|
|
111
|
+
def time_unit(self) -> str | None:
|
|
111
112
|
"""Return the time unit for a given axis."""
|
|
112
113
|
t_axis = self._axes_mapper.get_axis("t")
|
|
113
114
|
if t_axis is None:
|
|
114
115
|
return None
|
|
115
|
-
if not isinstance(t_axis.unit, TimeUnits):
|
|
116
|
-
raise NgioValidationError("The time unit must be of type TimeUnits.")
|
|
117
116
|
return t_axis.unit
|
|
118
117
|
|
|
119
118
|
@property
|
|
@@ -124,11 +123,50 @@ class Dataset:
|
|
|
124
123
|
y=self.get_scale("y"),
|
|
125
124
|
z=self.get_scale("z"),
|
|
126
125
|
t=self.get_scale("t"),
|
|
127
|
-
space_unit=self.space_unit,
|
|
128
|
-
time_unit=self.time_unit,
|
|
126
|
+
space_unit=self.space_unit, # type: ignore
|
|
127
|
+
time_unit=self.time_unit, # type: ignore
|
|
129
128
|
)
|
|
130
129
|
|
|
131
130
|
@property
|
|
132
131
|
def axes_mapper(self) -> AxesMapper:
|
|
133
132
|
"""Return the axes mapper object."""
|
|
134
133
|
return self._axes_mapper
|
|
134
|
+
|
|
135
|
+
def to_units(
|
|
136
|
+
self,
|
|
137
|
+
*,
|
|
138
|
+
space_unit: SpaceUnits = DefaultSpaceUnit,
|
|
139
|
+
time_unit: TimeUnits = DefaultTimeUnit,
|
|
140
|
+
) -> "Dataset":
|
|
141
|
+
"""Convert the pixel size to the given units.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
space_unit(str): The space unit to convert to.
|
|
145
|
+
time_unit(str): The time unit to convert to.
|
|
146
|
+
"""
|
|
147
|
+
new_axes = []
|
|
148
|
+
for ax in self.axes_mapper.on_disk_axes:
|
|
149
|
+
if ax.axis_type == AxisType.space:
|
|
150
|
+
new_ax = Axis(
|
|
151
|
+
on_disk_name=ax.on_disk_name,
|
|
152
|
+
axis_type=ax.axis_type,
|
|
153
|
+
unit=space_unit,
|
|
154
|
+
)
|
|
155
|
+
new_axes.append(new_ax)
|
|
156
|
+
elif ax.axis_type == AxisType.time:
|
|
157
|
+
new_ax = Axis(
|
|
158
|
+
on_disk_name=ax.on_disk_name, axis_type=ax.axis_type, unit=time_unit
|
|
159
|
+
)
|
|
160
|
+
new_axes.append(new_ax)
|
|
161
|
+
else:
|
|
162
|
+
new_axes.append(ax)
|
|
163
|
+
|
|
164
|
+
return Dataset(
|
|
165
|
+
path=self.path,
|
|
166
|
+
on_disk_axes=new_axes,
|
|
167
|
+
on_disk_scale=self._on_disk_scale,
|
|
168
|
+
on_disk_translation=self._on_disk_translation,
|
|
169
|
+
axes_setup=self.axes_mapper.axes_setup,
|
|
170
|
+
allow_non_canonical_axes=self.axes_mapper.allow_non_canonical_axes,
|
|
171
|
+
strict_canonical_order=self.axes_mapper.strict_canonical_order,
|
|
172
|
+
)
|