ngio 0.5.0b6__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 +69 -0
- ngio/common/__init__.py +28 -0
- ngio/common/_dimensions.py +335 -0
- ngio/common/_masking_roi.py +153 -0
- ngio/common/_pyramid.py +408 -0
- ngio/common/_roi.py +315 -0
- ngio/common/_synt_images_utils.py +101 -0
- ngio/common/_zoom.py +188 -0
- ngio/experimental/__init__.py +5 -0
- ngio/experimental/iterators/__init__.py +15 -0
- ngio/experimental/iterators/_abstract_iterator.py +390 -0
- ngio/experimental/iterators/_feature.py +189 -0
- ngio/experimental/iterators/_image_processing.py +130 -0
- ngio/experimental/iterators/_mappers.py +48 -0
- ngio/experimental/iterators/_rois_utils.py +126 -0
- ngio/experimental/iterators/_segmentation.py +235 -0
- ngio/hcs/__init__.py +19 -0
- ngio/hcs/_plate.py +1354 -0
- ngio/images/__init__.py +44 -0
- ngio/images/_abstract_image.py +967 -0
- ngio/images/_create_synt_container.py +132 -0
- ngio/images/_create_utils.py +423 -0
- ngio/images/_image.py +926 -0
- ngio/images/_label.py +411 -0
- ngio/images/_masked_image.py +531 -0
- ngio/images/_ome_zarr_container.py +1237 -0
- ngio/images/_table_ops.py +471 -0
- ngio/io_pipes/__init__.py +75 -0
- ngio/io_pipes/_io_pipes.py +361 -0
- ngio/io_pipes/_io_pipes_masked.py +488 -0
- ngio/io_pipes/_io_pipes_roi.py +146 -0
- ngio/io_pipes/_io_pipes_types.py +56 -0
- ngio/io_pipes/_match_shape.py +377 -0
- ngio/io_pipes/_ops_axes.py +344 -0
- ngio/io_pipes/_ops_slices.py +411 -0
- ngio/io_pipes/_ops_slices_utils.py +199 -0
- ngio/io_pipes/_ops_transforms.py +104 -0
- ngio/io_pipes/_zoom_transform.py +180 -0
- ngio/ome_zarr_meta/__init__.py +65 -0
- ngio/ome_zarr_meta/_meta_handlers.py +536 -0
- ngio/ome_zarr_meta/ngio_specs/__init__.py +77 -0
- ngio/ome_zarr_meta/ngio_specs/_axes.py +515 -0
- ngio/ome_zarr_meta/ngio_specs/_channels.py +462 -0
- ngio/ome_zarr_meta/ngio_specs/_dataset.py +89 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +539 -0
- ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +438 -0
- ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +122 -0
- ngio/ome_zarr_meta/v04/__init__.py +27 -0
- ngio/ome_zarr_meta/v04/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v04/_v04_spec.py +473 -0
- ngio/ome_zarr_meta/v05/__init__.py +27 -0
- ngio/ome_zarr_meta/v05/_custom_models.py +18 -0
- ngio/ome_zarr_meta/v05/_v05_spec.py +511 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/mask.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/nuclei.png +0 -0
- ngio/resources/20200812-CardiomyocyteDifferentiation14-Cycle1_B03/raw.jpg +0 -0
- ngio/resources/__init__.py +55 -0
- ngio/resources/resource_model.py +36 -0
- ngio/tables/__init__.py +43 -0
- ngio/tables/_abstract_table.py +270 -0
- ngio/tables/_tables_container.py +449 -0
- ngio/tables/backends/__init__.py +57 -0
- ngio/tables/backends/_abstract_backend.py +240 -0
- ngio/tables/backends/_anndata.py +139 -0
- ngio/tables/backends/_anndata_utils.py +90 -0
- ngio/tables/backends/_csv.py +19 -0
- ngio/tables/backends/_json.py +92 -0
- ngio/tables/backends/_parquet.py +19 -0
- ngio/tables/backends/_py_arrow_backends.py +222 -0
- ngio/tables/backends/_table_backends.py +226 -0
- ngio/tables/backends/_utils.py +608 -0
- ngio/tables/v1/__init__.py +23 -0
- ngio/tables/v1/_condition_table.py +71 -0
- ngio/tables/v1/_feature_table.py +125 -0
- ngio/tables/v1/_generic_table.py +49 -0
- ngio/tables/v1/_roi_table.py +575 -0
- ngio/transforms/__init__.py +5 -0
- ngio/transforms/_zoom.py +19 -0
- ngio/utils/__init__.py +45 -0
- ngio/utils/_cache.py +48 -0
- ngio/utils/_datasets.py +165 -0
- ngio/utils/_errors.py +37 -0
- ngio/utils/_fractal_fsspec_store.py +42 -0
- ngio/utils/_zarr_utils.py +534 -0
- ngio-0.5.0b6.dist-info/METADATA +148 -0
- ngio-0.5.0b6.dist-info/RECORD +88 -0
- ngio-0.5.0b6.dist-info/WHEEL +4 -0
- ngio-0.5.0b6.dist-info/licenses/LICENSE +28 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
"""HCS (High Content Screening) specific metadata classes for NGIO."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
from ome_zarr_models.common.plate import (
|
|
7
|
+
Acquisition,
|
|
8
|
+
Column,
|
|
9
|
+
PlateBase,
|
|
10
|
+
Row,
|
|
11
|
+
WellInPlate,
|
|
12
|
+
)
|
|
13
|
+
from ome_zarr_models.common.well_types import WellImage as WellImageCommon
|
|
14
|
+
from pydantic import BaseModel, SkipValidation, field_serializer
|
|
15
|
+
|
|
16
|
+
from ngio.ome_zarr_meta.ngio_specs._ngio_image import DefaultNgffVersion, NgffVersions
|
|
17
|
+
from ngio.utils import NgioValueError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def path_in_well_validation(path: str) -> str:
|
|
21
|
+
"""Validate the path in the well."""
|
|
22
|
+
# Check if the value contains only alphanumeric characters
|
|
23
|
+
if not path.isalnum():
|
|
24
|
+
warnings.warn(
|
|
25
|
+
f"Path '{path}' contains non-alphanumeric characters. "
|
|
26
|
+
"This may cause issues with some tools. "
|
|
27
|
+
"Consider using only alphanumeric characters in the path.",
|
|
28
|
+
stacklevel=2,
|
|
29
|
+
)
|
|
30
|
+
return path
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ImageInWellPath(BaseModel):
|
|
34
|
+
"""Image in a well."""
|
|
35
|
+
|
|
36
|
+
row: str
|
|
37
|
+
column: str | int
|
|
38
|
+
path: str
|
|
39
|
+
acquisition_id: int | None = None
|
|
40
|
+
acquisition_name: str | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CustomWellImage(WellImageCommon):
|
|
44
|
+
path: Annotated[str, SkipValidation]
|
|
45
|
+
|
|
46
|
+
@field_serializer("path")
|
|
47
|
+
def serialize_path(self, value: str) -> str:
|
|
48
|
+
"""Custom serialization for the path."""
|
|
49
|
+
return path_in_well_validation(value)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class PlateWithVersion(PlateBase):
|
|
53
|
+
version: NgffVersions
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NgioWellMeta(BaseModel):
|
|
57
|
+
"""HCS well metadata."""
|
|
58
|
+
|
|
59
|
+
images: list[CustomWellImage] # type: ignore (override of WellMeta04.images)
|
|
60
|
+
version: NgffVersions
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def default_init(
|
|
64
|
+
cls,
|
|
65
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
66
|
+
) -> "NgioWellMeta":
|
|
67
|
+
well = cls(images=[], version=ngff_version)
|
|
68
|
+
return well
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def acquisition_ids(self) -> list[int]:
|
|
72
|
+
"""Return the acquisition ids in the well."""
|
|
73
|
+
acquisitions = []
|
|
74
|
+
for images in self.images:
|
|
75
|
+
if (
|
|
76
|
+
images.acquisition is not None
|
|
77
|
+
and images.acquisition not in acquisitions
|
|
78
|
+
):
|
|
79
|
+
acquisitions.append(images.acquisition)
|
|
80
|
+
return acquisitions
|
|
81
|
+
|
|
82
|
+
def get_image_acquisition_id(self, image_path: str) -> int | None:
|
|
83
|
+
"""Return the acquisition id for the given image path."""
|
|
84
|
+
for images in self.images:
|
|
85
|
+
if images.path == image_path:
|
|
86
|
+
return images.acquisition
|
|
87
|
+
raise NgioValueError(f"Image at path {image_path} not found in the well.")
|
|
88
|
+
|
|
89
|
+
def paths(self, acquisition: int | None = None) -> list[str]:
|
|
90
|
+
"""Return the images paths in the well.
|
|
91
|
+
|
|
92
|
+
If acquisition is None, return all images paths in the well.
|
|
93
|
+
Else, return the images paths in the well for the given acquisition.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
acquisition (int | None): The acquisition id to filter the images.
|
|
97
|
+
"""
|
|
98
|
+
if acquisition is None:
|
|
99
|
+
return [images.path for images in self.images]
|
|
100
|
+
return [
|
|
101
|
+
images.path for images in self.images if images.acquisition == acquisition
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
def add_image(
|
|
105
|
+
self, path: str, acquisition: int | None = None, strict: bool = True
|
|
106
|
+
) -> "NgioWellMeta":
|
|
107
|
+
"""Add an image to the well.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
path (str): The path of the image.
|
|
111
|
+
acquisition (int | None): The acquisition id of the image.
|
|
112
|
+
strict (bool): If True, check if the image already exists in the well.
|
|
113
|
+
If False, do not check if the image already exists in the well.
|
|
114
|
+
"""
|
|
115
|
+
list_of_images = self.images
|
|
116
|
+
for image in list_of_images:
|
|
117
|
+
if image.path == path:
|
|
118
|
+
raise NgioValueError(
|
|
119
|
+
f"Image at path {path} already exists in the well."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
strict
|
|
124
|
+
and (acquisition is not None)
|
|
125
|
+
and (acquisition not in self.acquisition_ids)
|
|
126
|
+
):
|
|
127
|
+
raise NgioValueError(
|
|
128
|
+
f"Acquisition ID {acquisition} not found in well. "
|
|
129
|
+
"Please add it to the plate metadata first."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
new_image = CustomWellImage(path=path, acquisition=acquisition)
|
|
133
|
+
list_of_images.append(new_image)
|
|
134
|
+
return NgioWellMeta(images=list_of_images, version=self.version)
|
|
135
|
+
|
|
136
|
+
def remove_image(self, path: str) -> "NgioWellMeta":
|
|
137
|
+
"""Remove an image from the well.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
path (str): The path of the image.
|
|
141
|
+
"""
|
|
142
|
+
list_of_images = self.images
|
|
143
|
+
for image in list_of_images:
|
|
144
|
+
if image.path == path:
|
|
145
|
+
list_of_images.remove(image)
|
|
146
|
+
return NgioWellMeta(images=list_of_images, version=self.version)
|
|
147
|
+
raise NgioValueError(f"Image at path {path} not found in the well.")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _stringify_column(column: str | int) -> str:
|
|
151
|
+
"""Convert the column to a string.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
column (str | int): The column to convert.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
str: The column as a string.
|
|
158
|
+
"""
|
|
159
|
+
if isinstance(column, str):
|
|
160
|
+
return column
|
|
161
|
+
|
|
162
|
+
# Maybe we should pad the column with zeros
|
|
163
|
+
return str(column)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _find_row_index(rows: list[str], row: str) -> int | None:
|
|
167
|
+
try:
|
|
168
|
+
return rows.index(row)
|
|
169
|
+
except ValueError:
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _find_column_index(columns: list[str], column: str | int) -> int | None:
|
|
174
|
+
_num_columns = [int(columns) for columns in columns]
|
|
175
|
+
column = int(column)
|
|
176
|
+
try:
|
|
177
|
+
return _num_columns.index(column)
|
|
178
|
+
except ValueError:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _relabel_wells(
|
|
183
|
+
wells: list[WellInPlate], rows: list[Row], columns: list[Column]
|
|
184
|
+
) -> list[WellInPlate]:
|
|
185
|
+
new_wells = []
|
|
186
|
+
_rows = [row.name for row in rows]
|
|
187
|
+
_columns = [column.name for column in columns]
|
|
188
|
+
for well in wells:
|
|
189
|
+
row, column = well.path.split("/")
|
|
190
|
+
row_idx = _find_row_index(_rows, row)
|
|
191
|
+
column_idx = _find_column_index(_columns, column)
|
|
192
|
+
|
|
193
|
+
if row_idx is None:
|
|
194
|
+
raise NgioValueError(f"Row {row} not found in the plate.")
|
|
195
|
+
if column_idx is None:
|
|
196
|
+
raise NgioValueError(f"Column {column} not found in the plate.")
|
|
197
|
+
|
|
198
|
+
new_wells.append(
|
|
199
|
+
WellInPlate(
|
|
200
|
+
path=well.path,
|
|
201
|
+
rowIndex=row_idx,
|
|
202
|
+
columnIndex=column_idx,
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
return new_wells
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class NgioPlateMeta(BaseModel):
|
|
210
|
+
"""HCS plate metadata."""
|
|
211
|
+
|
|
212
|
+
plate: PlateWithVersion
|
|
213
|
+
version: NgffVersions
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def default_init(
|
|
217
|
+
cls,
|
|
218
|
+
images: list[ImageInWellPath] | None = None,
|
|
219
|
+
name: str | None = None,
|
|
220
|
+
ngff_version: NgffVersions = DefaultNgffVersion,
|
|
221
|
+
) -> "NgioPlateMeta":
|
|
222
|
+
plate = cls(
|
|
223
|
+
plate=PlateWithVersion(
|
|
224
|
+
rows=[],
|
|
225
|
+
columns=[],
|
|
226
|
+
acquisitions=None,
|
|
227
|
+
wells=[],
|
|
228
|
+
field_count=None,
|
|
229
|
+
name=name,
|
|
230
|
+
version=ngff_version,
|
|
231
|
+
),
|
|
232
|
+
version=ngff_version,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
if images is None:
|
|
236
|
+
return plate
|
|
237
|
+
|
|
238
|
+
for image in images:
|
|
239
|
+
plate = plate.add_well(
|
|
240
|
+
row=image.row,
|
|
241
|
+
column=image.column,
|
|
242
|
+
)
|
|
243
|
+
if image.acquisition_id is not None:
|
|
244
|
+
plate = plate.add_acquisition(
|
|
245
|
+
acquisition_id=image.acquisition_id,
|
|
246
|
+
acquisition_name=image.acquisition_name,
|
|
247
|
+
)
|
|
248
|
+
return plate
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def columns(self) -> list[str]:
|
|
252
|
+
"""Returns the list of columns in the plate."""
|
|
253
|
+
return [columns.name for columns in self.plate.columns]
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def rows(self) -> list[str]:
|
|
257
|
+
"""Returns the list of rows in the plate."""
|
|
258
|
+
return [rows.name for rows in self.plate.rows]
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def acquisitions_names(self) -> list[str | None]:
|
|
262
|
+
"""Return the acquisitions in the plate."""
|
|
263
|
+
if self.plate.acquisitions is None:
|
|
264
|
+
return []
|
|
265
|
+
return [acquisitions.name for acquisitions in self.plate.acquisitions]
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def acquisition_ids(self) -> list[int]:
|
|
269
|
+
"""Return the acquisitions ids in the plate."""
|
|
270
|
+
if self.plate.acquisitions is None:
|
|
271
|
+
return []
|
|
272
|
+
return [acquisitions.id for acquisitions in self.plate.acquisitions]
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def wells_paths(self) -> list[str]:
|
|
276
|
+
"""Return the wells paths in the plate."""
|
|
277
|
+
return [wells.path for wells in self.plate.wells]
|
|
278
|
+
|
|
279
|
+
def get_well_path(self, row: str, column: str | int) -> str:
|
|
280
|
+
"""Return the well path for the given row and column.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
row (str): The row of the well.
|
|
284
|
+
column (str | int): The column of the well.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
str: The path of the well.
|
|
288
|
+
"""
|
|
289
|
+
if row not in self.rows:
|
|
290
|
+
raise NgioValueError(
|
|
291
|
+
f"Row {row} not found in the plate. Available rows are {self.rows}."
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
row_idx = self.rows.index(row)
|
|
295
|
+
|
|
296
|
+
_num_columns = [int(columns) for columns in self.columns]
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
_column = int(column)
|
|
300
|
+
except ValueError:
|
|
301
|
+
raise NgioValueError(
|
|
302
|
+
f"Column {column} must be an integer or convertible to an integer."
|
|
303
|
+
) from None
|
|
304
|
+
|
|
305
|
+
column_idx = _num_columns.index(_column)
|
|
306
|
+
|
|
307
|
+
for well in self.plate.wells:
|
|
308
|
+
if well.columnIndex == column_idx and well.rowIndex == row_idx:
|
|
309
|
+
return well.path
|
|
310
|
+
|
|
311
|
+
raise NgioValueError(
|
|
312
|
+
f"Well at row {row} and column {column} not found in the plate."
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def add_row(self, row: str) -> "tuple[NgioPlateMeta, int]":
|
|
316
|
+
"""Add a row to the plate.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
row (str): The row to add.
|
|
320
|
+
"""
|
|
321
|
+
relabel_wells = False
|
|
322
|
+
|
|
323
|
+
row_names = self.rows
|
|
324
|
+
row_idx = _find_row_index(row_names, row)
|
|
325
|
+
if row_idx is not None:
|
|
326
|
+
# Nothing to do
|
|
327
|
+
return self, row_idx
|
|
328
|
+
|
|
329
|
+
row_names.append(row)
|
|
330
|
+
row_names.sort()
|
|
331
|
+
row_idx = row_names.index(row)
|
|
332
|
+
relabel_wells = True
|
|
333
|
+
|
|
334
|
+
rows = [Row(name=row) for row in row_names]
|
|
335
|
+
|
|
336
|
+
if relabel_wells:
|
|
337
|
+
wells = _relabel_wells(self.plate.wells, rows, self.plate.columns)
|
|
338
|
+
else:
|
|
339
|
+
wells = self.plate.wells
|
|
340
|
+
|
|
341
|
+
new_plate = PlateWithVersion(
|
|
342
|
+
rows=rows,
|
|
343
|
+
columns=self.plate.columns,
|
|
344
|
+
acquisitions=self.plate.acquisitions,
|
|
345
|
+
wells=wells,
|
|
346
|
+
field_count=self.plate.field_count,
|
|
347
|
+
name=self.plate.name,
|
|
348
|
+
version=self.version,
|
|
349
|
+
)
|
|
350
|
+
return NgioPlateMeta(plate=new_plate, version=self.version), row_idx
|
|
351
|
+
|
|
352
|
+
def add_column(self, column: str | int) -> "tuple[NgioPlateMeta, int]":
|
|
353
|
+
"""Add a column to the plate.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
column (str | int): The column to add.
|
|
357
|
+
"""
|
|
358
|
+
relabel_wells = False
|
|
359
|
+
|
|
360
|
+
columns_names = self.columns
|
|
361
|
+
column_idx = _find_column_index(columns_names, column)
|
|
362
|
+
if column_idx is not None:
|
|
363
|
+
# Nothing to do
|
|
364
|
+
return self, column_idx
|
|
365
|
+
|
|
366
|
+
columns_names.append(_stringify_column(column))
|
|
367
|
+
# sort as numbers
|
|
368
|
+
columns_names.sort(key=lambda x: int(x))
|
|
369
|
+
column_idx = columns_names.index(_stringify_column(column))
|
|
370
|
+
relabel_wells = True
|
|
371
|
+
|
|
372
|
+
columns = [Column(name=column) for column in columns_names]
|
|
373
|
+
|
|
374
|
+
if relabel_wells:
|
|
375
|
+
wells = _relabel_wells(self.plate.wells, self.plate.rows, columns)
|
|
376
|
+
else:
|
|
377
|
+
wells = self.plate.wells
|
|
378
|
+
|
|
379
|
+
new_plate = PlateWithVersion(
|
|
380
|
+
rows=self.plate.rows,
|
|
381
|
+
columns=columns,
|
|
382
|
+
acquisitions=self.plate.acquisitions,
|
|
383
|
+
wells=wells,
|
|
384
|
+
field_count=self.plate.field_count,
|
|
385
|
+
name=self.plate.name,
|
|
386
|
+
version=self.version,
|
|
387
|
+
)
|
|
388
|
+
return NgioPlateMeta(plate=new_plate, version=self.version), column_idx
|
|
389
|
+
|
|
390
|
+
def add_well(
|
|
391
|
+
self,
|
|
392
|
+
row: str,
|
|
393
|
+
column: str | int,
|
|
394
|
+
) -> "NgioPlateMeta":
|
|
395
|
+
"""Add an image to the well.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
row (str): The row of the well.
|
|
399
|
+
column (str | int): The column of the well.
|
|
400
|
+
"""
|
|
401
|
+
plate, row_idx = self.add_row(row=row)
|
|
402
|
+
plate, column_idx = plate.add_column(column=column)
|
|
403
|
+
|
|
404
|
+
wells = plate.plate.wells
|
|
405
|
+
for well_obj in wells:
|
|
406
|
+
if well_obj.rowIndex == row_idx and well_obj.columnIndex == column_idx:
|
|
407
|
+
break
|
|
408
|
+
else:
|
|
409
|
+
wells.append(
|
|
410
|
+
WellInPlate(
|
|
411
|
+
path=f"{row}/{_stringify_column(column)}",
|
|
412
|
+
rowIndex=row_idx,
|
|
413
|
+
columnIndex=column_idx,
|
|
414
|
+
)
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
new_plate = PlateWithVersion(
|
|
418
|
+
rows=plate.plate.rows,
|
|
419
|
+
columns=plate.plate.columns,
|
|
420
|
+
acquisitions=plate.plate.acquisitions,
|
|
421
|
+
wells=wells,
|
|
422
|
+
field_count=plate.plate.field_count,
|
|
423
|
+
name=plate.plate.name,
|
|
424
|
+
version=plate.version,
|
|
425
|
+
)
|
|
426
|
+
return NgioPlateMeta(plate=new_plate, version=plate.version)
|
|
427
|
+
|
|
428
|
+
def add_acquisition(
|
|
429
|
+
self,
|
|
430
|
+
acquisition_id: int,
|
|
431
|
+
acquisition_name: str | None = None,
|
|
432
|
+
**acquisition_kwargs,
|
|
433
|
+
) -> "NgioPlateMeta":
|
|
434
|
+
"""Add an acquisition to the plate.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
acquisition_id (int): The acquisition id of the well.
|
|
438
|
+
acquisition_name (str | None): The acquisition name of the well.
|
|
439
|
+
**acquisition_kwargs: Additional acquisition metadata.
|
|
440
|
+
"""
|
|
441
|
+
acquisitions = self.plate.acquisitions
|
|
442
|
+
if acquisitions is None:
|
|
443
|
+
acquisitions = []
|
|
444
|
+
|
|
445
|
+
for acquisition_obj in acquisitions:
|
|
446
|
+
if acquisition_obj.id == acquisition_id:
|
|
447
|
+
# If the acquisition already exists
|
|
448
|
+
# Nothing to do
|
|
449
|
+
# Maybe we should update the acquisition name and kwargs
|
|
450
|
+
return self
|
|
451
|
+
|
|
452
|
+
acquisitions.append(
|
|
453
|
+
Acquisition(id=acquisition_id, name=acquisition_name, **acquisition_kwargs)
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
new_plate = PlateWithVersion(
|
|
457
|
+
rows=self.plate.rows,
|
|
458
|
+
columns=self.plate.columns,
|
|
459
|
+
acquisitions=acquisitions,
|
|
460
|
+
wells=self.plate.wells,
|
|
461
|
+
field_count=self.plate.field_count,
|
|
462
|
+
name=self.plate.name,
|
|
463
|
+
version=self.version,
|
|
464
|
+
)
|
|
465
|
+
return NgioPlateMeta(plate=new_plate, version=self.version)
|
|
466
|
+
|
|
467
|
+
def remove_well(self, row: str, column: str | int) -> "NgioPlateMeta":
|
|
468
|
+
"""Remove a well from the plate.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
row (str): The row of the well.
|
|
472
|
+
column (str | int): The column of the well.
|
|
473
|
+
"""
|
|
474
|
+
row_idx = _find_row_index(self.rows, row)
|
|
475
|
+
if row_idx is None:
|
|
476
|
+
raise NgioValueError(f"Row {row} not found in the plate.")
|
|
477
|
+
|
|
478
|
+
column_idx = _find_column_index(self.columns, column)
|
|
479
|
+
if column_idx is None:
|
|
480
|
+
raise NgioValueError(f"Column {column} not found in the plate.")
|
|
481
|
+
|
|
482
|
+
wells = self.plate.wells
|
|
483
|
+
for well_obj in wells:
|
|
484
|
+
if well_obj.rowIndex == row_idx and well_obj.columnIndex == column_idx:
|
|
485
|
+
wells.remove(well_obj)
|
|
486
|
+
break
|
|
487
|
+
else:
|
|
488
|
+
raise NgioValueError(
|
|
489
|
+
f"Well at row {row} and column {column} not found in the plate."
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
new_plate = PlateWithVersion(
|
|
493
|
+
rows=self.plate.rows,
|
|
494
|
+
columns=self.plate.columns,
|
|
495
|
+
acquisitions=self.plate.acquisitions,
|
|
496
|
+
wells=wells,
|
|
497
|
+
field_count=self.plate.field_count,
|
|
498
|
+
name=self.plate.name,
|
|
499
|
+
version=self.version,
|
|
500
|
+
)
|
|
501
|
+
return NgioPlateMeta(plate=new_plate, version=self.version)
|
|
502
|
+
|
|
503
|
+
def derive(
|
|
504
|
+
self,
|
|
505
|
+
name: str | None = None,
|
|
506
|
+
ngff_version: NgffVersions | None = None,
|
|
507
|
+
keep_acquisitions: bool = False,
|
|
508
|
+
) -> "NgioPlateMeta":
|
|
509
|
+
"""Derive the plate metadata.
|
|
510
|
+
|
|
511
|
+
Args:
|
|
512
|
+
name (str): The name of the derived plate.
|
|
513
|
+
ngff_version (NgffVersion | None): The version of the derived plate.
|
|
514
|
+
If None, use the version of the original plate.
|
|
515
|
+
keep_acquisitions (bool): If True, keep the acquisitions in the plate.
|
|
516
|
+
"""
|
|
517
|
+
columns = self.plate.columns
|
|
518
|
+
rows = self.plate.rows
|
|
519
|
+
|
|
520
|
+
if keep_acquisitions:
|
|
521
|
+
acquisitions = self.plate.acquisitions
|
|
522
|
+
else:
|
|
523
|
+
acquisitions = None
|
|
524
|
+
|
|
525
|
+
if ngff_version is None:
|
|
526
|
+
ngff_version = self.version
|
|
527
|
+
|
|
528
|
+
return NgioPlateMeta(
|
|
529
|
+
plate=PlateWithVersion(
|
|
530
|
+
rows=rows,
|
|
531
|
+
columns=columns,
|
|
532
|
+
acquisitions=acquisitions,
|
|
533
|
+
wells=[],
|
|
534
|
+
field_count=self.plate.field_count,
|
|
535
|
+
name=name,
|
|
536
|
+
version=ngff_version,
|
|
537
|
+
),
|
|
538
|
+
version=ngff_version,
|
|
539
|
+
)
|