ngio 0.2.0b3__py3-none-any.whl → 0.2.2__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/hcs/plate.py CHANGED
@@ -3,18 +3,16 @@
3
3
  from ngio.images import OmeZarrContainer
4
4
  from ngio.ome_zarr_meta import (
5
5
  ImageInWellPath,
6
+ NgffVersions,
6
7
  NgioPlateMeta,
7
8
  NgioWellMeta,
8
9
  find_plate_meta_handler,
9
10
  find_well_meta_handler,
10
11
  get_plate_meta_handler,
11
12
  get_well_meta_handler,
13
+ path_in_well_validation,
12
14
  )
13
- from ngio.utils import (
14
- AccessModeLiteral,
15
- StoreOrGroup,
16
- ZarrGroupHandler,
17
- )
15
+ from ngio.utils import AccessModeLiteral, StoreOrGroup, ZarrGroupHandler
18
16
 
19
17
 
20
18
  # Mock lock class that does nothing
@@ -42,6 +40,10 @@ class OmeZarrWell:
42
40
  self._group_handler = group_handler
43
41
  self._meta_handler = find_well_meta_handler(group_handler)
44
42
 
43
+ def __repr__(self) -> str:
44
+ """Return a string representation of the well."""
45
+ return f"Well(#images: {len(self.paths())})"
46
+
45
47
  @property
46
48
  def meta_handler(self):
47
49
  """Return the metadata handler."""
@@ -52,6 +54,11 @@ class OmeZarrWell:
52
54
  """Return the metadata."""
53
55
  return self._meta_handler.meta
54
56
 
57
+ @property
58
+ def acquisition_ids(self) -> list[int]:
59
+ """Return the acquisitions ids in the well."""
60
+ return self.meta.acquisition_ids
61
+
55
62
  def paths(self, acquisition: int | None = None) -> list[str]:
56
63
  """Return the images paths in the well.
57
64
 
@@ -63,6 +70,97 @@ class OmeZarrWell:
63
70
  """
64
71
  return self.meta.paths(acquisition)
65
72
 
73
+ def get_image_store(self, image_path: str) -> StoreOrGroup:
74
+ """Get the image store from the well.
75
+
76
+ Args:
77
+ image_path (str): The path of the image.
78
+ """
79
+ return self._group_handler.get_group(image_path, create_mode=True)
80
+
81
+ def get_image_acquisition_id(self, image_path: str) -> int | None:
82
+ """Get the acquisition id of an image in the well.
83
+
84
+ Args:
85
+ image_path (str): The path of the image.
86
+
87
+ Returns:
88
+ int | None: The acquisition id of the image.
89
+ """
90
+ return self.meta.get_image_acquisition_id(image_path=image_path)
91
+
92
+ def get_image(self, image_path: str) -> OmeZarrContainer:
93
+ """Get an image from the well.
94
+
95
+ Args:
96
+ image_path (str): The path of the image.
97
+
98
+ Returns:
99
+ OmeZarrContainer: The image.
100
+ """
101
+ handler = self._group_handler.derive_handler(image_path)
102
+ return OmeZarrContainer(handler)
103
+
104
+ def _add_image(
105
+ self,
106
+ image_path: str,
107
+ acquisition_id: int | None = None,
108
+ strict: bool = True,
109
+ atomic: bool = False,
110
+ ) -> StoreOrGroup:
111
+ """Add an image to an ome-zarr well."""
112
+ image_path = path_in_well_validation(path=image_path)
113
+
114
+ if atomic:
115
+ well_lock = self._group_handler.lock
116
+ else:
117
+ well_lock = MockLock()
118
+
119
+ with well_lock:
120
+ meta = self.meta.add_image(
121
+ path=image_path, acquisition=acquisition_id, strict=strict
122
+ )
123
+ self.meta_handler.write_meta(meta)
124
+ self.meta_handler._group_handler.clean_cache()
125
+
126
+ return self._group_handler.get_group(image_path, create_mode=True)
127
+
128
+ def atomic_add_image(
129
+ self,
130
+ image_path: str,
131
+ acquisition_id: int | None = None,
132
+ strict: bool = True,
133
+ ) -> StoreOrGroup:
134
+ """Parallel safe version of add_image."""
135
+ return self._add_image(
136
+ image_path=image_path,
137
+ acquisition_id=acquisition_id,
138
+ atomic=True,
139
+ strict=strict,
140
+ )
141
+
142
+ def add_image(
143
+ self,
144
+ image_path: str,
145
+ acquisition_id: int | None = None,
146
+ strict: bool = True,
147
+ ) -> StoreOrGroup:
148
+ """Add an image to an ome-zarr well.
149
+
150
+ Args:
151
+ image_path (str): The path of the image.
152
+ acquisition_id (int | None): The acquisition id to filter the images.
153
+ strict (bool): Whether to check if the acquisition id is already exists
154
+ in the well. Defaults to True. If False this might lead to
155
+ acquision in a well that does not exist at the plate level.
156
+ """
157
+ return self._add_image(
158
+ image_path=image_path,
159
+ acquisition_id=acquisition_id,
160
+ atomic=False,
161
+ strict=strict,
162
+ )
163
+
66
164
 
