ngio 0.2.8__py3-none-any.whl → 0.3.0__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.
Files changed (39) hide show
  1. ngio/common/__init__.py +16 -0
  2. ngio/common/_array_pipe.py +50 -27
  3. ngio/common/_table_ops.py +471 -0
  4. ngio/hcs/__init__.py +1 -1
  5. ngio/hcs/{plate.py → _plate.py} +451 -78
  6. ngio/images/__init__.py +3 -3
  7. ngio/images/{image.py → _image.py} +26 -21
  8. ngio/images/{label.py → _label.py} +6 -4
  9. ngio/images/{masked_image.py → _masked_image.py} +2 -2
  10. ngio/images/{ome_zarr_container.py → _ome_zarr_container.py} +152 -86
  11. ngio/ome_zarr_meta/_meta_handlers.py +16 -8
  12. ngio/ome_zarr_meta/ngio_specs/_axes.py +4 -7
  13. ngio/ome_zarr_meta/ngio_specs/_channels.py +41 -29
  14. ngio/tables/__init__.py +14 -2
  15. ngio/tables/_abstract_table.py +269 -0
  16. ngio/tables/{tables_container.py → _tables_container.py} +186 -100
  17. ngio/tables/backends/__init__.py +20 -0
  18. ngio/tables/backends/_abstract_backend.py +58 -80
  19. ngio/tables/backends/{_anndata_v1.py → _anndata.py} +5 -1
  20. ngio/tables/backends/_csv.py +35 -0
  21. ngio/tables/backends/{_json_v1.py → _json.py} +4 -1
  22. ngio/tables/backends/{_csv_v1.py → _non_zarr_backends.py} +61 -27
  23. ngio/tables/backends/_parquet.py +47 -0
  24. ngio/tables/backends/_table_backends.py +39 -18
  25. ngio/tables/backends/_utils.py +147 -1
  26. ngio/tables/v1/__init__.py +19 -3
  27. ngio/tables/v1/_condition_table.py +71 -0
  28. ngio/tables/v1/_feature_table.py +63 -129
  29. ngio/tables/v1/_generic_table.py +21 -159
  30. ngio/tables/v1/_roi_table.py +285 -201
  31. ngio/utils/_fractal_fsspec_store.py +29 -0
  32. {ngio-0.2.8.dist-info → ngio-0.3.0.dist-info}/METADATA +4 -3
  33. ngio-0.3.0.dist-info/RECORD +61 -0
  34. ngio/tables/_validators.py +0 -108
  35. ngio-0.2.8.dist-info/RECORD +0 -57
  36. /ngio/images/{abstract_image.py → _abstract_image.py} +0 -0
  37. /ngio/images/{create.py → _create.py} +0 -0
  38. {ngio-0.2.8.dist-info → ngio-0.3.0.dist-info}/WHEEL +0 -0
  39. {ngio-0.2.8.dist-info → ngio-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,18 @@
1
1
  """A module for handling the Plate Collection in an OME-Zarr file."""
2
2
 
3
- from typing import Literal, overload
4
-
3
+ import asyncio
4
+ import warnings
5
+ from collections.abc import Collection
6
+ from typing import Literal
7
+
8
+ from ngio.common import (
9
+ concatenate_image_tables,
10
+ concatenate_image_tables_as,
11
+ concatenate_image_tables_as_async,
12
+ concatenate_image_tables_async,
13
+ list_image_tables,
14
+ list_image_tables_async,
15
+ )
5
16
  from ngio.images import OmeZarrContainer
6
17
  from ngio.ome_zarr_meta import (
7
18
  ImageInWellPath,
@@ -15,17 +26,19 @@ from ngio.ome_zarr_meta import (
15
26
  path_in_well_validation,
16
27
  )
17
28
  from ngio.tables import (
29
+ ConditionTable,
18
30
  FeatureTable,
19
31
  GenericRoiTable,
20
32
  MaskingRoiTable,
21
33
  RoiTable,
22
34
  Table,
35
+ TableBackend,
23
36
  TablesContainer,
37
+ TableType,
24
38
  TypedTable,
25
39
  )
26
40
  from ngio.utils import (
27
41
  AccessModeLiteral,
28
- NgioValidationError,
29
42
  NgioValueError,
30
43
  StoreOrGroup,
31
44
  ZarrGroupHandler,
@@ -186,6 +199,28 @@ class OmeZarrWell:
186
199
  )
187
200
 
188
201
 
202
+ def _buil_extras(paths: Collection[str]) -> list[dict[str, str]]:
203
+ """Build the extras for the images.
204
+
205
+ Args:
206
+ paths (Collection[str]): The paths of the images.
207
+
208
+ Returns:
209
+ list[dict[str, str]]: The extras for the images.
210
+ """
211
+ extras = []
212
+ for path in paths:
213
+ row, column, path_in_well = path.split("/")
214
+ extras.append(
215
+ {
216
+ "row": row,
217
+ "column": column,
218
+ "path_in_well": path_in_well,
219
+ }
220
+ )
221
+ return extras
222
+
223
+
189
224
  class OmeZarrPlate:
190
225
  """A class to handle the Plate Collection in an OME-Zarr file."""
191
226
 
@@ -253,6 +288,22 @@ class OmeZarrPlate:
253
288
  """Return the wells paths in the plate."""
254
289
  return self.meta.wells_paths
255
290
 
291
+ async def images_paths_async(self, acquisition: int | None = None) -> list[str]:
292
+ """Return the images paths in the plate asynchronously.
293
+
294
+ If acquisition is None, return all images paths in the plate.
295
+ Else, return the images paths in the plate for the given acquisition.
296
+
297
+ Args:
298
+ acquisition (int | None): The acquisition id to filter the images.
299
+ """
300
+ wells = await self.get_wells_async()
301
+ paths = []
302
+ for well_path, well in wells.items():
303
+ for img_path in well.paths(acquisition):
304
+ paths.append(f"{well_path}/{img_path}")
305
+ return paths
306
+
256
307
  def images_paths(self, acquisition: int | None = None) -> list[str]:
257
308
  """Return the images paths in the plate.
258
309
 
@@ -262,9 +313,10 @@ class OmeZarrPlate:
262
313
  Args:
263
314
  acquisition (int | None): The acquisition id to filter the images.
264
315
  """
316
+ wells = self.get_wells()
265
317
  images = []
266
- for well_path, wells in self.get_wells().items():
267
- for img_path in wells.paths(acquisition):
318
+ for well_path, well in wells.items():
319
+ for img_path in well.paths(acquisition):
268
320
  images.append(f"{well_path}/{img_path}")
269
321
  return images
270
322
 
@@ -317,6 +369,37 @@ class OmeZarrPlate:
317
369
  group_handler = self._group_handler.derive_handler(well_path)
318
370
  return OmeZarrWell(group_handler)
319
371
 
372
+ async def get_wells_async(self) -> dict[str, OmeZarrWell]:
373
+ """Get all wells in the plate asynchronously.
374
+
375
+ This method processes wells in parallel for improved performance
376
+ when working with a large number of wells.
377
+
378
+ Returns:
379
+ dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
380
+ path and the value is the well object.
381
+ """
382
+ wells = self._group_handler.get_from_cache("wells")
383
+ if wells is not None:
384
+ return wells # type: ignore[return-value]
385
+
386
+ def process_well(well_path):
387
+ group_handler = self._group_handler.derive_handler(well_path)
388
+ well = OmeZarrWell(group_handler)
389
+ return well_path, well
390
+
391
+ wells, tasks = {}, []
392
+ for well_path in self.wells_paths():
393
+ task = asyncio.to_thread(process_well, well_path)
394
+ tasks.append(task)
395
+
396
+ results = await asyncio.gather(*tasks)
397
+ for well_path, well in results:
398
+ wells[well_path] = well
399
+
400
+ self._group_handler.add_to_cache("wells", wells)
401
+ return wells
402
+
320
403
  def get_wells(self) -> dict[str, OmeZarrWell]:
321
404
  """Get all wells in the plate.
322
405
 
@@ -324,23 +407,86 @@ class OmeZarrPlate:
324
407
  dict[str, OmeZarrWell]: A dictionary of wells, where the key is the well
325
408
  path and the value is the well object.
326
409
  """
327
- wells = {}
328
- for well_path in self.wells_paths():
410
+ wells = self._group_handler.get_from_cache("wells")
411
+ if wells is not None:
412
+ return wells # type: ignore[return-value]
413
+
414
+ def process_well(well_path):
329
415
  group_handler = self._group_handler.derive_handler(well_path)
330
416
  well = OmeZarrWell(group_handler)
417
+ return well_path, well
418
+
419
+ wells = {}
420
+ for well_path in self.wells_paths():
421
+ _, well = process_well(well_path)
331
422
  wells[well_path] = well
423
+
424
+ self._group_handler.add_to_cache("wells", wells)
332
425
  return wells
333
426
 
427
+ async def get_images_async(
428
+ self, acquisition: int | None = None
429
+ ) -> dict[str, OmeZarrContainer]:
430
+ """Get all images in the plate asynchronously.
431
+
432
+ This method processes images in parallel for improved performance
433
+ when working with a large number of images.
434
+
435
+ Args:
436
+ acquisition: The acquisition id to filter the images.
437
+
438
+ Returns:
439
+ dict[str, OmeZarrContainer]: A dictionary of images, where the key is the
440
+ image path and the value is the image object.
441
+ """
442
+ images = self._group_handler.get_from_cache("images")
443
+ if images is not None:
444
+ return images # type: ignore[return-value]
445
+
446
+ paths = await self.images_paths_async(acquisition=acquisition)
447
+
448
+ def process_image(image_path):
449
+ """Process a single image and return the image path and image object."""
450
+ img_group_handler = self._group_handler.derive_handler(image_path)
451
+ image = OmeZarrContainer(img_group_handler)
452
+ return image_path, image
453
+
454
+ images, tasks = {}, []
455
+ for image_path in paths:
456
+ task = asyncio.to_thread(process_image, image_path)
457
+ tasks.append(task)
458
+
459
+ results = await asyncio.gather(*tasks)
460
+
461
+ for image_path, image in results:
462
+ images[image_path] = image
463
+
464
+ self._group_handler.add_to_cache("images", images)
465
+ return images
466
+
334
467
  def get_images(self, acquisition: int | None = None) -> dict[str, OmeZarrContainer]:
335
468
  """Get all images in the plate.
336
469
 
337
470
  Args:
338
471
  acquisition: The acquisition id to filter the images.
339
472
  """
340
- images = {}
341
- for image_path in self.images_paths(acquisition):
473
+ images = self._group_handler.get_from_cache("images")
474
+ if images is not None:
475
+ return images # type: ignore[return-value]
476
+ paths = self.images_paths(acquisition=acquisition)
477
+
478
+ def process_image(image_path):
479
+ """Process a single image and return the image path and image object."""
342
480
  img_group_handler = self._group_handler.derive_handler(image_path)
343
- images[image_path] = OmeZarrContainer(img_group_handler)
481
+ image = OmeZarrContainer(img_group_handler)
482
+ return image_path, image
483
+
484
+ images = {}
485
+ for image_path in paths:
486
+ _, image = process_image(image_path)
487
+ images[image_path] = image
488
+
489
+ self._group_handler.add_to_cache("images", images)
344
490
  return images
345
491
 
346
492
  def get_image(
@@ -658,100 +804,147 @@ class OmeZarrPlate:
658
804
  parallel_safe=parallel_safe,
659
805
  )
660
806
 
661
- @property
662
- def tables_container(self) -> TablesContainer:
807
+ def _get_tables_container(self) -> TablesContainer | None:
663
808
  """Return the tables container."""
664
809
  if self._tables_container is None:
665
- self._tables_container = _default_table_container(self._group_handler)
666
- if self._tables_container is None:
667
- raise NgioValidationError("No tables found in the image.")
810
+ _tables_container = _default_table_container(self._group_handler)
811
+ if _tables_container is None:
812
+ return None
813
+ self._tables_container = _tables_container
668
814
  return self._tables_container
669
815
 
670
816
  @property
671
- def list_tables(self) -> list[str]:
817
+ def tables_container(self) -> TablesContainer:
818
+ """Return the tables container."""
819
+ _table_container = self._get_tables_container()
820
+ if _table_container is None:
821
+ raise NgioValueError(
822
+ "No tables container found. Please add a tables container to the plate."
823
+ )
824
+ return _table_container
825
+
826
+ def list_tables(self, filter_types: TypedTable | str | None = None) -> list[str]:
672
827
  """List all tables in the image."""
673
- return self.tables_container.list()
828
+ return self.tables_container.list(filter_types=filter_types)
674
829
 
675
830
  def list_roi_tables(self) -> list[str]:
676
831
  """List all ROI tables in the image."""
677
- return self.tables_container.list_roi_tables()
832
+ masking_roi = self.tables_container.list(
833
+ filter_types="masking_roi_table",
834
+ )
835
+ roi = self.tables_container.list(
836
+ filter_types="roi_table",
837
+ )
838
+ return masking_roi + roi
839
+
840
+ def get_roi_table(self, name: str) -> RoiTable:
841
+ """Get a ROI table from the image.
842
+
843
+ Args:
844
+ name (str): The name of the table.
845
+ """
846
+ table = self.tables_container.get(name=name, strict=True)
847
+ if not isinstance(table, RoiTable):
848
+ raise NgioValueError(f"Table {name} is not a ROI table. Got {type(table)}")
849
+ return table
850
+
851
+ def get_masking_roi_table(self, name: str) -> MaskingRoiTable:
852
+ """Get a masking ROI table from the image.
853
+
854
+ Args:
855
+ name (str): The name of the table.
856
+ """
857
+ table = self.tables_container.get(name=name, strict=True)
858
+ if not isinstance(table, MaskingRoiTable):
859
+ raise NgioValueError(
860
+ f"Table {name} is not a masking ROI table. Got {type(table)}"
861
+ )
862
+ return table
678
863
 
679
- @overload
680
- def get_table(self, name: str) -> Table: ...
864
+ def get_feature_table(self, name: str) -> FeatureTable:
865
+ """Get a feature table from the image.
681
866
 
682
- @overload
683
- def get_table(self, name: str, check_type: None) -> Table: ...
867
+ Args:
868
+ name (str): The name of the table.
869
+ """
870
+ table = self.tables_container.get(name=name, strict=True)
871
+ if not isinstance(table, FeatureTable):
872
+ raise NgioValueError(
873
+ f"Table {name} is not a feature table. Got {type(table)}"
874
+ )
875
+ return table
684
876
 
685
- @overload
686
- def get_table(self, name: str, check_type: Literal["roi_table"]) -> RoiTable: ...
877
+ def get_generic_roi_table(self, name: str) -> GenericRoiTable:
878
+ """Get a generic ROI table from the image.
687
879
 
688
- @overload
689
- def get_table(
690
- self, name: str, check_type: Literal["masking_roi_table"]
691
- ) -> MaskingRoiTable: ...
880
+ Args:
881
+ name (str): The name of the table.
882
+ """
883
+ table = self.tables_container.get(name=name, strict=True)
884
+ if not isinstance(table, GenericRoiTable):
885
+ raise NgioValueError(
886
+ f"Table {name} is not a generic ROI table. Got {type(table)}"
887
+ )
888
+ return table
692
889
 
693
- @overload
694
- def get_table(
695
- self, name: str, check_type: Literal["feature_table"]
696
- ) -> FeatureTable: ...
890
+ def get_condition_table(self, name: str) -> ConditionTable:
891
+ """Get a condition table from the image.
697
892
 
698
- @overload
699
- def get_table(
700
- self, name: str, check_type: Literal["generic_roi_table"]
701
- ) -> GenericRoiTable: ...
893
+ Args:
894
+ name (str): The name of the table.
895
+ """
896
+ table = self.tables_container.get(name=name, strict=True)
897
+ if not isinstance(table, ConditionTable):
898
+ raise NgioValueError(
899
+ f"Table {name} is not a condition table. Got {type(table)}"
900
+ )
901
+ return table
702
902
 
703
903
  def get_table(self, name: str, check_type: TypedTable | None = None) -> Table:
704
904
  """Get a table from the image.
705
905
 
706
906
  Args:
707
907
  name (str): The name of the table.
708
- check_type (TypedTable | None): The type of the table. If None, the
709
- type is not checked. If a type is provided, the table must be of that
710
- type.
711
- """
712
- if check_type is None:
713
- table = self.tables_container.get(name, strict=False)
714
- return table
715
-
716
- table = self.tables_container.get(name, strict=True)
717
- match check_type:
718
- case "roi_table":
719
- if not isinstance(table, RoiTable):
720
- raise NgioValueError(
721
- f"Table '{name}' is not a ROI table. Found type: {table.type()}"
722
- )
723
- return table
724
- case "masking_roi_table":
725
- if not isinstance(table, MaskingRoiTable):
726
- raise NgioValueError(
727
- f"Table '{name}' is not a masking ROI table. "
728
- f"Found type: {table.type()}"
729
- )
730
- return table
731
-
732
- case "generic_roi_table":
733
- if not isinstance(table, GenericRoiTable):
734
- raise NgioValueError(
735
- f"Table '{name}' is not a generic ROI table. "
736
- f"Found type: {table.type()}"
737
- )
738
- return table
739
-
740
- case "feature_table":
741
- if not isinstance(table, FeatureTable):
742
- raise NgioValueError(
743
- f"Table '{name}' is not a feature table. "
744
- f"Found type: {table.type()}"
745
- )
746
- return table
747
- case _:
748
- raise NgioValueError(f"Unknown check_type: {check_type}")
908
+ check_type (TypedTable | None): Deprecated. Please use
909
+ 'get_table_as' instead, or one of the type specific
910
+ get_*table() methods.
911
+
912
+ """
913
+ if check_type is not None:
914
+ warnings.warn(
915
+ "The 'check_type' argument is deprecated, and will be removed in "
916
+ "ngio=0.3. Use 'get_table_as' instead or one of the "
917
+ "type specific get_*table() methods.",
918
+ DeprecationWarning,
919
+ stacklevel=2,
920
+ )
921
+ return self.tables_container.get(name=name, strict=False)
922
+
923
+ def get_table_as(
924
+ self,
925
+ name: str,
926
+ table_cls: type[TableType],
927
+ backend: TableBackend | None = None,
928
+ ) -> TableType:
929
+ """Get a table from the image as a specific type.
930
+
931
+ Args:
932
+ name (str): The name of the table.
933
+ table_cls (type[TableType]): The type of the table.
934
+ backend (TableBackend | None): The backend to use. If None,
935
+ the default backend is used.
936
+ """
937
+ return self.tables_container.get_as(
938
+ name=name,
939
+ table_cls=table_cls,
940
+ backend=backend,
941
+ )
749
942
 
750
943
  def add_table(
751
944
  self,
752
945
  name: str,
753
946
  table: Table,
754
- backend: str | None = None,
947
+ backend: TableBackend = "anndata",
755
948
  overwrite: bool = False,
756
949
  ) -> None:
757
950
  """Add a table to the image."""
@@ -759,6 +952,186 @@ class OmeZarrPlate:
759
952
  name=name, table=table, backend=backend, overwrite=overwrite
760
953
  )
761
954
 
955
+ def list_image_tables(
956
+ self,
957
+ acquisition: int | None = None,
958
+ filter_types: str | None = None,
959
+ mode: Literal["common", "all"] = "common",
960
+ ) -> list[str]:
961
+ """List all image tables in the image.
962
+
963
+ Args:
964
+ acquisition (int | None): The acquisition id to filter the images.
965
+ filter_types (str | None): The type of tables to filter. If None,
966
+ return all tables. Defaults to None.
967
+ mode (Literal["common", "all"]): The mode to use for listing the tables.
968
+ If 'common', return only common tables between all images.
969
+ If 'all', return all tables. Defaults to 'common'.
970
+ """
971
+ images = self.get_images(acquisition=acquisition)
972
+ return list_image_tables(
973
+ images=images.values(),
974
+ filter_types=filter_types,
975
+ mode=mode,
976
+ )
977
+
978
+ async def list_image_tables_async(
979
+ self,
980
+ acquisition: int | None = None,
981
+ filter_types: str | None = None,
982
+ mode: Literal["common", "all"] = "common",
983
+ ) -> list[str]:
984
+ """List all image tables in the image asynchronously.
985
+
986
+ Args:
987
+ acquisition (int | None): The acquisition id to filter the images.
988
+ filter_types (str | None): The type of tables to filter. If None,
989
+ return all tables. Defaults to None.
990
+ mode (Literal["common", "all"]): The mode to use for listing the tables.
991
+ If 'common', return only common tables between all images.
992
+ If 'all', return all tables. Defaults to 'common'.
993
+ """
994
+ images = await self.get_images_async(acquisition=acquisition)
995
+ return await list_image_tables_async(
996
+ images=images.values(),
997
+ filter_types=filter_types,
998
+ mode=mode,
999
+ )
1000
+
1001
+ def concatenate_image_tables(
1002
+ self,
1003
+ table_name: str,
1004
+ acquisition: int | None = None,
1005
+ strict: bool = True,
1006
+ index_key: str | None = None,
1007
+ mode: Literal["eager", "lazy"] = "eager",
1008
+ ) -> Table:
1009
+ """Concatenate tables from all images in the plate.
1010
+
1011
+ Args:
1012
+ table_name: The name of the table to concatenate.
1013
+ index_key: The key to use for the index of the concatenated table.
1014
+ acquisition: The acquisition id to filter the images.
1015
+ strict: If True, raise an error if the table is not found in the image.
1016
+ index_key: If a string is provided, a new index column will be created
1017
+ new_index_pattern = {row}_{column}_{path_in_well}_{label}
1018
+ mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
1019
+ if 'eager', the table will be loaded into memory.
1020
+ if 'lazy', the table will be loaded as a lazy frame.
1021
+ """
1022
+ images = self.get_images(acquisition=acquisition)
1023
+ extras = _buil_extras(images.keys())
1024
+ return concatenate_image_tables(
1025
+ images=images.values(),
1026
+ extras=extras,
1027
+ table_name=table_name,
1028
+ index_key=index_key,
1029
+ strict=strict,
1030
+ mode=mode,
1031
+ )
1032
+
1033
+ def concatenate_image_tables_as(
1034
+ self,
1035
+ table_name: str,
1036
+ table_cls: type[TableType],
1037
+ acquisition: int | None = None,
1038
+ index_key: str | None = None,
1039
+ strict: bool = True,
1040
+ mode: Literal["eager", "lazy"] = "eager",
1041
+ ) -> TableType:
1042
+ """Concatenate tables from all images in the plate as a specific type.
1043
+
1044
+ Args:
1045
+ table_name: The name of the table to concatenate.
1046
+ table_cls: The type of the table to concatenate.
1047
+ index_key: The key to use for the index of the concatenated table.
1048
+ acquisition: The acquisition id to filter the images.
1049
+ index_key: If a string is provided, a new index column will be created
1050
+ new_index_pattern = {row}_{column}_{path_in_well}_{label}
1051
+ strict: If True, raise an error if the table is not found in the image.
1052
+ mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
1053
+ if 'eager', the table will be loaded into memory.
1054
+ if 'lazy', the table will be loaded as a lazy frame.
1055
+ """
1056
+ images = self.get_images(acquisition=acquisition)
1057
+ extras = _buil_extras(images.keys())
1058
+ return concatenate_image_tables_as(
1059
+ images=images.values(),
1060
+ extras=extras,
1061
+ table_name=table_name,
1062
+ table_cls=table_cls,
1063
+ index_key=index_key,
1064
+ strict=strict,
1065
+ mode=mode,
1066
+ )
1067
+
1068
+ async def concatenate_image_tables_async(
1069
+ self,
1070
+ table_name: str,
1071
+ acquisition: int | None = None,
1072
+ index_key: str | None = None,
1073
+ strict: bool = True,
1074
+ mode: Literal["eager", "lazy"] = "eager",
1075
+ ) -> Table:
1076
+ """Concatenate tables from all images in the plate asynchronously.
1077
+
1078
+ Args:
1079
+ table_name: The name of the table to concatenate.
1080
+ index_key: The key to use for the index of the concatenated table.
1081
+ acquisition: The acquisition id to filter the images.
1082
+ index_key: If a string is provided, a new index column will be created
1083
+ new_index_pattern = {row}_{column}_{path_in_well}_{label}
1084
+ strict: If True, raise an error if the table is not found in the image.
1085
+ mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
1086
+ if 'eager', the table will be loaded into memory.
1087
+ if 'lazy', the table will be loaded as a lazy frame.
1088
+ """
1089
+ images = await self.get_images_async(acquisition=acquisition)
1090
+ extras = _buil_extras(images.keys())
1091
+ return await concatenate_image_tables_async(
1092
+ images=images.values(),
1093
+ extras=extras,
1094
+ table_name=table_name,
1095
+ index_key=index_key,
1096
+ strict=strict,
1097
+ mode=mode,
1098
+ )
1099
+
1100
+ async def concatenate_image_tables_as_async(
1101
+ self,
1102
+ table_name: str,
1103
+ table_cls: type[TableType],
1104
+ acquisition: int | None = None,
1105
+ index_key: str | None = None,
1106
+ strict: bool = True,
1107
+ mode: Literal["eager", "lazy"] = "eager",
1108
+ ) -> TableType:
1109
+ """Concatenate tables from all images in the plate as a specific type.
1110
+
1111
+ Args:
1112
+ table_name: The name of the table to concatenate.
1113
+ table_cls: The type of the table to concatenate.
1114
+ index_key: The key to use for the index of the concatenated table.
1115
+ acquisition: The acquisition id to filter the images.
1116
+ index_key: If a string is provided, a new index column will be created
1117
+ new_index_pattern = {row}_{column}_{path_in_well}_{label}
1118
+ strict: If True, raise an error if the table is not found in the image.
1119
+ mode: The mode to use for concatenation. Can be 'eager' or 'lazy'.
1120
+ if 'eager', the table will be loaded into memory.
1121
+ if 'lazy', the table will be loaded as a lazy frame.
1122
+ """
1123
+ images = await self.get_images_async(acquisition=acquisition)
1124
+ extras = _buil_extras(images.keys())
1125
+ return await concatenate_image_tables_as_async(
1126
+ images=images.values(),
1127
+ extras=extras,
1128
+ table_name=table_name,
1129
+ table_cls=table_cls,
1130
+ index_key=index_key,
1131
+ strict=strict,
1132
+ mode=mode,
1133
+ )
1134
+
762
1135
 
763
1136
  def open_ome_zarr_plate(
764
1137
  store: StoreOrGroup,
ngio/images/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  """OME-Zarr object models."""
2
2
 
3
- from ngio.images.image import Image, ImagesContainer
4
- from ngio.images.label import Label, LabelsContainer
5
- from ngio.images.ome_zarr_container import (
3
+ from ngio.images._image import Image, ImagesContainer
4
+ from ngio.images._label import Label, LabelsContainer
5
+ from ngio.images._ome_zarr_container import (
6
6
  OmeZarrContainer,
7
7
  create_empty_ome_zarr,
8
8
  create_ome_zarr_from_array,