ngio 0.3.0a1__py3-none-any.whl → 0.3.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.
@@ -329,33 +329,35 @@ class ChannelsMeta(BaseModel):
329
329
  @classmethod
330
330
  def default_init(
331
331
  cls,
332
- labels: Collection[str] | int,
333
- wavelength_id: Collection[str] | None = None,
334
- colors: Collection[str | NgioColors] | None = None,
335
- start: Collection[int | float] | int | float | None = None,
336
- end: Collection[int | float] | int | float | None = None,
337
- active: Collection[bool] | None = None,
332
+ labels: Collection[str | None] | int,
333
+ wavelength_id: Collection[str | None] | None = None,
334
+ colors: Collection[str | NgioColors | None] | None = None,
335
+ start: Collection[int | float | None] | int | float | None = None,
336
+ end: Collection[int | float | None] | int | float | None = None,
337
+ active: Collection[bool | None] | None = None,
338
338
  data_type: Any = np.uint16,
339
339
  **omero_kwargs: dict,
340
340
  ) -> "ChannelsMeta":
341
341
  """Create a ChannelsMeta object with the default unit.
342
342
 
343
343
  Args:
344
- labels(Collection[str] | int): The list of channels names in the image.
345
- If an integer is provided, the channels will be named "channel_i".
346
- wavelength_id(Collection[str] | None): The wavelength ID of the channel.
347
- If None, the wavelength ID will be the same as the channel name.
348
- colors(Collection[str, NgioColors] | None): The list of colors for the
349
- channels. If None, the colors will be random.
350
- start(Collection[int | float] | int | float | None): The start value of the
351
- channel. If None, the start value will be the minimum value of the
352
- data type.
353
- end(Collection[int | float] | int | float | None): The end value of the
354
- channel. If None, the end value will be the maximum value of the
355
- data type.
344
+ labels(Collection[str | None] | int): The list of channels names
345
+ in the image. If an integer is provided, the channels will be
346
+ named "channel_i".
347
+ wavelength_id(Collection[str | None] | None): The wavelength ID of the
348
+ channel. If None, the wavelength ID will be the same as the
349
+ channel name.
350
+ colors(Collection[str | NgioColors | None] | None): The list of
351
+ colors for the channels. If None, the colors will be random.
352
+ start(Collection[int | float | None] | int | float | None): The start
353
+ value of the channel. If None, the start value will be the
354
+ minimum value of the data type.
355
+ end(Collection[int | float | None] | int | float | None): The end
356
+ value of the channel. If None, the end value will be the
357
+ maximum value of the data type.
356
358
  data_type(Any): The data type of the channel. Will be used to set the
357
359
  min and max values of the channel.
358
- active (Collection[bool] | None):active(bool): Whether the channel should
360
+ active (Collection[bool | None] | None): Whether the channel should
359
361
  be shown by default.
360
362
  omero_kwargs(dict): Extra fields to store in the omero attributes.
361
363
  """
@@ -366,25 +368,35 @@ class ChannelsMeta(BaseModel):
366
368
  labels = _check_unique(labels)
367
369
 
368
370
  _wavelength_id: Collection[str | None] = [None] * len(labels)
369
- if isinstance(wavelength_id, Collection):
371
+ if wavelength_id is None:
372
+ _wavelength_id: Collection[str | None] = [None] * len(labels)
373
+ else:
370
374
  _wavelength_id = _check_elements(wavelength_id, str)
371
375
  _wavelength_id = _check_unique(wavelength_id)
372
376
 
373
- _colors: Collection[str | NgioColors | None] = [None] * len(labels)
374
- if isinstance(colors, Collection):
377
+ if colors is None:
378
+ _colors = [NgioColors.semi_random_pick(label) for label in labels]
379
+ else:
375
380
  _colors = _check_elements(colors, str | NgioColors)
376
381
 
377
- _start: Collection[int | float | None] = [None] * len(labels)
378
- if isinstance(start, Collection):
382
+ if start is None:
383
+ _start = [None] * len(labels)
384
+ elif isinstance(start, int | float):
385
+ _start = [start] * len(labels)
386
+ else:
379
387
  _start = _check_elements(start, (int, float))
380
388
 