67
165
  class OmeZarrPlate:
68
166
  """A class to handle the Plate Collection in an OME-Zarr file."""
@@ -78,7 +176,7 @@ class OmeZarrPlate:
78
176
 
79
177
  def __repr__(self) -> str:
80
178
  """Return a string representation of the plate."""
81
- return f"Plate([rows x columns] ({len(self.rows)} x {len(self.columns)})"
179
+ return f"Plate([rows x columns] ({len(self.rows)} x {len(self.columns)}))"
82
180
 
83
181
  @property
84
182
  def meta_handler(self):
@@ -106,25 +204,74 @@ class OmeZarrPlate:
106
204
  return self.meta.acquisitions_names
107
205
 
108
206
  @property
109
- def acquisitions_ids(self) -> list[int]:
207
+ def acquisition_ids(self) -> list[int]:
110
208
  """Return the acquisitions ids in the plate."""
111
- return self.meta.acquisitions_ids
209
+ return self.meta.acquisition_ids
112
210
 
113
- @property
114
- def wells_paths(self) -> list[str]:
115
- """Return the wells paths in the plate."""
116
- return self.meta.wells_paths
117
-
118
- def get_well_path(self, row: str, column: int | str) -> str:
211
+ def _well_path(self, row: str, column: int | str) -> str:
119
212
  """Return the well path in the plate."""
120
213
  return self.meta.get_well_path(row=row, column=column)
121
214
 
122
- def get_image_path(self, row: str, column: int | str, path: str) -> str:
215
+ def _image_path(self, row: str, column: int | str, path: str) -> str:
123
216
  """Return the image path in the plate."""
124
217
  well = self.get_well(row, column)
125
218
  if path not in well.paths():
126
219
  raise ValueError(f"Image {path} does not exist in well {row}{column}")
