ngio 0.1.6__py3-none-any.whl → 0.2.0a1__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 +31 -5
- ngio/common/__init__.py +44 -0
- ngio/common/_array_pipe.py +160 -0
- ngio/common/_axes_transforms.py +63 -0
- ngio/common/_common_types.py +5 -0
- ngio/common/_dimensions.py +113 -0
- ngio/common/_pyramid.py +222 -0
- ngio/{core/roi.py → common/_roi.py} +22 -23
- ngio/common/_slicer.py +97 -0
- ngio/{pipes/_zoom_utils.py → common/_zoom.py} +2 -78
- ngio/hcs/__init__.py +60 -0
- ngio/images/__init__.py +23 -0
- ngio/images/abstract_image.py +240 -0
- ngio/images/create.py +251 -0
- ngio/images/image.py +383 -0
- ngio/images/label.py +96 -0
- ngio/images/omezarr_container.py +512 -0
- ngio/ome_zarr_meta/__init__.py +35 -0
- ngio/ome_zarr_meta/_generic_handlers.py +320 -0
- ngio/ome_zarr_meta/_meta_handlers.py +142 -0
- ngio/ome_zarr_meta/ngio_specs/__init__.py +63 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +481 -0
- ngio/ome_zarr_meta/ngio_specs/_channels.py +378 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +134 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +5 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +434 -0
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +84 -0
- ngio/ome_zarr_meta/v04/__init__.py +11 -0
- ngio/ome_zarr_meta/v04/_meta_handlers.py +54 -0
- ngio/ome_zarr_meta/v04/_v04_spec_utils.py +412 -0
- ngio/tables/__init__.py +21 -5
- ngio/tables/_validators.py +192 -0
- ngio/tables/backends/__init__.py +8 -0
- ngio/tables/backends/_abstract_backend.py +71 -0
- ngio/tables/backends/_anndata_utils.py +194 -0
- ngio/tables/backends/_anndata_v1.py +75 -0
- ngio/tables/backends/_json_v1.py +56 -0
- ngio/tables/backends/_table_backends.py +102 -0
- ngio/tables/tables_container.py +300 -0
- ngio/tables/v1/__init__.py +6 -5
- ngio/tables/v1/_feature_table.py +161 -0
- ngio/tables/v1/_generic_table.py +99 -182
- ngio/tables/v1/_masking_roi_table.py +175 -0
- ngio/tables/v1/_roi_table.py +226 -0
- ngio/utils/__init__.py +23 -10
- ngio/utils/_datasets.py +51 -0
- ngio/utils/_errors.py +10 -4
- ngio/utils/_zarr_utils.py +378 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/METADATA +18 -39
- ngio-0.2.0a1.dist-info/RECORD +53 -0
- ngio/core/__init__.py +0 -7
- ngio/core/dimensions.py +0 -122
- ngio/core/image_handler.py +0 -228
- ngio/core/image_like_handler.py +0 -549
- ngio/core/label_handler.py +0 -410
- ngio/core/ngff_image.py +0 -387
- ngio/core/utils.py +0 -287
- ngio/io/__init__.py +0 -19
- ngio/io/_zarr.py +0 -88
- ngio/io/_zarr_array_utils.py +0 -0
- ngio/io/_zarr_group_utils.py +0 -60
- ngio/iterators/__init__.py +0 -1
- ngio/ngff_meta/__init__.py +0 -27
- ngio/ngff_meta/fractal_image_meta.py +0 -1267
- ngio/ngff_meta/meta_handler.py +0 -92
- ngio/ngff_meta/utils.py +0 -235
- ngio/ngff_meta/v04/__init__.py +0 -6
- ngio/ngff_meta/v04/specs.py +0 -158
- ngio/ngff_meta/v04/zarr_utils.py +0 -376
- ngio/pipes/__init__.py +0 -7
- ngio/pipes/_slicer_transforms.py +0 -176
- ngio/pipes/_transforms.py +0 -33
- ngio/pipes/data_pipe.py +0 -52
- ngio/tables/_ad_reader.py +0 -80
- ngio/tables/_utils.py +0 -301
- ngio/tables/tables_group.py +0 -252
- ngio/tables/v1/feature_tables.py +0 -182
- ngio/tables/v1/masking_roi_tables.py +0 -243
- ngio/tables/v1/roi_tables.py +0 -285
- ngio/utils/_common_types.py +0 -5
- ngio/utils/_pydantic_utils.py +0 -52
- ngio-0.1.6.dist-info/RECORD +0 -44
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/WHEEL +0 -0
- {ngio-0.1.6.dist-info → ngio-0.2.0a1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Module for handling the /tables group in an OME-NGFF file."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Protocol
|
|
4
|
+
|
|
5
|
+
from ngio.tables.v1 import FeatureTableV1, MaskingROITableV1, RoiTableV1
|
|
6
|
+
from ngio.tables.v1._generic_table import GenericTable
|
|
7
|
+
from ngio.utils import (
|
|
8
|
+
AccessModeLiteral,
|
|
9
|
+
NgioValidationError,
|
|
10
|
+
NgioValueError,
|
|
11
|
+
StoreOrGroup,
|
|
12
|
+
ZarrGroupHandler,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
RoiTable = RoiTableV1
|
|
16
|
+
MaskingROITable = MaskingROITableV1
|
|
17
|
+
FeatureTable = FeatureTableV1
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Table(Protocol):
|
|
21
|
+
"""Placeholder class for a table."""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def type() -> str | None:
|
|
25
|
+
"""Return the type of the table."""
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def version() -> str | None:
|
|
30
|
+
"""Return the version of the table."""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def backend_name(self) -> str | None:
|
|
35
|
+
"""The name of the backend."""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def _from_handler(
|
|
40
|
+
cls, handler: ZarrGroupHandler, backend_name: str | None = None
|
|
41
|
+
) -> "Table":
|
|
42
|
+
"""Create a new table from a Zarr group handler."""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
def _set_backend(
|
|
46
|
+
self,
|
|
47
|
+
handler: ZarrGroupHandler,
|
|
48
|
+
backend_name: str | None = None,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Set the backend store and path for the table."""
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def consolidate(self) -> None:
|
|
54
|
+
"""Consolidate the table on disk."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
TypedTable = Literal["roi_table", "masking_roi_table", "feature_table"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _unique_table_name(type_name, version) -> str:
|
|
62
|
+
"""Return the unique name for a table."""
|
|
63
|
+
return f"{type_name}_v{version}"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ImplementedTables:
|
|
67
|
+
"""A singleton class to manage the available table handler plugins."""
|
|
68
|
+
|
|
69
|
+
_instance = None
|
|
70
|
+
_implemented_tables: dict[str, type[Table]]
|
|
71
|
+
|
|
72
|
+
def __new__(cls):
|
|
73
|
+
"""Create a new instance of the class if it does not exist."""
|
|
74
|
+
if cls._instance is None:
|
|
75
|
+
cls._instance = super().__new__(cls)
|
|
76
|
+
cls._instance._implemented_tables = {}
|
|
77
|
+
return cls._instance
|
|
78
|
+
|
|
79
|
+
def available_implementations(self) -> list[str]:
|
|
80
|
+
"""Get the available table handler versions."""
|
|
81
|
+
return list(self._implemented_tables.keys())
|
|
82
|
+
|
|
83
|
+
def get_table(
|
|
84
|
+
self,
|
|
85
|
+
type: str,
|
|
86
|
+
version: str,
|
|
87
|
+
handler: ZarrGroupHandler,
|
|
88
|
+
backend_name: str | None = None,
|
|
89
|
+
) -> Table:
|
|
90
|
+
"""Try to get a handler for the given store based on the metadata version."""
|
|
91
|
+
_errors = {}
|
|
92
|
+
for name, table_cls in self._implemented_tables.items():
|
|
93
|
+
if name != _unique_table_name(type, version):
|
|
94
|
+
continue
|
|
95
|
+
try:
|
|
96
|
+
table = table_cls._from_handler(
|
|
97
|
+
handler=handler, backend_name=backend_name
|
|
98
|
+
)
|
|
99
|
+
return table
|
|
100
|
+
except Exception as e:
|
|
101
|
+
_errors[name] = e
|
|
102
|
+
print(_errors)
|
|
103
|
+
# If no table was found, we can try to load the table from a generic table
|
|
104
|
+
try:
|
|
105
|
+
table = GenericTable._from_handler(
|
|
106
|
+
handler=handler, backend_name=backend_name
|
|
107
|
+
)
|
|
108
|
+
return table
|
|
109
|
+
except Exception as e:
|
|
110
|
+
_errors["generic"] = e
|
|
111
|
+
|
|
112
|
+
if len(_errors) == 0:
|
|
113
|
+
raise NgioValidationError(
|
|
114
|
+
f"Could not find a table implementation for {type} v{version}. "
|
|
115
|
+
f"Available tables: {self.available_implementations()}"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
raise NgioValidationError(
|
|
119
|
+
f"Could not load table from any known version. Errors: {_errors}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def add_implementation(self, handler: type[Table], overwrite: bool = False):
|
|
123
|
+
"""Register a new table handler."""
|
|
124
|
+
table_type = handler.type()
|
|
125
|
+
version = handler.version()
|
|
126
|
+
if table_type is None:
|
|
127
|
+
raise NgioValueError("Table handler must have a type.")
|
|
128
|
+
|
|
129
|
+
if version is None:
|
|
130
|
+
raise NgioValueError("Table handler must have a version.")
|
|
131
|
+
|
|
132
|
+
table_unique_name = _unique_table_name(table_type, version)
|
|
133
|
+
if table_unique_name in self._implemented_tables and not overwrite:
|
|
134
|
+
raise NgioValueError(
|
|
135
|
+
f"Table handler for {table_unique_name} already exists. "
|
|
136
|
+
"Use overwrite=True to replace it."
|
|
137
|
+
)
|
|
138
|
+
self._implemented_tables[table_unique_name] = handler
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _get_table_type(handler: ZarrGroupHandler) -> str:
|
|
142
|
+
"""Get the type of the table from the handler."""
|
|
143
|
+
attrs = handler.load_attrs()
|
|
144
|
+
return attrs.get("type", "None")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _get_table_version(handler: ZarrGroupHandler) -> str:
|
|
148
|
+
"""Get the version of the table from the handler."""
|
|
149
|
+
attrs = handler.load_attrs()
|
|
150
|
+
return attrs.get("fractal_table_version", "None")
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class TablesContainer:
|
|
154
|
+
"""A class to handle the /labels group in an OME-NGFF file."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, group_handler: ZarrGroupHandler) -> None:
|
|
157
|
+
"""Initialize the LabelGroupHandler."""
|
|
158
|
+
self._group_handler = group_handler
|
|
159
|
+
|
|
160
|
+
# Validate the group
|
|
161
|
+
# Either contains a tables attribute or is empty
|
|
162
|
+
attrs = self._group_handler.load_attrs()
|
|
163
|
+
if len(attrs) == 0:
|
|
164
|
+
# It's an empty group
|
|
165
|
+
pass
|
|
166
|
+
elif "tables" in attrs and isinstance(attrs["tables"], list):
|
|
167
|
+
# It's a valid group
|
|
168
|
+
pass
|
|
169
|
+
else:
|
|
170
|
+
raise NgioValidationError(
|
|
171
|
+
f"Invalid /tables group. "
|
|
172
|
+
f"Expected a single tables attribute with a list of table names. "
|
|
173
|
+
f"Found: {attrs}"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def _get_tables_list(self) -> list[str]:
|
|
177
|
+
"""Create the /tables group if it doesn't exist."""
|
|
178
|
+
attrs = self._group_handler.load_attrs()
|
|
179
|
+
return attrs.get("tables", [])
|
|
180
|
+
|
|
181
|
+
def _get_table_group_handler(self, name: str) -> ZarrGroupHandler:
|
|
182
|
+
"""Get the group handler for a table."""
|
|
183
|
+
handler = self._group_handler.derive_handler(path=name)
|
|
184
|
+
return handler
|
|
185
|
+
|
|
186
|
+
def list(self, filter_types: str | None = None) -> list[str]:
|
|
187
|
+
"""List all labels in the group."""
|
|
188
|
+
tables = self._get_tables_list()
|
|
189
|
+
if filter_types is None:
|
|
190
|
+
return tables
|
|
191
|
+
|
|
192
|
+
filtered_tables = []
|
|
193
|
+
for table_name in tables:
|
|
194
|
+
tb_handler = self._get_table_group_handler(table_name)
|
|
195
|
+
table_type = _get_table_type(tb_handler)
|
|
196
|
+
if table_type == filter_types:
|
|
197
|
+
filtered_tables.append(table_name)
|
|
198
|
+
return filtered_tables
|
|
199
|
+
|
|
200
|
+
def get(self, name: str, backend_name: str | None = None) -> Table:
|
|
201
|
+
"""Get a label from the group."""
|
|
202
|
+
if name not in self.list():
|
|
203
|
+
raise KeyError(f"Table '{name}' not found in the group.")
|
|
204
|
+
|
|
205
|
+
table_handler = self._get_table_group_handler(name)
|
|
206
|
+
table_type = _get_table_type(table_handler)
|
|
207
|
+
table_version = _get_table_version(table_handler)
|
|
208
|
+
return ImplementedTables().get_table(
|
|
209
|
+
type=table_type,
|
|
210
|
+
version=table_version,
|
|
211
|
+
handler=table_handler,
|
|
212
|
+
backend_name=backend_name,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def add(
|
|
216
|
+
self,
|
|
217
|
+
name: str,
|
|
218
|
+
table: Table,
|
|
219
|
+
backend: str | None = None,
|
|
220
|
+
overwrite: bool = False,
|
|
221
|
+
) -> None:
|
|
222
|
+
"""Add a table to the group."""
|
|
223
|
+
existing_tables = self._get_tables_list()
|
|
224
|
+
if name in existing_tables and not overwrite:
|
|
225
|
+
raise NgioValueError(
|
|
226
|
+
f"Table '{name}' already exists in the group. "
|
|
227
|
+
"Use overwrite=True to replace it."
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
table_handler = self._group_handler.derive_handler(path=name)
|
|
231
|
+
|
|
232
|
+
if backend is None:
|
|
233
|
+
backend = table.backend_name
|
|
234
|
+
|
|
235
|
+
table._set_backend(
|
|
236
|
+
handler=table_handler,
|
|
237
|
+
backend_name=backend,
|
|
238
|
+
)
|
|
239
|
+
table.consolidate()
|
|
240
|
+
if name not in existing_tables:
|
|
241
|
+
existing_tables.append(name)
|
|
242
|
+
self._group_handler.write_attrs({"tables": existing_tables})
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
ImplementedTables().add_implementation(RoiTableV1)
|
|
246
|
+
ImplementedTables().add_implementation(MaskingROITableV1)
|
|
247
|
+
ImplementedTables().add_implementation(FeatureTableV1)
|
|
248
|
+
|
|
249
|
+
###################################################################################
|
|
250
|
+
#
|
|
251
|
+
# Utility functions to open and write tables
|
|
252
|
+
#
|
|
253
|
+
###################################################################################
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def open_tables_container(
|
|
257
|
+
store: StoreOrGroup,
|
|
258
|
+
cache: bool = False,
|
|
259
|
+
mode: AccessModeLiteral = "a",
|
|
260
|
+
parallel_safe: bool = False,
|
|
261
|
+
) -> TablesContainer:
|
|
262
|
+
"""Open a table handler from a Zarr store."""
|
|
263
|
+
handler = ZarrGroupHandler(
|
|
264
|
+
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
265
|
+
)
|
|
266
|
+
return TablesContainer(handler)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def open_table(
|
|
270
|
+
store: StoreOrGroup,
|
|
271
|
+
cache: bool = False,
|
|
272
|
+
mode: AccessModeLiteral = "a",
|
|
273
|
+
parallel_safe: bool = False,
|
|
274
|
+
) -> Table:
|
|
275
|
+
"""Open a table from a Zarr store."""
|
|
276
|
+
handler = ZarrGroupHandler(
|
|
277
|
+
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
278
|
+
)
|
|
279
|
+
return ImplementedTables().get_table(
|
|
280
|
+
_get_table_type(handler), _get_table_version(handler), handler
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def write_table(
|
|
285
|
+
store: StoreOrGroup,
|
|
286
|
+
table: Table,
|
|
287
|
+
backend: str | None = None,
|
|
288
|
+
cache: bool = False,
|
|
289
|
+
mode: AccessModeLiteral = "a",
|
|
290
|
+
parallel_safe: bool = False,
|
|
291
|
+
) -> None:
|
|
292
|
+
"""Write a table to a Zarr store."""
|
|
293
|
+
handler = ZarrGroupHandler(
|
|
294
|
+
store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
|
|
295
|
+
)
|
|
296
|
+
table._set_backend(
|
|
297
|
+
handler=handler,
|
|
298
|
+
backend_name=backend,
|
|
299
|
+
)
|
|
300
|
+
table.consolidate()
|
ngio/tables/v1/__init__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Tables implementations for fractal_tables v1."""
|
|
2
2
|
|
|
3
|
-
from ngio.tables.v1.
|
|
4
|
-
from ngio.tables.v1.
|
|
5
|
-
from ngio.tables.v1.
|
|
3
|
+
from ngio.tables.v1._feature_table import FeatureTableV1
|
|
4
|
+
from ngio.tables.v1._generic_table import GenericTable
|
|
5
|
+
from ngio.tables.v1._masking_roi_table import MaskingROITableV1
|
|
6
|
+
from ngio.tables.v1._roi_table import RoiTableV1
|
|
6
7
|
|
|
7
|
-
__all__ = ["
|
|
8
|
+
__all__ = ["FeatureTableV1", "GenericTable", "MaskingROITableV1", "RoiTableV1"]
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""Implementation of the FeatureTableV1 class.
|
|
2
|
+
|
|
3
|
+
This class follows the roi_table specification at:
|
|
4
|
+
https://fractal-analytics-platform.github.io/fractal-tasks-core/tables/
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
from ngio.tables._validators import validate_index_key
|
|
13
|
+
from ngio.tables.backends import ImplementedTableBackends
|
|
14
|
+
from ngio.utils import ZarrGroupHandler
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RegionMeta(BaseModel):
|
|
18
|
+
"""Metadata for the region."""
|
|
19
|
+
|
|
20
|
+
path: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FeatureTableMeta(BaseModel):
|
|
24
|
+
"""Metadata for the ROI table."""
|
|
25
|
+
|
|
26
|
+
fractal_table_version: Literal["1"] = "1"
|
|
27
|
+
type: Literal["feature_table"] = "feature_table"
|
|
28
|
+
backend: str | None = None
|
|
29
|
+
region: RegionMeta | None = None
|
|
30
|
+
instance_key: str = "label"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FeatureTableV1:
|
|
34
|
+
"""Class to represent a feature table.
|
|
35
|
+
|
|
36
|
+
This can be used to load any table that does not have
|
|
37
|
+
a specific definition.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
dataframe: pd.DataFrame | None = None,
|
|
43
|
+
reference_label: str | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Initialize the GenericTable."""
|
|
46
|
+
if reference_label is None:
|
|
47
|
+
self._meta = FeatureTableMeta()
|
|
48
|
+
else:
|
|
49
|
+
path = f"../labels/{reference_label}"
|
|
50
|
+
self._meta = FeatureTableMeta(region=RegionMeta(path=path))
|
|
51
|
+
|
|
52
|
+
self._reference_label = reference_label
|
|
53
|
+
self._instance_key = "label"
|
|
54
|
+
if dataframe is None:
|
|
55
|
+
self._dataframe = None
|
|
56
|
+
else:
|
|
57
|
+
self._dataframe = validate_index_key(
|
|
58
|
+
dataframe, self._instance_key, overwrite=True
|
|
59
|
+
)
|
|
60
|
+
self._table_backend = None
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def type() -> str:
|
|
64
|
+
"""Return the type of the table."""
|
|
65
|
+
return "feature_table"
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def version() -> str:
|
|
69
|
+
"""The generic table does not have a version.
|
|
70
|
+
|
|
71
|
+
Since does not follow a specific schema.
|
|
72
|
+
"""
|
|
73
|
+
return "1"
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def backend_name(self) -> str | None:
|
|
77
|
+
"""Return the name of the backend."""
|
|
78
|
+
if self._table_backend is None:
|
|
79
|
+
return None
|
|
80
|
+
return self._table_backend.backend_name()
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def dataframe(self) -> pd.DataFrame:
|
|
84
|
+
"""Return the table as a DataFrame."""
|
|
85
|
+
if self._dataframe is None and self._table_backend is None:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
"The table does not have a DataFrame in memory nor a backend."
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if self._dataframe is None and self._table_backend is not None:
|
|
91
|
+
self._dataframe = self._table_backend.load_as_dataframe()
|
|
92
|
+
|
|
93
|
+
if self._dataframe is None:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
"The table does not have a DataFrame in memory nor a backend."
|
|
96
|
+
)
|
|
97
|
+
return self._dataframe
|
|
98
|
+
|
|
99
|
+
@dataframe.setter
|
|
100
|
+
def dataframe(self, dataframe: pd.DataFrame) -> None:
|
|
101
|
+
"""Set the table as a DataFrame."""
|
|
102
|
+
self._dataframe = dataframe
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def _from_handler(
|
|
106
|
+
cls, handler: ZarrGroupHandler, backend_name: str | None = None
|
|
107
|
+
) -> "FeatureTableV1":
|
|
108
|
+
"""Create a new ROI table from a Zarr group handler."""
|
|
109
|
+
meta = FeatureTableMeta(**handler.load_attrs())
|
|
110
|
+
instance_key = "label" if meta.instance_key is None else meta.instance_key
|
|
111
|
+
if backend_name is None:
|
|
112
|
+
backend = ImplementedTableBackends().get_backend(
|
|
113
|
+
backend_name=meta.backend,
|
|
114
|
+
group_handler=handler,
|
|
115
|
+
index_key=instance_key,
|
|
116
|
+
index_type="int",
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
backend = ImplementedTableBackends().get_backend(
|
|
120
|
+
backend_name=backend_name,
|
|
121
|
+
group_handler=handler,
|
|
122
|
+
index_key=instance_key,
|
|
123
|
+
index_type="int",
|
|
124
|
+
)
|
|
125
|
+
meta.backend = backend_name
|
|
126
|
+
|
|
127
|
+
if not backend.implements_dataframe:
|
|
128
|
+
raise ValueError("The backend does not implement the dataframe protocol.")
|
|
129
|
+
|
|
130
|
+
table = cls()
|
|
131
|
+
table._meta = meta
|
|
132
|
+
table._table_backend = backend
|
|
133
|
+
return table
|
|
134
|
+
|
|
135
|
+
def _set_backend(
|
|
136
|
+
self,
|
|
137
|
+
handler: ZarrGroupHandler,
|
|
138
|
+
backend_name: str | None = None,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Set the backend of the table."""
|
|
141
|
+
instance_key = "label" if self._instance_key is None else self._instance_key
|
|
142
|
+
backend = ImplementedTableBackends().get_backend(
|
|
143
|
+
backend_name=backend_name,
|
|
144
|
+
group_handler=handler,
|
|
145
|
+
index_key=instance_key,
|
|
146
|
+
index_type="int",
|
|
147
|
+
)
|
|
148
|
+
self._meta.backend = backend_name
|
|
149
|
+
self._table_backend = backend
|
|
150
|
+
|
|
151
|
+
def consolidate(self) -> None:
|
|
152
|
+
"""Write the current state of the table to the Zarr file."""
|
|
153
|
+
if self._table_backend is None:
|
|
154
|
+
raise ValueError(
|
|
155
|
+
"No backend set for the table. "
|
|
156
|
+
"Please add the table to a OME-Zarr Image before calling consolidate."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
self._table_backend.write_from_dataframe(
|
|
160
|
+
self.dataframe, metadata=self._meta.model_dump(exclude_none=True)
|
|
161
|
+
)
|