381
- _end: Collection[int | float | None] = [None] * len(labels)
382
- if isinstance(end, Collection):
389
+ if end is None:
390
+ _end = [None] * len(labels)
391
+ elif isinstance(end, int | float):
392
+ _end = [end] * len(labels)
393
+ else:
383
394
  _end = _check_elements(end, (int, float))
384
395
 
385
- _active: Collection[bool] = [True] * len(labels)
386
- if isinstance(active, Collection):
387
- _active = _check_elements(active, bool)
396
+ if active is None:
397
+ _active = [True] * len(labels)
398
+ else:
399
+ _active = _check_elements(active, (bool,))
388
400
 
389
401
  all_lengths = [
390
402
  len(labels),
ngio/tables/__init__.py CHANGED
@@ -1,11 +1,6 @@
1
1
  """Ngio Tables implementations."""
2
2
 
3
- from ngio.tables.backends import (
4
- ImplementedTableBackends,
5
- TableBackend,
6
- TableBackendProtocol,
7
- )
8
- from ngio.tables.tables_container import (
3
+ from ngio.tables._tables_container import (
9
4
  ConditionTable,
10
5
  FeatureTable,
11
6
  GenericRoiTable,
@@ -19,10 +14,17 @@ from ngio.tables.tables_container import (
19
14
  open_table_as,
20
15
  open_tables_container,
21
16
  )
17
+ from ngio.tables.backends import (
18
+ DefaultTableBackend,
19
+ ImplementedTableBackends,
20
+ TableBackend,
21
+ TableBackendProtocol,
22
+ )
22
23
  from ngio.tables.v1._generic_table import GenericTable
23
24
 
24
25
  __all__ = [
25
26
  "ConditionTable",
27
+ "DefaultTableBackend",
26
28
  "FeatureTable",
27
29
  "GenericRoiTable",
28
30
  "GenericTable",
@@ -10,6 +10,7 @@ from anndata import AnnData
10
10
 
11
11
  from ngio.tables.backends import (
12
12
  BackendMeta,
13
+ DefaultTableBackend,
13
14
  ImplementedTableBackends,
14
15
  TableBackend,
15
16
  TableBackendProtocol,
@@ -203,7 +204,7 @@ class AbstractBaseTable(ABC):
203
204
  def set_backend(
204
205
  self,
205
206
  handler: ZarrGroupHandler | None = None,
206
- backend: TableBackend = "anndata",
207
+ backend: TableBackend = DefaultTableBackend,
207
208
  ) -> None:
208
209
  """Set the backend of the table."""
209
210
  if handler is None:
@@ -8,6 +8,7 @@ import polars as pl
8
8
 
9
9
  from ngio.tables.backends import (
10
10
  BackendMeta,
11
+ DefaultTableBackend,
11
12
  TableBackend,
12
13
  TabularData,
13
14
  )
@@ -90,7 +91,7 @@ class Table(Protocol):
90
91
  def set_backend(
91
92
  self,
92
93
  handler: ZarrGroupHandler | None = None,
93
- backend: TableBackend = "anndata",
94
+ backend: TableBackend = DefaultTableBackend,
94
95
  ) -> None:
95
96
  """Set the backend store and path for the table.
96
97
 
@@ -126,13 +127,18 @@ class Table(Protocol):
126
127
 
127
128
 
128
129
  TypedTable = Literal[
130
+ "generic_table",
129
131
  "roi_table",
130
132
  "masking_roi_table",
131
133
  "feature_table",
132
- "generic_roi_table",
133
134
  "condition_table",
134
135
  ]
135
136
 
137
+ TypedRoiTable = Literal[
138
+ "roi_table",
139
+ "masking_roi_table",
140
+ ]
141
+
136
142
  TableType = TypeVar("TableType", bound=Table)
137
143
 
138
144
 
@@ -192,19 +198,34 @@ class ImplementedTables:
192
198
  table = table_cls.from_handler(handler=handler, backend=backend)
193
199
  return table
194
200
 
195
- def add_implementation(self, handler: type[Table], overwrite: bool = False):
201
+ def _add_implementation(
202
+ self, handler: type[Table], name: str, overwrite: bool = False
203
+ ) -> None:
204
+ """Register a new table handler."""
205
+ if name in self._implemented_tables and not overwrite:
206
+ raise NgioValueError(
207
+ f"Table handler for {name} already implemented. "
208
+ "Use overwrite=True to replace it."
209
+ )
210
+ self._implemented_tables[name] = handler
211
+
212
+ def add_implementation(
213
+ self,
214
+ handler: type[Table],
215
+ overwrite: bool = False,
216
+ aliases: list[str] | None = None,
217
+ ) -> None:
196
218
  """Register a new table handler."""
197
219
  meta = TableMeta(
198
220
  type=handler.table_type(),
199
221
  table_version=handler.version(),
200
222
  )
201
223
 
202
- if meta.unique_name() in self._implemented_tables and not overwrite:
203
- raise NgioValueError(
204
- f"Table handler for {meta.unique_name()} already implemented. "
205
- "Use overwrite=True to replace it."
206
- )
207
- self._implemented_tables[meta.unique_name()] = handler
224
+ self._add_implementation(handler, meta.unique_name(), overwrite)
225
+
226
+ if aliases is not None:
227
+ for alias in aliases:
228
+ self._add_implementation(handler, alias, overwrite)
208
229
 
209
230
 
210
231
  class TablesContainer:
@@ -240,14 +261,7 @@ class TablesContainer:
240
261
  handler = self._group_handler.derive_handler(path=name)
241
262
  return handler
242
263
 
243
- def list_roi_tables(self) -> list[str]:
244
- """List all ROI tables in the group."""
245
- _tables = []
246
- for _type in ["roi_table", "masking_roi_table"]:
247
- _tables.extend(self.list(_type))
248
- return _tables
249
-
250
- def list(self, filter_types: str | None = None) -> list[str]:
264
+ def list(self, filter_types: TypedTable | str | None = None) -> list[str]:
251
265
  """List all labels in the group."""
252
266
  tables = self._get_tables_list()
253
267
  if filter_types is None:
@@ -301,7 +315,7 @@ class TablesContainer:
301
315
  self,
302
316
  name: str,
303
317
  table: Table,
304
- backend: TableBackend = "anndata",
318
+ backend: TableBackend = DefaultTableBackend,
305
319
  overwrite: bool = False,
306
320
  ) -> None:
307
321
  """Add a table to the group."""
@@ -393,7 +407,7 @@ def open_table_as(
393
407
  def write_table(
394
408
  store: StoreOrGroup,
395
409
  table: Table,
396
- backend: TableBackend = "anndata",
410
+ backend: TableBackend = DefaultTableBackend,
397
411
  cache: bool = False,
398
412
  mode: AccessModeLiteral = "a",
399
413
  parallel_safe: bool = False,
@@ -6,6 +6,7 @@ from ngio.tables.backends._csv import CsvTableBackend
6
6
  from ngio.tables.backends._json import JsonTableBackend
7
7
  from ngio.tables.backends._parquet import ParquetTableBackend
8
8
  from ngio.tables.backends._table_backends import (
9
+ DefaultTableBackend,
9
10
  ImplementedTableBackends,
10
11
  TableBackend,
11
12
  TableBackendProtocol,
@@ -32,6 +33,7 @@ __all__ = [
32
33
  "AnnDataBackend",
33
34
  "BackendMeta",
34
35
  "CsvTableBackend",
36
+ "DefaultTableBackend",
35
37
  "ImplementedTableBackends",
36
38
  "JsonTableBackend",
37
39
  "ParquetTableBackend",
@@ -19,7 +19,7 @@ from ngio.utils import NgioValueError, ZarrGroupHandler
19
19
  class BackendMeta(BaseModel):
20
20
  """Metadata for the backend."""
21
21
 
22
- backend: str = "anndata"
22
+ backend: str = "anndata_v1"
23
23
  index_key: str | None = None
24
24
  index_type: Literal["int", "str"] | None = None
25
25
 
@@ -180,24 +180,47 @@ class ImplementedTableBackends:
180
180
  )
181
181
  return backend
182
182
 
183
- def add_backend(
183
+ def _add_backend(
184
184
  self,
185
- table_beckend: type[TableBackendProtocol],
185
+ table_backend: type[TableBackendProtocol],
186
+ name: str,
186
187
  overwrite: bool = False,
187
- ):
188
+ ) -> None:
188
189
  """Register a new handler."""
189
- backend_name = table_beckend.backend_name()
190
- if backend_name in self._implemented_backends and not overwrite:
190
+ if name in self._implemented_backends and not overwrite:
191
191
  raise NgioValueError(
192
- f"Table backend {backend_name} already implemented. "
192
+ f"Table backend {name} already implemented. "
193
193
  "Use the `overwrite=True` parameter to overwrite it."
194
194
  )
195
- self._implemented_backends[backend_name] = table_beckend
196
-
195
+ self._implemented_backends[name] = table_backend
197
196
 
198
- ImplementedTableBackends().add_backend(AnnDataBackend)
199
- ImplementedTableBackends().add_backend(JsonTableBackend)
200
- ImplementedTableBackends().add_backend(CsvTableBackend)
201
- ImplementedTableBackends().add_backend(ParquetTableBackend)
197
+ def add_backend(
198
+ self,
199
+ table_backend: type[TableBackendProtocol],
200
+ overwrite: bool = False,
201
+ aliases: list[str] | None = None,
202
+ ) -> None:
203
+ """Register a new handler."""
204
+ self._add_backend(
205
+ table_backend=table_backend,
206
+ name=table_backend.backend_name(),
207
+ overwrite=overwrite,
208
+ )
209
+ if aliases is not None:
210
+ for alias in aliases:
211
+ self._add_backend(
212
+ table_backend=table_backend, name=alias, overwrite=overwrite
213
+ )
214
+
215
+
216
+ ImplementedTableBackends().add_backend(AnnDataBackend, aliases=["anndata_v1"])
217
+ ImplementedTableBackends().add_backend(
218
+ JsonTableBackend, aliases=["experimental_json_v1"]
219
+ )
220
+ ImplementedTableBackends().add_backend(CsvTableBackend, aliases=["experimental_csv_v1"])
221
+ ImplementedTableBackends().add_backend(
222
+ ParquetTableBackend, aliases=["experimental_parquet_v1"]
223
+ )
202
224
 
203
225
  TableBackend = Literal["anndata", "json", "csv", "parquet"] | str | TableBackendProtocol
226
+ DefaultTableBackend = "anndata_v1"
@@ -1,6 +1,6 @@
1
1
  """Implementation of a generic table class."""
2
2
 
3
- from ngio.tables.abstract_table import AbstractBaseTable
3
+ from ngio.tables._abstract_table import AbstractBaseTable
4
4
  from ngio.tables.backends import (
5
5
  BackendMeta,
6
6
  TableBackend,
@@ -8,7 +8,7 @@ from typing import Literal
8
8
 
9
9
  from pydantic import BaseModel, Field
10
10
 
11
- from ngio.tables.abstract_table import AbstractBaseTable
11
+ from ngio.tables._abstract_table import AbstractBaseTable
12
12
  from ngio.tables.backends import BackendMeta, TableBackend, TabularData
13
13
  from ngio.utils import NgioValueError
14
14
  from ngio.utils._zarr_utils import ZarrGroupHandler
@@ -26,7 +26,7 @@ class FeatureTableMeta(BackendMeta):
26
26
  table_version: Literal["1"] = "1"
27
27
  type: Literal["feature_table"] = "feature_table"
28
28
  region: RegionMeta | None = None
29
- instance_key: str = "label" # Legacy field, kept for compatibility
29
+ instance_key: str = "label" # Legacy field, kept for compatibility
30
30
  # Backend metadata
31
31
  index_key: str | None = "label"
32
32
  index_type: Literal["int", "str"] | None = "int"
@@ -1,6 +1,6 @@
1
1
  """Implementation of a generic table class."""
2
2
 
3
- from ngio.tables.abstract_table import AbstractBaseTable
3
+ from ngio.tables._abstract_table import AbstractBaseTable
4
4
  from ngio.tables.backends import BackendMeta, TableBackend
5
5
  from ngio.utils import ZarrGroupHandler
6
6
 
@@ -13,7 +13,7 @@ import pandas as pd
13
13
  from pydantic import BaseModel
14
14
 
15
15
  from ngio.common import Roi
16
- from ngio.tables.abstract_table import (
16
+ from ngio.tables._abstract_table import (
17
17
  AbstractBaseTable,
18
18
  TabularData,
19
19
  )
@@ -47,11 +47,20 @@ REQUIRED_COLUMNS = [
47
47
  ORIGIN_COLUMNS = [
48
48
  "x_micrometer_original",
49
49
  "y_micrometer_original",
50
+ "z_micrometer_original",
50
51
  ]
51
52
 
52
53
  TRANSLATION_COLUMNS = ["translation_x", "translation_y", "translation_z"]
53
54
 
54
- PLATE_COLUMNS = ["plate_name", "row", "column", "path", "acquisition"]
55
+ PLATE_COLUMNS = [
56
+ "plate_name",
57
+ "row",
58
+ "column",
59
+ "path_in_well",
60
+ "path_in_plate",
61
+ "acquisition_id",
62
+ "acquisition_name",
63
+ ]
55
64
 
56
65
  INDEX_COLUMNS = [
57
66
  "FieldIndex",
@@ -390,7 +399,8 @@ class MaskingRoiTableV1(GenericRoiTableV1):
390
399
  meta = MaskingRoiTableV1Meta()
391
400
 
392
401
  if reference_label is not None:
393
- meta.region = RegionMeta(path=reference_label)
402
+ path = f"../labels/{reference_label}"
403
+ meta.region = RegionMeta(path=path)
394
404
 
395
405
  if meta.index_key is None:
396
406
  meta.index_key = "label"
@@ -37,6 +37,6 @@ def fractal_fsspec_store(
37
37
  raise NgioValueError(
38
38
  f"Store {url} can not be read. Possible problems are: \n"
39
39
  "- The url does not exist. \n"
40
- f"- The url is not a valid .zarr. \n"
40
+ "- The url is not a valid .zarr. \n"
41
41
  )
42
42
  return store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngio
3
- Version: 0.3.0a1
3
+ Version: 0.3.2
4
4
  Summary: Next Generation file format IO
5
5
  Project-URL: homepage, https://github.com/fractal-analytics-platform/ngio
6
6
  Project-URL: repository, https://github.com/fractal-analytics-platform/ngio
@@ -63,7 +63,7 @@ Requires-Dist: pytest-cov; extra == 'test'
63
63
  Requires-Dist: scikit-image; extra == 'test'
64
64
  Description-Content-Type: text/markdown
65
65
 
66
- # NGIO - Next Generation file format IO
66
+ # Ngio - Next Generation file format IO
67
67
 
68
68
  [![License](https://img.shields.io/pypi/l/ngio.svg?color=green)](https://github.com/lorenzocerrone/ngio/raw/main/LICENSE)
69
69
  [![PyPI](https://img.shields.io/pypi/v/ngio.svg?color=green)](https://pypi.org/project/ngio)
@@ -71,36 +71,69 @@ Description-Content-Type: text/markdown
71
71
  [![CI](https://github.com/fractal-analytics-platform/ngio/actions/workflows/ci.yml/badge.svg)](https://github.com/fractal-analytics-platform/ngio/actions/workflows/ci.yml)
72
72
  [![codecov](https://codecov.io/gh/fractal-analytics-platform/ngio/graph/badge.svg?token=FkmF26FZki)](https://codecov.io/gh/fractal-analytics-platform/ngio)
73
73
 
74
- NGIO is a Python library to streamline OME-Zarr image analysis workflows.
75
-
76
- **Main Goals:**
77
-
78
- - Abstract object base API for handling OME-Zarr files
79
- - Powerful iterators for processing data using common access patterns
80
- - Tight integration with [Fractal's Table Fractal](https://fractal-analytics-platform.github.io/fractal-tasks-core/tables/)
81
- - Validation of OME-Zarr files
82
-
83
- To get started, check out the [Getting Started](https://fractal-analytics-platform.github.io/ngio/getting-started/) guide. Or checkout our [Documentation](https://fractal-analytics-platform.github.io/ngio/)
84
-
85
- ## 🚧 Ngio is Under active Development 🚧
86
-
87
- ### Roadmap
88
-
89
- | Feature | Status | ETA | Description |
90
- |---------|--------|-----|-------------|
91
- | Metadata Handling | | | Read, Write, Validate OME-Zarr Metadata (0.4 supported, 0.5 ready) |
92
- | OME-Zarr Validation | ✅ | | Validate OME-Zarr files for compliance with the OME-Zarr Specification + Compliance between Metadata and Data |
93
- | Base Image Handling | ✅ | | Load data from OME-Zarr files, retrieve basic metadata, and write data |
94
- | ROI Handling | | | Common ROI models |
95
- | Label Handling | ✅ | Mid-September | Based on Image Handling |
96
- | Table Validation | ✅ | Mid-September | Validate Table fractal V1 + Compliance between Metadata and Data |
97
- | Table Handling | ✅ | Mid-September | Read, Write ROI, Features, and Masked Tables |
98
- | Basic Iterators | Ongoing | End-September | Read and Write Iterators for common access patterns |
99
- | Base Documentation | ✅ | End-September | API Documentation and Examples |
100
- | Beta Ready Testing | ✅ | End-September | Beta Testing; Library is ready for testing, but the API is not stable |
101
- | Streaming from Fractal | Ongoing | December | Ngio can stream OME-Zarr from fractal |
102
- | Mask Iterators | Ongoing | Early 2025 | Iterators over Masked Tables |
103
- | Advanced Iterators | Not started | mid-2025 | Iterators for advanced access patterns |
104
- | Parallel Iterators | Not started | mid-2025 | Concurrent Iterators for parallel read and write |
105
- | Full Documentation | Not started | 2025 | Complete Documentation |
106
- | Release 1.0 (Commitment to API) | Not started | 2025 | API is stable; breaking changes will be avoided |
74
+ ngio is a Python library designed to simplify bioimage analysis workflows, offering an intuitive interface for working with OME-Zarr files.
75
+
76
+ ## What is Ngio?
77
+
78
+ Ngio is built for the [OME-Zarr](https://ngff.openmicroscopy.org/) file format, a modern, cloud-optimized format for biological imaging data. OME-Zarr stores large, multi-dimensional microscopy images and metadata in an efficient and scalable way.
79
+
80
+ Ngio's mission is to streamline working with OME-Zarr files by providing a simple, object-based API for opening, exploring, and manipulating OME-Zarr images and high-content screening (HCS) plates. It also offers comprehensive support for labels, tables and regions of interest (ROIs), making it easy to extract and analyze specific regions in your data.
81
+
82
+ ## Key Features
83
+
84
+ ### 📊 Simple Object-Based API
85
+
86
+ - Easily open, explore, and manipulate OME-Zarr images and HCS plates
87
+ - Create and derive new images and labels with minimal boilerplate code
88
+
89
+ ### 🔍 Rich Tables and Regions of Interest (ROI) Support
90
+
91
+ - Extract and analyze specific regions of interest
92
+ - Tight integration with [Tabular Data](https://fractal-analytics-platform.github.io/ngio/stable/table_specs/overview/)
93
+
94
+ ### 🔄 Scalable Data Processing (Coming Soon)
95
+
96
+ - Powerful iterators for processing data at scale
97
+ - Efficient memory management for large datasets
98
+
99
+ ## Installation
100
+
101
+ You can install ngio via pip:
102
+
103
+ ```bash
104
+ pip install ngio
105
+ ```
106
+
107
+ To get started check out the [Quickstart Guide](https://fractal-analytics-platform.github.io/ngio/stable/getting_started/0_quickstart/).
108
+
109
+ ## Supported OME-Zarr versions
110
+
111
+ Currently, ngio only supports OME-Zarr v0.4. Support for version 0.5 and higher is planned for future releases.
112
+
113
+ ## Development Status
114
+
115
+ !!! warning
116
+ Ngio is under active development and is not yet stable. The API is subject to change, and bugs and breaking changes are expected.
117
+ We follow [Semantic Versioning](https://semver.org/). Which means for 0.x releases potentially breaking changes can be introduced in minor releases.
118
+
119
+ ### Available Features
120
+
121
+ - ✅ OME-Zarr metadata handling and validation
122
+ - ✅ Image and label access across pyramid levels
123
+ - ✅ ROI and table support
124
+ - ✅ Streaming from remote sources
125
+ - ✅ Documentation and examples
126
+
127
+ ### Upcoming Features
128
+
129
+ - Advanced image processing iterators
130
+ - Parallel processing capabilities
131
+ - Support for OME-Zarr v0.5 and Zarr v3
132
+
133
+ ## Contributors
134
+
135
+ Ngio is developed at the [BioVisionCenter](https://www.biovisioncenter.uzh.ch/en.html), University of Zurich, by [@lorenzocerrone](https://github.com/lorenzocerrone) and [@jluethi](https://github.com/jluethi).
136
+
137
+ ## License
138
+
139
+ Ngio is released under the BSD-3-Clause License. See [LICENSE](https://github.com/fractal-analytics-platform/ngio/blob/main/LICENSE) for details.