127
- return f"{self.get_well_path(row, column)}/{path}"
220
+ return f"{self._well_path(row, column)}/{path}"
221
+
222
+ def wells_paths(self) -> list[str]:
223
+ """Return the wells paths in the plate."""
224
+ return self.meta.wells_paths
225
+
226
+ def images_paths(self, acquisition: int | None = None) -> list[str]:
227
+ """Return the images paths in the plate.
228
+
229
+ If acquisition is None, return all images paths in the plate.
230
+ Else, return the images paths in the plate for the given acquisition.
231
+
232
+ Args:
233
+ acquisition (int | None): The acquisition id to filter the images.
234
+ """
235
+ images = []
236
+ for well_path, wells in self.get_wells().items():
237
+ for img_path in wells.paths(acquisition):
238
+ images.append(f"{well_path}/{img_path}")
239
+ return images
240
+
241
+ def well_images_paths(
242
+ self, row: str, column: int | str, acquisition: int | None = None
243
+ ) -> list[str]:
244
+ """Return the images paths in a well.
245
+
246
+ If acquisition is None, return all images paths in the well.
247
+ Else, return the images paths in the well for the given acquisition.
248
+
249
+ Args:
250
+ row (str): The row of the well.
251
+ column (int | str): The column of the well.
252
+ acquisition (int | None): The acquisition id to filter the images.
253
+ """
254
+ images = []
255
+ well = self.get_well(row=row, column=column)
256
+ for path in well.paths(acquisition):
257
+ images.append(self._image_path(row=row, column=column, path=path))
258
+ return images
259
+
260
+ def get_image_acquisition_id(
261
+ self, row: str, column: int | str, image_path: str
262
+ ) -> int | None:
263
+ """Get the acquisition id of an image in a well.
264
+
265
+ Args:
266
+ row (str): The row of the well.
267
+ column (int | str): The column of the well.
268
+ image_path (str): The path of the image.
269
+
270
+ Returns:
271
+ int | None: The acquisition id of the image.
272
+ """
273
+ well = self.get_well(row=row, column=column)
274
+ return well.get_image_acquisition_id(image_path=image_path)
128
275
 
129
276
  def get_well(self, row: str, column: int | str) -> OmeZarrWell:
130
277
  """Get a well from the plate.
@@ -136,36 +283,69 @@ class OmeZarrPlate:
136
283
  Returns:
137
284
  OmeZarrWell: The well.
138
285
  """
139
- well_path = self.meta.get_well_path(row=row, column=column)
286
+ well_path = self._well_path(row=row, column=column)
140
287
  group_handler = self._group_handler.derive_handler(well_path)
141
288
  return OmeZarrWell(group_handler)
142
289
 
143
290
  def get_wells(self) -> dict[str, OmeZarrWell]:
144
- """Get all wells in the plate."""
291
+ """Get all wells in the plate.
292
+
293
+ Returns:
294
+ dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
295
+ path and the value is the well object.
296
+ """
145
297
  wells = {}
146
- for well_path in self.wells_paths:
298
+ for well_path in self.wells_paths():
147
299
  group_handler = self._group_handler.derive_handler(well_path)
148
300
  well = OmeZarrWell(group_handler)
149
301
  wells[well_path] = well
150
302
  return wells
151
303
 
152
- def get_images(self, acquisition: int | None = None) -> list[OmeZarrContainer]:
304
+ def get_images(self, acquisition: int | None = None) -> dict[str, OmeZarrContainer]:
153
305
  """Get all images in the plate.
154
306
 
155
307
  Args:
156
308
  acquisition: The acquisition id to filter the images.
157
309
  """
158
- images = []
159
- for well_path, well in self.get_wells().items():
160
- for img_path in well.paths(acquisition):
161
- full_path = f"{well_path}/{img_path}"
162
- img_group_handler = self._group_handler.derive_handler(full_path)
163
- images.append(OmeZarrContainer(img_group_handler))
310
+ images = {}
311
+ for image_path in self.images_paths(acquisition):
312
+ img_group_handler = self._group_handler.derive_handler(image_path)
313
+ images[image_path] = OmeZarrContainer(img_group_handler)
164
314
  return images
165
315
 
316
+ def get_image(
317
+ self, row: str, column: int | str, image_path: str
318
+ ) -> OmeZarrContainer:
319
+ """Get an image from the plate.
320
+
321
+ Args:
322
+ row (str): The row of the well.
323
+ column (int | str): The column of the well.
324
+ image_path (str): The path of the image.
325
+
326
+ Returns:
327
+ OmeZarrContainer: The image.
328
+ """
329
+ image_path = self._image_path(row=row, column=column, path=image_path)
330
+ group_handler = self._group_handler.derive_handler(image_path)
331
+ return OmeZarrContainer(group_handler)
332
+
333
+ def get_image_store(
334
+ self, row: str, column: int | str, image_path: str
335
+ ) -> StoreOrGroup:
336
+ """Get the image store from the plate.
337
+
338
+ Args:
339
+ row (str): The row of the well.
340
+ column (int | str): The column of the well.
341
+ image_path (str): The path of the image.
342
+ """
343
+ well = self.get_well(row=row, column=column)
344
+ return well.get_image_store(image_path=image_path)
345
+
166
346
  def get_well_images(
167
347
  self, row: str, column: str | int, acquisition: int | None = None
168
- ) -> list[OmeZarrContainer]:
348
+ ) -> dict[str, OmeZarrContainer]:
169
349
  """Get all images in a well.
170
350
 
171
351
  Args:
@@ -173,28 +353,27 @@ class OmeZarrPlate:
173
353
  column: The column of the well.
174
354
  acquisition: The acquisition id to filter the images.
175
355
  """
176
- well_path = self.meta.get_well_path(row=row, column=column)
177
- group_handler = self._group_handler.derive_handler(well_path)
178
- well = OmeZarrWell(group_handler)
179
-
180
- images = []
181
- for path in well.paths(acquisition):
182
- image_path = f"{well_path}/{path}"
183
- group_handler = self._group_handler.derive_handler(image_path)
184
- images.append(OmeZarrContainer(group_handler))
185
-
356
+ images = {}
357
+ for image_paths in self.well_images_paths(
358
+ row=row, column=column, acquisition=acquisition
359
+ ):
360
+ group_handler = self._group_handler.derive_handler(image_paths)
361
+ images[image_paths] = OmeZarrContainer(group_handler)
186
362
  return images
187
363
 
188
364
  def _add_image(
189
365
  self,
190
366
  row: str,
191
367
  column: int | str,
192
- image_path: str,
368
+ image_path: str | None = None,
193
369
  acquisition_id: int | None = None,
194
370
  acquisition_name: str | None = None,
195
371
  atomic: bool = False,
196
- ) -> StoreOrGroup:
372
+ ) -> StoreOrGroup | None:
197
373
  """Add an image to an ome-zarr plate."""
374
+ if image_path is not None:
375
+ image_path = path_in_well_validation(path=image_path)
376
+
198
377
  if atomic:
199
378
  plate_lock = self._group_handler.lock
200
379
  else:
@@ -202,7 +381,11 @@ class OmeZarrPlate:
202
381
 
203
382
  with plate_lock:
204
383
  meta = self.meta
205
- meta = meta.add_well(row, column, acquisition_id, acquisition_name)
384
+ meta = meta.add_well(row=row, column=column)
385
+ if acquisition_id is not None:
386
+ meta = meta.add_acquisition(
387
+ acquisition_id=acquisition_id, acquisition_name=acquisition_name
388
+ )
206
389
  self.meta_handler.write_meta(meta)
207
390
  self.meta_handler._group_handler.clean_cache()
208
391
 
@@ -220,18 +403,25 @@ class OmeZarrPlate:
220
403
  # Initialize the well metadata
221
404
  # if the group is empty
222
405
  well_meta = NgioWellMeta.default_init()
223
- meta_handler = get_well_meta_handler(group_handler, version="0.4")
406
+ version = self.meta.plate.version
407
+ version = version if version is not None else "0.4"
408
+ meta_handler = get_well_meta_handler(group_handler, version=version)
224
409
  else:
225
410
  meta_handler = find_well_meta_handler(group_handler)
226
411
  well_meta = meta_handler.meta
227
412
 
228
413
  group_handler = self._group_handler.derive_handler(well_path)
229
414
 
230
- well_meta = well_meta.add_image(path=image_path, acquisition=acquisition_id)
415
+ if image_path is not None:
416
+ well_meta = well_meta.add_image(
417
+ path=image_path, acquisition=acquisition_id, strict=False
418
+ )
231
419
  meta_handler.write_meta(well_meta)
232
420
  meta_handler._group_handler.clean_cache()
233
421
 
234
- return group_handler.get_group(image_path, create_mode=True)
422
+ if image_path is not None:
423
+ return group_handler.get_group(image_path, create_mode=True)
424
+ return None
235
425
 
236
426
  def atomic_add_image(
237
427
  self,
@@ -242,7 +432,12 @@ class OmeZarrPlate:
242
432
  acquisition_name: str | None = None,
243
433
  ) -> StoreOrGroup:
244
434
  """Parallel safe version of add_image."""
245
- return self._add_image(
435
+ if image_path is None:
436
+ raise ValueError(
437
+ "Image path cannot be None for atomic add_image. "
438
+ "If your intent is to add a well, use add_well instead."
439
+ )
440
+ group = self._add_image(
246
441
  row=row,
247
442
  column=column,
248
443
  image_path=image_path,
@@ -250,6 +445,12 @@ class OmeZarrPlate:
250
445
  acquisition_name=acquisition_name,
251
446
  atomic=True,
252
447
  )
448
+ if group is None:
449
+ raise ValueError(
450
+ f"Some error occurred while adding image {image_path} "
451
+ f"to well {row}{column}."
452
+ )
453
+ return group
253
454
 
254
455
  def add_image(
255
456
  self,
@@ -260,7 +461,12 @@ class OmeZarrPlate:
260
461
  acquisition_name: str | None = None,
261
462
  ) -> StoreOrGroup:
262
463
  """Add an image to an ome-zarr plate."""
263
- return self._add_image(
464
+ if image_path is None:
465
+ raise ValueError(
466
+ "Image path cannot be None for atomic add_image. "
467
+ "If your intent is to add a well, use add_well instead."
468
+ )
469
+ group = self._add_image(
264
470
  row=row,
265
471
  column=column,
266
472
  image_path=image_path,
@@ -268,6 +474,68 @@ class OmeZarrPlate:
268
474
  acquisition_name=acquisition_name,
269
475
  atomic=False,
270
476
  )
477
+ if group is None:
478
+ raise ValueError(
479
+ f"Some error occurred while adding image {image_path} "
480
+ f"to well {row}{column}."
481
+ )
482
+ return group
483
+
484
+ def add_well(
485
+ self,
486
+ row: str,
487
+ column: int | str,
488
+ ) -> OmeZarrWell:
489
+ """Add a well to an ome-zarr plate."""
490
+ _ = self._add_image(
491
+ row=row,
492
+ column=column,
493
+ image_path=None,
494
+ acquisition_id=None,
495
+ acquisition_name=None,
496
+ atomic=False,
497
+ )
498
+ return self.get_well(row=row, column=column)
499
+
500
+ def add_column(
501
+ self,
502
+ column: int | str,
503
+ ) -> "OmeZarrPlate":
504
+ """Add a column to an ome-zarr plate."""
505
+ meta, _ = self.meta.add_column(column)
506
+ self.meta_handler.write_meta(meta)
507
+ self.meta_handler._group_handler.clean_cache()
508
+ return self
509
+
510
+ def add_row(
511
+ self,
512
+ row: str,
513
+ ) -> "OmeZarrPlate":
514
+ """Add a row to an ome-zarr plate."""
515
+ meta, _ = self.meta.add_row(row)
516
+ self.meta_handler.write_meta(meta)
517
+ self.meta_handler._group_handler.clean_cache()
518
+ return self
519
+
520
+ def add_acquisition(
521
+ self,
522
+ acquisition_id: int,
523
+ acquisition_name: str,
524
+ ) -> "OmeZarrPlate":
525
+ """Add an acquisition to an ome-zarr plate.
526
+
527
+ Be aware that this is not a parallel safe operation.
528
+
529
+ Args:
530
+ acquisition_id (int): The acquisition id.
531
+ acquisition_name (str): The acquisition name.
532
+ """
533
+ meta = self.meta.add_acquisition(
534
+ acquisition_id=acquisition_id, acquisition_name=acquisition_name
535
+ )
536
+ self.meta_handler.write_meta(meta)
537
+ self.meta_handler._group_handler.clean_cache()
538
+ return self
271
539
 
272
540
  def _remove_well(
273
541
  self,
@@ -338,8 +606,40 @@ class OmeZarrPlate:
338
606
  atomic=False,
339
607
  )
340
608
 
609
+ def derive_plate(
610
+ self,
611
+ store: StoreOrGroup,
612
+ plate_name: str | None = None,
613
+ version: NgffVersions = "0.4",
614
+ keep_acquisitions: bool = False,
615
+ cache: bool = False,
616
+ overwrite: bool = False,
617
+ parallel_safe: bool = True,
618
+ ) -> "OmeZarrPlate":
619
+ """Derive a new OME-Zarr plate from an existing one.
341
620
 
342
- def open_omezarr_plate(
621
+ Args:
622
+ store (StoreOrGroup): The Zarr store or group that stores the plate.
623
+ plate_name (str | None): The name of the new plate.
624
+ version (NgffVersion): The version of the new plate.
625
+ keep_acquisitions (bool): Whether to keep the acquisitions in the new plate.
626
+ cache (bool): Whether to use a cache for the zarr group metadata.
627
+ overwrite (bool): Whether to overwrite the existing plate.
628
+ parallel_safe (bool): Whether the group handler is parallel safe.
629
+ """
630
+ return derive_ome_zarr_plate(
631
+ ome_zarr_plate=self,
632
+ store=store,
633
+ plate_name=plate_name,
634
+ version=version,
635
+ keep_acquisitions=keep_acquisitions,
636
+ cache=cache,
637
+ overwrite=overwrite,
638
+ parallel_safe=parallel_safe,
639
+ )
640
+
641
+
642
+ def open_ome_zarr_plate(
343
643
  store: StoreOrGroup,
344
644
  cache: bool = False,
345
645
  mode: AccessModeLiteral = "r+",
@@ -360,26 +660,42 @@ def open_omezarr_plate(
360
660
  return OmeZarrPlate(group_handler)
361
661
 
362
662
 
663
+ def _create_empty_plate_from_meta(
664
+ store: StoreOrGroup,
665
+ meta: NgioPlateMeta,
666
+ version: NgffVersions = "0.4",
667
+ overwrite: bool = False,
668
+ ) -> ZarrGroupHandler:
669
+ """Create an empty OME-Zarr plate from metadata."""
670
+ mode = "w" if overwrite else "w-"
671
+ group_handler = ZarrGroupHandler(
672
+ store=store, cache=True, mode=mode, parallel_safe=False
673
+ )
674
+ meta_handler = get_plate_meta_handler(group_handler, version=version)
675
+ meta_handler.write_meta(meta)
676
+ return group_handler
677
+
678
+
363
679
  def create_empty_plate(
364
680
  store: StoreOrGroup,
365
681
  name: str,
366
682
  images: list[ImageInWellPath] | None = None,
367
- version: str = "0.4",
683
+ version: NgffVersions = "0.4",
368
684
  cache: bool = False,
369
685
  overwrite: bool = False,
370
686
  parallel_safe: bool = True,
371
687
  ) -> OmeZarrPlate:
372
688
  """Initialize and create an empty OME-Zarr plate."""
373
- mode = "w" if overwrite else "w-"
374
- group_handler = ZarrGroupHandler(
375
- store=store, cache=True, mode=mode, parallel_safe=False
376
- )
377
- meta_handler = get_plate_meta_handler(group_handler, version=version)
378
689
  plate_meta = NgioPlateMeta.default_init(
379
690
  name=name,
380
691
  version=version,
381
692
  )
382
- meta_handler.write_meta(plate_meta)
693
+ group_handler = _create_empty_plate_from_meta(
694
+ store=store,
695
+ meta=plate_meta,
696
+ version=version,
697
+ overwrite=overwrite,
698
+ )
383
699
 
384
700
  if images is not None:
385
701
  plate = OmeZarrPlate(group_handler)
@@ -391,7 +707,102 @@ def create_empty_plate(
391
707
  acquisition_id=image.acquisition_id,
392
708
  acquisition_name=image.acquisition_name,
393
709
  )
394
- return open_omezarr_plate(
710
+ return open_ome_zarr_plate(
711
+ store=store,
712
+ cache=cache,
713
+ mode="r+",
714
+ parallel_safe=parallel_safe,
715
+ )
716
+
717
+
718
+ def derive_ome_zarr_plate(
719
+ ome_zarr_plate: OmeZarrPlate,
720
+ store: StoreOrGroup,
721
+ plate_name: str | None = None,
722
+ version: NgffVersions = "0.4",
723
+ keep_acquisitions: bool = False,
724
+ cache: bool = False,
725
+ overwrite: bool = False,
726
+ parallel_safe: bool = True,
727
+ ) -> OmeZarrPlate:
728
+ """Derive a new OME-Zarr plate from an existing one.
729
+
730
+ Args:
731
+ ome_zarr_plate (OmeZarrPlate): The existing OME-Zarr plate.
732
+ store (StoreOrGroup): The Zarr store or group that stores the plate.
733
+ plate_name (str | None): The name of the new plate.
734
+ version (NgffVersion): The version of the new plate.
735
+ keep_acquisitions (bool): Whether to keep the acquisitions in the new plate.
736
+ cache (bool): Whether to use a cache for the zarr group metadata.
737
+ overwrite (bool): Whether to overwrite the existing plate.
738
+ parallel_safe (bool): Whether the group handler is parallel safe.
739
+ """
740
+ if plate_name is None:
741
+ plate_name = ome_zarr_plate.meta.plate.name
742
+
743
+ new_meta = ome_zarr_plate.meta.derive(
744
+ name=plate_name,
745
+ version=version,
746
+ keep_acquisitions=keep_acquisitions,
747
+ )
748
+ _ = _create_empty_plate_from_meta(
749
+ store=store,
750
+ meta=new_meta,
751
+ overwrite=overwrite,
752
+ version=version,
753
+ )
754
+ return open_ome_zarr_plate(
755
+ store=store,
756
+ cache=cache,
757
+ mode="r+",
758
+ parallel_safe=parallel_safe,
759
+ )
760
+
761
+
762
+ def open_ome_zarr_well(
763
+ store: StoreOrGroup,
764
+ cache: bool = False,
765
+ mode: AccessModeLiteral = "r+",
766
+ parallel_safe: bool = True,
767
+ ) -> OmeZarrWell:
768
+ """Open an OME-Zarr well.
769
+
770
+ Args:
771
+ store (StoreOrGroup): The Zarr store or group that stores the plate.
772
+ cache (bool): Whether to use a cache for the zarr group metadata.
773
+ mode (AccessModeLiteral): The access mode for the image. Defaults to "r+".
774
+ parallel_safe (bool): Whether the group handler is parallel safe.
775
+ """
776
+ group_handler = ZarrGroupHandler(
777
+ store=store, cache=cache, mode=mode, parallel_safe=parallel_safe
778
+ )
779
+ return OmeZarrWell(group_handler)
780
+
781
+
782
+ def create_empty_well(
783
+ store: StoreOrGroup,
784
+ version: NgffVersions = "0.4",
785
+ cache: bool = False,
786
+ overwrite: bool = False,
787
+ parallel_safe: bool = True,
788
+ ) -> OmeZarrWell:
789
+ """Create an empty OME-Zarr well.
790
+
791
+ Args:
792
+ store (StoreOrGroup): The Zarr store or group that stores the well.
793
+ version (NgffVersion): The version of the new well.
794
+ cache (bool): Whether to use a cache for the zarr group metadata.
795
+ overwrite (bool): Whether to overwrite the existing well.
796
+ parallel_safe (bool): Whether the group handler is parallel safe.
797
+ """
798
+ group_handler = ZarrGroupHandler(
799
+ store=store, cache=True, mode="w" if overwrite else "w-", parallel_safe=False
800
+ )
801
+ meta_handler = get_well_meta_handler(group_handler, version=version)
802
+ meta = NgioWellMeta.default_init()
803
+ meta_handler.write_meta(meta)
804
+
805
+ return open_ome_zarr_well(
395
806
  store=store,
396
807
  cache=cache,
397
808
  mode="r+",