ngio 0.2.0a1__tar.gz → 0.2.0a2__tar.gz

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 (91) hide show
  1. {ngio-0.2.0a1 → ngio-0.2.0a2}/PKG-INFO +1 -1
  2. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/notebooks/image.ipynb +67 -58
  3. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/notebooks/processing.ipynb +34 -34
  4. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_pyramid.py +1 -0
  5. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/images/image.py +8 -2
  6. ngio-0.2.0a2/src/ngio/images/label.py +236 -0
  7. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/images/omezarr_container.py +27 -4
  8. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/utils/_datasets.py +1 -1
  9. ngio-0.2.0a1/src/ngio/images/label.py +0 -96
  10. {ngio-0.2.0a1 → ngio-0.2.0a2}/.copier-answers.yml +0 -0
  11. {ngio-0.2.0a1 → ngio-0.2.0a2}/.gitattributes +0 -0
  12. {ngio-0.2.0a1 → ngio-0.2.0a2}/.github/ISSUE_TEMPLATE.md +0 -0
  13. {ngio-0.2.0a1 → ngio-0.2.0a2}/.github/TEST_FAIL_TEMPLATE.md +0 -0
  14. {ngio-0.2.0a1 → ngio-0.2.0a2}/.github/dependabot.yml +0 -0
  15. {ngio-0.2.0a1 → ngio-0.2.0a2}/.github/workflows/build_docs.yml +0 -0
  16. {ngio-0.2.0a1 → ngio-0.2.0a2}/.github/workflows/ci.yml +0 -0
  17. {ngio-0.2.0a1 → ngio-0.2.0a2}/.gitignore +0 -0
  18. {ngio-0.2.0a1 → ngio-0.2.0a2}/.pre-commit-config.yaml +0 -0
  19. {ngio-0.2.0a1 → ngio-0.2.0a2}/LICENSE +0 -0
  20. {ngio-0.2.0a1 → ngio-0.2.0a2}/README.md +0 -0
  21. {ngio-0.2.0a1 → ngio-0.2.0a2}/_typos.toml +0 -0
  22. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/api/core.md +0 -0
  23. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/getting-started.md +0 -0
  24. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/index.md +0 -0
  25. {ngio-0.2.0a1 → ngio-0.2.0a2}/docs/notebooks/basic_usage.ipynb +0 -0
  26. {ngio-0.2.0a1 → ngio-0.2.0a2}/mkdocs.yml +0 -0
  27. {ngio-0.2.0a1 → ngio-0.2.0a2}/pyproject.toml +0 -0
  28. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/__init__.py +0 -0
  29. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/__init__.py +0 -0
  30. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_array_pipe.py +0 -0
  31. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_axes_transforms.py +0 -0
  32. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_common_types.py +0 -0
  33. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_dimensions.py +0 -0
  34. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_roi.py +0 -0
  35. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_slicer.py +0 -0
  36. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/common/_zoom.py +0 -0
  37. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/hcs/__init__.py +0 -0
  38. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/images/__init__.py +0 -0
  39. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/images/abstract_image.py +0 -0
  40. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/images/create.py +0 -0
  41. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/__init__.py +0 -0
  42. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/_generic_handlers.py +0 -0
  43. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/_meta_handlers.py +0 -0
  44. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/__init__.py +0 -0
  45. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_axes.py +0 -0
  46. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_channels.py +0 -0
  47. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_dataset.py +0 -0
  48. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_ngio_hcs.py +0 -0
  49. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_ngio_image.py +0 -0
  50. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/ngio_specs/_pixel_size.py +0 -0
  51. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/v04/__init__.py +0 -0
  52. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/v04/_meta_handlers.py +0 -0
  53. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/ome_zarr_meta/v04/_v04_spec_utils.py +0 -0
  54. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/__init__.py +0 -0
  55. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/_validators.py +0 -0
  56. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/__init__.py +0 -0
  57. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/_abstract_backend.py +0 -0
  58. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/_anndata_utils.py +0 -0
  59. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/_anndata_v1.py +0 -0
  60. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/_json_v1.py +0 -0
  61. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/backends/_table_backends.py +0 -0
  62. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/tables_container.py +0 -0
  63. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/v1/__init__.py +0 -0
  64. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/v1/_feature_table.py +0 -0
  65. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/v1/_generic_table.py +0 -0
  66. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/v1/_masking_roi_table.py +0 -0
  67. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/tables/v1/_roi_table.py +0 -0
  68. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/utils/__init__.py +0 -0
  69. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/utils/_errors.py +0 -0
  70. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/utils/_logger.py +0 -0
  71. {ngio-0.2.0a1 → ngio-0.2.0a2}/src/ngio/utils/_zarr_utils.py +0 -0
  72. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/conftest.py +0 -0
  73. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/data/meta_v04/base_ome_zarr_image_meta.json +0 -0
  74. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/data/meta_v04/base_ome_zarr_image_meta_wrong_axis_order.json +0 -0
  75. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/data/meta_v04/base_ome_zarr_label_meta.json +0 -0
  76. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/common/test_dimensions.py +0 -0
  77. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/common/test_pyramid.py +0 -0
  78. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/common/test_roi.py +0 -0
  79. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/images/test_omezarr_container.py +0 -0
  80. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_backends.py +0 -0
  81. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_feature_table.py +0 -0
  82. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_generic_table.py +0 -0
  83. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_masking_roi_table_v1.py +0 -0
  84. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_roi_table_v1.py +0 -0
  85. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_table_group.py +0 -0
  86. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/tables/test_validators.py +0 -0
  87. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/test_ome_zarr_meta/test_image_handler.py +0 -0
  88. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/test_ome_zarr_meta/test_unit_ngio_specs.py +0 -0
  89. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/test_ome_zarr_meta/test_unit_v04_utils.py +0 -0
  90. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/utils/test_download_datasets.py +0 -0
  91. {ngio-0.2.0a1 → ngio-0.2.0a2}/tests/unit/utils/test_zarr_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngio
3
- Version: 0.2.0a1
3
+ Version: 0.2.0a2
4
4
  Summary: Next Generation file format IO
5
5
  Project-URL: homepage, https://github.com/lorenzocerrone/ngio
6
6
  Project-URL: repository, https://github.com/lorenzocerrone/ngio
@@ -17,12 +17,12 @@
17
17
  "source": [
18
18
  "import matplotlib.pyplot as plt\n",
19
19
  "\n",
20
- "from ngio.core.ngff_image import NgffImage\n",
20
+ "from ngio import open_omezarr_container\n",
21
21
  "from ngio.utils import download_ome_zarr_dataset\n",
22
22
  "\n",
23
23
  "hcs_path = download_ome_zarr_dataset(\"CardiomyocyteSmallMip\")\n",
24
24
  "image_path = hcs_path / \"B\" / \"03\" / \"0\"\n",
25
- "ngff_image = NgffImage(image_path)"
25
+ "omezarr = open_omezarr_container(image_path)"
26
26
  ]
27
27
  },
28
28
  {
@@ -31,7 +31,7 @@
31
31
  "source": [
32
32
  "## Images\n",
33
33
  "\n",
34
- "Images can be loaded from a `NgffImage` object."
34
+ "Images can be loaded from a `OmeZarrContainer` object."
35
35
  ]
36
36
  },
37
37
  {
@@ -40,11 +40,10 @@
40
40
  "metadata": {},
41
41
  "outputs": [],
42
42
  "source": [
43
- "image = ngff_image.get_image()\n",
43
+ "image = omezarr.get_image()\n",
44
44
  "\n",
45
45
  "print(\"Image information:\")\n",
46
46
  "print(f\"{image.shape=}\")\n",
47
- "print(f\"{image.axes_names=}\")\n",
48
47
  "print(f\"{image.pixel_size=}\")\n",
49
48
  "print(f\"{image.channel_labels=}\")\n",
50
49
  "print(f\"{image.dimensions=}\")"
@@ -71,18 +70,14 @@
71
70
  "metadata": {},
72
71
  "outputs": [],
73
72
  "source": [
74
- "# Get image as a dask array\n",
75
- "dask_array = image.on_disk_dask_array\n",
76
- "dask_array"
73
+ "image.get_array(mode=\"dask\") # this call is lazy"
77
74
  ]
78
75
  },
79
76
  {
80
77
  "cell_type": "markdown",
81
78
  "metadata": {},
82
79
  "source": [
83
- "Note, directly accessing the `.on_disk_array` or `.on_disk_dask_array` attributes will load the image as stored in the file.\n",
84
- "\n",
85
- "Since in principle the images can have different axes order. A safer way to access the image data is to use the `.get_array()` method, which will return the image data in canonical order (TCZYX)."
80
+ "Images can be queried for any axes, in any order"
86
81
  ]
87
82
  },
88
83
  {
@@ -91,11 +86,13 @@
91
86
  "metadata": {},
92
87
  "outputs": [],
93
88
  "source": [
94
- "image_numpy = image.get_array(\n",
95
- " c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\"\n",
96
- ")\n",
89
+ "print(\"On disk shape: \", image.shape)\n",
90
+ "\n",
91
+ "# Axes order can be specified\n",
92
+ "# if an axis is not present in the array, it will be added as a singleton dimension\n",
93
+ "array = image.get_array(axes_order=[\"x\", \"t\", \"c\", \"y\", \"z\"], mode=\"dask\")\n",
97
94
  "\n",
98
- "print(f\"{image_numpy.shape=}\")"
95
+ "print(\"Array shape: \", array.shape)"
99
96
  ]
100
97
  },
101
98
  {
@@ -113,13 +110,14 @@
113
110
  "metadata": {},
114
111
  "outputs": [],
115
112
  "source": [
116
- "roi_table = ngff_image.tables.get_table(\"FOV_ROI_table\")\n",
117
- "roi = roi_table.get_roi(\"FOV_1\")\n",
113
+ "roi_table = omezarr.get_table(\"FOV_ROI_table\", check_type=\"roi_table\")\n",
114
+ "# Get a roi by name\n",
115
+ "roi = roi_table.get(\"FOV_1\")\n",
118
116
  "print(f\"{roi=}\")\n",
119
117
  "\n",
120
- "image_roi_1 = image.get_array_from_roi(\n",
121
- " roi=roi, c=0, preserve_dimensions=True, mode=\"dask\"\n",
122
- ")\n",
118
+ "# .get_roi works exactly like .get_array\n",
119
+ "# the only difference is that x, y, z, axes are queried from the roi object\n",
120
+ "image_roi_1 = image.get_roi(roi=roi, c=0, mode=\"dask\")\n",
123
121
  "image_roi_1"
124
122
  ]
125
123
  },
@@ -136,21 +134,21 @@
136
134
  "metadata": {},
137
135
  "outputs": [],
138
136
  "source": [
139
- "image_2 = ngff_image.get_image(path=\"2\")\n",
137
+ "image_2 = omezarr.get_image(path=\"2\")\n",
140
138
  "# Two images at different resolutions\n",
141
139
  "print(f\"{image.pixel_size=}\")\n",
142
140
  "print(f\"{image_2.pixel_size=}\")\n",
143
141
  "\n",
144
142
  "# Get roi for higher resolution image\n",
145
- "image_1_roi_1 = image.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n",
143
+ "image_1_roi_1 = image.get_roi(roi=roi, c=0, mode=\"dask\")\n",
146
144
  "\n",
147
145
  "# Get roi for lower resolution image\n",
148
- "image_2_roi_1 = image_2.get_array_from_roi(roi=roi, c=0, preserve_dimensions=False)\n",
146
+ "image_2_roi_1 = image_2.get_roi(roi=roi, c=0, mode=\"dask\")\n",
149
147
  "\n",
150
148
  "# Plot the two images side by side\n",
151
149
  "fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
152
- "axs[0].imshow(image_1_roi_1[0], cmap=\"gray\")\n",
153
- "axs[1].imshow(image_2_roi_1[0], cmap=\"gray\")\n",
150
+ "axs[0].imshow(image_1_roi_1[0, 0], cmap=\"gray\")\n",
151
+ "axs[1].imshow(image_2_roi_1[0, 0], cmap=\"gray\")\n",
154
152
  "plt.show()"
155
153
  ]
156
154
  },
@@ -180,17 +178,26 @@
180
178
  "\n",
181
179
  "\n",
182
180
  "# Load the image from disk and show the edited image\n",
183
- "nuclei = ngff_image.labels.get_label(\"nuclei\")\n",
181
+ "nuclei = omezarr.get_label(\"nuclei\", path=\"0\")\n",
184
182
  "fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
185
- "axs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\")\n",
186
- "axs[1].imshow(nuclei.on_disk_array[0])\n",
183
+ "\n",
184
+ "axs[0].imshow(image.get_array()[0, 0], cmap=\"gray\")\n",
185
+ "axs[1].imshow(nuclei.get_array()[0])\n",
187
186
  "for ax in axs:\n",
188
187
  " ax.axis(\"off\")\n",
189
188
  "plt.tight_layout()\n",
190
189
  "plt.show()\n",
191
190
  "\n",
192
191
  "# Add back the original slice to the image\n",
193
- "image.set_array(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000))"
192
+ "image.set_array(patch=small_slice, x=slice(1000, 2000), y=slice(1000, 2000))\n",
193
+ "\n",
194
+ "fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
195
+ "axs[0].imshow(image.get_array()[0, 0], cmap=\"gray\")\n",
196
+ "axs[1].imshow(nuclei.get_array()[0])\n",
197
+ "for ax in axs:\n",
198
+ " ax.axis(\"off\")\n",
199
+ "plt.tight_layout()\n",
200
+ "plt.show()"
194
201
  ]
195
202
  },
196
203
  {
@@ -209,15 +216,16 @@
209
216
  "outputs": [],
210
217
  "source": [
211
218
  "# Create a a new label object and set it to a simple segmentation\n",
212
- "new_label = ngff_image.labels.derive(\"new_label\", overwrite=True)\n",
219
+ "new_label = omezarr.derive_label(\"new_label_2\", overwrite=True)\n",
213
220
  "\n",
214
- "simple_segmentation = image.on_disk_array[0] > 100\n",
215
- "new_label.on_disk_array[...] = simple_segmentation\n",
221
+ "simple_segmentation = image.get_array(c=0) > 100\n",
222
+ "simple_segmentation = simple_segmentation[0]\n",
223
+ "new_label.set_array(simple_segmentation)\n",
216
224
  "\n",
217
225
  "# make a subplot with two image show side by side\n",
218
226
  "fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
219
- "axs[0].imshow(image.on_disk_array[0, 0], cmap=\"gray\")\n",
220
- "axs[1].imshow(new_label.on_disk_array[0], cmap=\"gray\")\n",
227
+ "axs[0].imshow(image.get_array()[0, 0], cmap=\"gray\")\n",
228
+ "axs[1].imshow(new_label.get_array()[0], cmap=\"gray\")\n",
221
229
  "for ax in axs:\n",
222
230
  " ax.axis(\"off\")\n",
223
231
  "plt.tight_layout()\n",
@@ -241,15 +249,15 @@
241
249
  "metadata": {},
242
250
  "outputs": [],
243
251
  "source": [
244
- "label_0 = ngff_image.labels.get_label(\"new_label\", path=\"0\")\n",
245
- "label_2 = ngff_image.labels.get_label(\"new_label\", path=\"2\")\n",
252
+ "label_0 = omezarr.get_label(\"new_label\", path=\"0\")\n",
253
+ "label_2 = omezarr.get_label(\"new_label\", path=\"2\")\n",
246
254
  "\n",
247
- "label_before_consolidation = label_2.on_disk_array[...]\n",
255
+ "label_before_consolidation = label_2.zarr_array[...]\n",
248
256
  "\n",
249
257
  "# Consolidate the label\n",
250
258
  "label_0.consolidate()\n",
251
259
  "\n",
252
- "label_after_consolidation = label_2.on_disk_array[...]\n",
260
+ "label_after_consolidation = label_2.zarr_array[...]\n",
253
261
  "\n",
254
262
  "\n",
255
263
  "# make a subplot with two image show side by side\n",
@@ -281,42 +289,43 @@
281
289
  "import numpy as np\n",
282
290
  "import pandas as pd\n",
283
291
  "\n",
284
- "print(f\"List of feature table: {ngff_image.tables.list(table_type='feature_table')}\")\n",
292
+ "from ngio.tables import FeatureTable\n",
293
+ "\n",
294
+ "print(f\"List of all tables: {omezarr.list_tables()}\")\n",
285
295
  "\n",
286
296
  "\n",
287
- "nuclei = ngff_image.labels.get_label(\"nuclei\")\n",
297
+ "nuclei = omezarr.get_label(\"nuclei\", path=\"0\")\n",
298
+ "roi_table = omezarr.get_table(\"FOV_ROI_table\", check_type=\"roi_table\")\n",
288
299
  "\n",
289
300
  "# Create a table with random features for each nuclei in each ROI\n",
290
301
  "list_of_records = []\n",
291
- "for roi in roi_table.rois:\n",
292
- " nuclei_in_roi = nuclei.get_array_from_roi(roi, mode=\"numpy\")\n",
302
+ "for roi in roi_table.rois():\n",
303
+ " nuclei_in_roi = nuclei.get_roi(roi, mode=\"numpy\")\n",
293
304
  " for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n",
294
305
  " list_of_records.append(\n",
295
306
  " {\n",
296
307
  " \"label\": nuclei_id,\n",
297
308
  " \"feat1\": np.random.rand(),\n",
298
309
  " \"feat2\": np.random.rand(),\n",
299
- " \"ROI\": roi.infos.get(\"FieldIndex\"),\n",
310
+ " \"ROI\": roi.name,\n",
300
311
  " }\n",
301
312
  " )\n",
302
313
  "\n",
303
314
  "feat_df = pd.DataFrame.from_records(list_of_records)\n",
304
315
  "\n",
305
- "# Create a new feature table\n",
306
- "feat_table = ngff_image.tables.new(\n",
307
- " name=\"new_feature_table\",\n",
308
- " label_image=\"../nuclei\",\n",
309
- " table_type=\"feature_table\",\n",
310
- " overwrite=True,\n",
311
- ")\n",
312
- "\n",
313
- "print(\n",
314
- " f\"New list of feature table: {ngff_image.tables.list(table_type='feature_table')}\"\n",
315
- ")\n",
316
- "feat_table.set_table(feat_df)\n",
317
- "feat_table.consolidate()\n",
318
- "\n",
319
- "feat_table.table"
316
+ "feat_table = FeatureTable(feat_df, reference_label=\"nuclei\")\n",
317
+ "\n",
318
+ "omezarr.add_table(\"new_feature_table\", feat_table, overwrite=True)"
319
+ ]
320
+ },
321
+ {
322
+ "cell_type": "code",
323
+ "execution_count": null,
324
+ "metadata": {},
325
+ "outputs": [],
326
+ "source": [
327
+ "feat_table = omezarr.get_table(\"new_feature_table\")\n",
328
+ "feat_table.dataframe"
320
329
  ]
321
330
  },
322
331
  {
@@ -35,12 +35,12 @@
35
35
  "source": [
36
36
  "import matplotlib.pyplot as plt\n",
37
37
  "\n",
38
- "from ngio.core import NgffImage\n",
38
+ "from ngio import open_omezarr_container\n",
39
39
  "from ngio.utils import download_ome_zarr_dataset\n",
40
40
  "\n",
41
41
  "hcs_path = download_ome_zarr_dataset(\"CardiomyocyteTiny\")\n",
42
42
  "image_path = hcs_path / \"B\" / \"03\" / \"0\"\n",
43
- "ngff_image = NgffImage(image_path)"
43
+ "omezarr = open_omezarr_container(image_path)"
44
44
  ]
45
45
  },
46
46
  {
@@ -56,10 +56,10 @@
56
56
  "metadata": {},
57
57
  "outputs": [],
58
58
  "source": [
59
- "mip_ngff = ngff_image.derive_new_image(\n",
59
+ "mip_omezarr = omezarr.derive_image(\n",
60
60
  " \"data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n",
61
- " name=\"MIP\",\n",
62
- " on_disk_shape=(1, 1, 2160, 5120),\n",
61
+ " shape=(1, 1, 2160, 5120),\n",
62
+ " overwrite=True,\n",
63
63
  ")"
64
64
  ]
65
65
  },
@@ -78,32 +78,32 @@
78
78
  "outputs": [],
79
79
  "source": [
80
80
  "# Get the source imag\n",
81
- "source_image = ngff_image.get_image()\n",
81
+ "source_image = omezarr.get_image()\n",
82
82
  "print(\"Source image loaded with shape:\", source_image.shape)\n",
83
83
  "\n",
84
84
  "# Get the MIP image\n",
85
- "mip_image = mip_ngff.get_image()\n",
85
+ "mip_image = mip_omezarr.get_image()\n",
86
86
  "print(\"MIP image loaded with shape:\", mip_image.shape)\n",
87
87
  "\n",
88
88
  "# Get a ROI table\n",
89
- "roi_table = ngff_image.tables.get_table(\"FOV_ROI_table\")\n",
90
- "print(\"ROI table loaded with\", len(roi_table.rois), \"ROIs\")\n",
89
+ "roi_table = omezarr.get_table(\"FOV_ROI_table\", check_type=\"roi_table\")\n",
90
+ "print(\"ROI table loaded with\", len(roi_table.rois()), \"ROIs\")\n",
91
91
  "\n",
92
92
  "# For each ROI in the table\n",
93
93
  "# - get the data from the source image\n",
94
94
  "# - calculate the MIP\n",
95
95
  "# - set the data in the MIP image\n",
96
- "for roi in roi_table.rois:\n",
97
- " print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n",
98
- " patch = source_image.get_array_from_roi(roi)\n",
96
+ "for roi in roi_table.rois():\n",
97
+ " print(f\" - Processing ROI {roi.name}\")\n",
98
+ " patch = source_image.get_roi(roi)\n",
99
99
  " mip_patch = patch.max(axis=1, keepdims=True)\n",
100
- " mip_image.set_array_from_roi(patch=mip_patch, roi=roi)\n",
100
+ " mip_image.set_roi(patch=mip_patch, roi=roi)\n",
101
101
  "\n",
102
102
  "print(\"MIP image saved\")\n",
103
103
  "\n",
104
104
  "plt.figure(figsize=(5, 5))\n",
105
105
  "plt.title(\"Mip\")\n",
106
- "plt.imshow(mip_image.on_disk_array[0, 0, :, :], cmap=\"gray\")\n",
106
+ "plt.imshow(mip_image.zarr_array[0, 0, :, :], cmap=\"gray\")\n",
107
107
  "plt.axis(\"off\")\n",
108
108
  "plt.tight_layout()\n",
109
109
  "plt.show()"
@@ -126,7 +126,7 @@
126
126
  "outputs": [],
127
127
  "source": [
128
128
  "# Get the MIP image at a lower resolution\n",
129
- "mip_image_2 = mip_ngff.get_image(path=\"2\")\n",
129
+ "mip_image_2 = mip_omezarr.get_image(path=\"2\")\n",
130
130
  "\n",
131
131
  "image_before_consolidation = mip_image_2.get_array(c=0, z=0)\n",
132
132
  "\n",
@@ -137,9 +137,9 @@
137
137
  "\n",
138
138
  "fig, axs = plt.subplots(2, 1, figsize=(10, 5))\n",
139
139
  "axs[0].set_title(\"Before consolidation\")\n",
140
- "axs[0].imshow(image_before_consolidation, cmap=\"gray\")\n",
140
+ "axs[0].imshow(image_before_consolidation[0, 0], cmap=\"gray\")\n",
141
141
  "axs[1].set_title(\"After consolidation\")\n",
142
- "axs[1].imshow(image_after_consolidation, cmap=\"gray\")\n",
142
+ "axs[1].imshow(image_after_consolidation[0, 0], cmap=\"gray\")\n",
143
143
  "for ax in axs:\n",
144
144
  " ax.axis(\"off\")\n",
145
145
  "plt.tight_layout()\n",
@@ -162,18 +162,17 @@
162
162
  "metadata": {},
163
163
  "outputs": [],
164
164
  "source": [
165
- "mip_roi_table = mip_ngff.tables.new(\"FOV_ROI_table\", overwrite=True)\n",
165
+ "from ngio.tables import RoiTable\n",
166
166
  "\n",
167
167
  "roi_list = []\n",
168
- "for roi in roi_table.rois:\n",
169
- " print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n",
168
+ "for roi in roi_table.rois():\n",
169
+ " print(f\" - Processing ROI {roi.name}\")\n",
170
170
  " roi.z_length = 1 # In the MIP image, the z dimension is 1\n",
171
171
  " roi_list.append(roi)\n",
172
172
  "\n",
173
- "mip_roi_table.set_rois(roi_list, overwrite=True)\n",
174
- "mip_roi_table.consolidate()\n",
173
+ "mip_roi_table = RoiTable(rois=roi_list)\n",
175
174
  "\n",
176
- "mip_roi_table.table"
175
+ "mip_omezarr.add_table(\"FOV_ROI_table\", mip_roi_table)"
177
176
  ]
178
177
  },
179
178
  {
@@ -226,7 +225,7 @@
226
225
  "metadata": {},
227
226
  "outputs": [],
228
227
  "source": [
229
- "nuclei_image = mip_ngff.labels.derive(name=\"nuclei\", overwrite=True)"
228
+ "nuclei_image = mip_omezarr.derive_label(name=\"nuclei\", overwrite=True)"
230
229
  ]
231
230
  },
232
231
  {
@@ -243,30 +242,31 @@
243
242
  "outputs": [],
244
243
  "source": [
245
244
  "# Get the source imag\n",
246
- "source_image = mip_ngff.get_image()\n",
245
+ "source_image = mip_omezarr.get_image()\n",
247
246
  "print(\"Source image loaded with shape:\", source_image.shape)\n",
248
247
  "\n",
249
248
  "# Get a ROI table\n",
250
- "roi_table = mip_ngff.tables.get_table(\"FOV_ROI_table\")\n",
251
- "print(\"ROI table loaded with\", len(roi_table.rois), \"ROIs\")\n",
249
+ "roi_table = mip_omezarr.get_table(\"FOV_ROI_table\", check_type=\"roi_table\")\n",
250
+ "print(\"ROI table loaded with\", len(roi_table.rois()), \"ROIs\")\n",
252
251
  "\n",
253
252
  "# Find the DAPI channel\n",
254
- "dapi_idx = source_image.get_channel_idx(label=\"DAPI\")\n",
253
+ "dapi_idx = source_image.channel_labels.index(\"DAPI\")\n",
255
254
  "\n",
256
255
  "# For each ROI in the table\n",
257
256
  "# - get the data from the source image\n",
258
257
  "# - calculate the Segmentation\n",
259
258
  "# - set the data in segmentation image\n",
260
259
  "max_label = 0\n",
261
- "for roi in roi_table.rois:\n",
262
- " print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n",
263
- " patch = source_image.get_array_from_roi(roi, c=dapi_idx)\n",
260
+ "for roi in roi_table.rois():\n",
261
+ " print(f\" - Processing ROI {roi.name}\")\n",
262
+ " patch = source_image.get_roi(roi, c=dapi_idx)\n",
264
263
  " segmentation = otsu_threshold_segmentation(patch, max_label)\n",
265
264
  "\n",
266
265
  " # Add the max label of the previous segmentation to avoid overlapping labels\n",
266
+ " segmentation = segmentation[0]\n",
267
267
  " max_label = segmentation.max()\n",
268
268
  "\n",
269
- " nuclei_image.set_array_from_roi(patch=segmentation, roi=roi)\n",
269
+ " nuclei_image.set_roi(patch=segmentation, roi=roi)\n",
270
270
  "\n",
271
271
  "# Consolidate the segmentation image\n",
272
272
  "nuclei_image.consolidate()\n",
@@ -274,9 +274,9 @@
274
274
  "print(\"Segmentation image saved\")\n",
275
275
  "fig, axs = plt.subplots(2, 1, figsize=(10, 5))\n",
276
276
  "axs[0].set_title(\"MIP\")\n",
277
- "axs[0].imshow(source_image.on_disk_array[0, 0], cmap=\"gray\")\n",
277
+ "axs[0].imshow(source_image.zarr_array[0, 0], cmap=\"gray\")\n",
278
278
  "axs[1].set_title(\"Nuclei segmentation\")\n",
279
- "axs[1].imshow(nuclei_image.on_disk_array[0], cmap=rand_cmap, interpolation=\"nearest\")\n",
279
+ "axs[1].imshow(nuclei_image.zarr_array[0], cmap=rand_cmap, interpolation=\"nearest\")\n",
280
280
  "for ax in axs:\n",
281
281
  " ax.axis(\"off\")\n",
282
282
  "plt.tight_layout()\n",
@@ -203,6 +203,7 @@ def init_empty_pyramid(
203
203
  dtype=dtype,
204
204
  chunks=chunks,
205
205
  dimension_separator="/",
206
+ overwrite=True,
206
207
  )
207
208
 
208
209
  # Todo redo this with when a proper build of pyramid is implemented
@@ -236,6 +236,8 @@ class ImagesContainer:
236
236
  ref_path: str | None = None,
237
237
  shape: Collection[int] | None = None,
238
238
  chunks: Collection[int] | None = None,
239
+ xy_scaling_factor: float = 2.0,
240
+ z_scaling_factor: float = 1.0,
239
241
  overwrite: bool = False,
240
242
  ) -> "ImagesContainer":
241
243
  """Create an OME-Zarr image from a numpy array."""
@@ -245,6 +247,8 @@ class ImagesContainer:
245
247
  ref_path=ref_path,
246
248
  shape=shape,
247
249
  chunks=chunks,
250
+ xy_scaling_factor=xy_scaling_factor,
251
+ z_scaling_factor=z_scaling_factor,
248
252
  overwrite=overwrite,
249
253
  )
250
254
 
@@ -314,6 +318,8 @@ def derive_image_container(
314
318
  ref_path: str | None = None,
315
319
  shape: Collection[int] | None = None,
316
320
  chunks: Collection[int] | None = None,
321
+ xy_scaling_factor: float = 2.0,
322
+ z_scaling_factor: float = 1.0,
317
323
  overwrite: bool = False,
318
324
  ) -> ImagesContainer:
319
325
  """Create an OME-Zarr image from a numpy array."""
@@ -347,8 +353,8 @@ def derive_image_container(
347
353
  z_spacing=ref_image.pixel_size.z,
348
354
  time_spacing=ref_image.pixel_size.t,
349
355
  levels=ref_meta.levels,
350
- xy_scaling_factor=2.0, # will need to be fixed
351
- z_scaling_factor=1.0, # will need to be fixed
356
+ xy_scaling_factor=xy_scaling_factor,
357
+ z_scaling_factor=z_scaling_factor,
352
358
  time_unit=ref_image.pixel_size.time_unit,
353
359
  space_unit=ref_image.pixel_size.space_unit,
354
360
  axes_names=ref_image.dataset.axes_mapper.on_disk_axes_names,
@@ -0,0 +1,236 @@
1
+ """A module for handling label images in OME-NGFF files."""
2
+
3
+ from collections.abc import Collection
4
+ from typing import Literal
5
+
6
+ from ngio.images.abstract_image import AbstractImage, consolidate_image
7
+ from ngio.images.create import _create_empty_label
8
+ from ngio.images.image import Image
9
+ from ngio.ome_zarr_meta import (
10
+ ImplementedLabelMetaHandlers,
11
+ LabelMetaHandler,
12
+ NgioLabelMeta,
13
+ )
14
+ from ngio.ome_zarr_meta.ngio_specs import SpaceUnits, TimeUnits
15
+ from ngio.utils import (
16
+ NgioValidationError,
17
+ NgioValueError,
18
+ StoreOrGroup,
19
+ ZarrGroupHandler,
20
+ )
21
+
22
+
23
+ class Label(AbstractImage[LabelMetaHandler]):
24
+ """Placeholder class for a label."""
25
+
26
+ def __init__(
27
+ self,
28
+ group_handler: ZarrGroupHandler,
29
+ path: str,
30
+ meta_handler: LabelMetaHandler | None,
31
+ ) -> None:
32
+ """Initialize the Image at a single level.
33
+
34
+ Args:
35
+ group_handler: The Zarr group handler.
36
+ path: The path to the image in the omezarr file.
37
+ meta_handler: The image metadata handler.
38
+
39
+ """
40
+ if meta_handler is None:
41
+ meta_handler = ImplementedLabelMetaHandlers().find_meta_handler(
42
+ group_handler
43
+ )
44
+ super().__init__(
45
+ group_handler=group_handler, path=path, meta_handler=meta_handler
46
+ )
47
+
48
+ @property
49
+ def meta(self) -> NgioLabelMeta:
50
+ """Return the metadata."""
51
+ return self._meta_handler.meta
52
+
53
+ def consolidate(
54
+ self,
55
+ mode: Literal["dask", "numpy", "coarsen"] = "dask",
56
+ ) -> None:
57
+ """Consolidate the label on disk."""
58
+ consolidate_image(self, mode=mode, order=0)
59
+
60
+
61
+ class LabelsContainer:
62
+ """A class to handle the /labels group in an OME-NGFF file."""
63
+
64
+ def __init__(self, group_handler: ZarrGroupHandler) -> None:
65
+ """Initialize the LabelGroupHandler."""
66
+ self._group_handler = group_handler
67
+
68
+ # Validate the group
69
+ # Either contains a labels attribute or is empty
70
+ attrs = self._group_handler.load_attrs()
71
+ if len(attrs) == 0:
72
+ # It's an empty group
73
+ pass
74
+ elif "labels" in attrs and isinstance(attrs["labels"], list):
75
+ # It's a valid group
76
+ pass
77
+ else:
78
+ raise NgioValidationError(
79
+ f"Invalid /labels group. "
80
+ f"Expected a single labels attribute with a list of label names. "
81
+ f"Found: {attrs}"
82
+ )
83
+
84
+ def list(self) -> list[str]:
85
+ """Create the /labels group if it doesn't exist."""
86
+ attrs = self._group_handler.load_attrs()
87
+ return attrs.get("labels", [])
88
+
89
+ def get(self, name: str, path: str) -> Label:
90
+ """Get a label from the group."""
91
+ group_handler = self._group_handler.derive_handler(name)
92
+ return Label(group_handler, path, None)
93
+
94
+ def derive(
95
+ self,
96
+ name: str,
97
+ ref_image: Image,
98
+ shape: Collection[int] | None = None,
99
+ chunks: Collection[int] | None = None,
100
+ dtype: str = "uint16",
101
+ xy_scaling_factor=2.0,
102
+ z_scaling_factor=1.0,
103
+ overwrite: bool = False,
104
+ ) -> None:
105
+ """Add a label to the group."""
106
+ existing_labels = self.list()
107
+ if name in existing_labels and not overwrite:
108
+ raise NgioValueError(
109
+ f"Table '{name}' already exists in the group. "
110
+ "Use overwrite=True to replace it."
111
+ )
112
+
113
+ label_group = self._group_handler.get_group(name, create_mode=True)
114
+
115
+ _derive_label(
116
+ ref_image=ref_image,
117
+ store=label_group,
118
+ shape=shape,
119
+ chunks=chunks,
120
+ dtype=dtype,
121
+ xy_scaling_factor=xy_scaling_factor,
122
+ z_scaling_factor=z_scaling_factor,
123
+ overwrite=overwrite,
124
+ )
125
+
126
+ if name not in existing_labels:
127
+ existing_labels.append(name)
128
+ self._group_handler.write_attrs({"labels": existing_labels})
129
+
130
+ def new(
131
+ self,
132
+ name: str,
133
+ shape: Collection[int],
134
+ xy_pixelsize: float,
135
+ z_spacing: float = 1.0,
136
+ time_spacing: float = 1.0,
137
+ levels: "int | list[str]" = 5,
138
+ xy_scaling_factor: float = 2.0,
139
+ z_scaling_factor: float = 1.0,
140
+ space_unit: SpaceUnits | str | None = None,
141
+ time_unit: TimeUnits | str | None = None,
142
+ axes_names: Collection[str] | None = None,
143
+ chunks: Collection[int] | None = None,
144
+ dtype: str = "uint16",
145
+ overwrite: bool = False,
146
+ version: str = "0.4",
147
+ ) -> None:
148
+ """Add a label to the group."""
149
+ existing_labels = self.list()
150
+ if name in existing_labels and not overwrite:
151
+ raise NgioValueError(
152
+ f"Table '{name}' already exists in the group. "
153
+ "Use overwrite=True to replace it."
154
+ )
155
+
156
+ label_group = self._group_handler.get_group(name, create_mode=True)
157
+
158
+ _create_empty_label(
159
+ store=label_group,
160
+ shape=shape,
161
+ xy_pixelsize=xy_pixelsize,
162
+ z_spacing=z_spacing,
163
+ time_spacing=time_spacing,
164
+ levels=levels,
165
+ xy_scaling_factor=xy_scaling_factor,
166
+ z_scaling_factor=z_scaling_factor,
167
+ space_unit=space_unit,
168
+ time_unit=time_unit,
169
+ axes_names=axes_names,
170
+ chunks=chunks,
171
+ dtype=dtype,
172
+ overwrite=overwrite,
173
+ version=version,
174
+ )
175
+
176
+ if name not in existing_labels:
177
+ existing_labels.append(name)
178
+ self._group_handler.write_attrs({"labels": existing_labels})
179
+
180
+
181
+ def _derive_label(
182
+ ref_image: Image,
183
+ store: StoreOrGroup,
184
+ shape: Collection[int] | None = None,
185
+ chunks: Collection[int] | None = None,
186
+ dtype: str = "uint16",
187
+ xy_scaling_factor=2.0,
188
+ z_scaling_factor=1.0,
189
+ overwrite: bool = False,
190
+ ) -> None:
191
+ """Create an OME-Zarr image from a numpy array."""
192
+ ref_meta = ref_image.meta
193
+ # remove channls if present
194
+ shape_ref = ref_image.shape
195
+ chunks_ref = ref_image.chunks
196
+ axes_names_ref = ref_image.dataset.axes_mapper.on_disk_axes_names
197
+ c_axis = ref_image.dataset.axes_mapper.get_index("c")
198
+ if c_axis is not None:
199
+ shape_ref = shape_ref[:c_axis] + shape_ref[c_axis + 1 :]
200
+ chunks_ref = chunks_ref[:c_axis] + chunks_ref[c_axis + 1 :]
201
+ axes_names_ref = axes_names_ref[:c_axis] + axes_names_ref[c_axis + 1 :]
202
+
203
+ if shape is None:
204
+ shape = shape_ref
205
+
206
+ if chunks is None:
207
+ chunks = chunks_ref
208
+
209
+ if len(shape) != len(shape_ref):
210
+ raise NgioValidationError(
211
+ "The shape of the new image does not match the reference image."
212
+ )
213
+
214
+ if len(chunks) != len(chunks_ref):
215
+ raise NgioValidationError(
216
+ "The chunks of the new image does not match the reference image."
217
+ )
218
+
219
+ _ = _create_empty_label(
220
+ store=store,
221
+ shape=shape,
222
+ xy_pixelsize=ref_image.pixel_size.x,
223
+ z_spacing=ref_image.pixel_size.z,
224
+ time_spacing=ref_image.pixel_size.t,
225
+ levels=ref_meta.levels,
226
+ xy_scaling_factor=xy_scaling_factor,
227
+ z_scaling_factor=z_scaling_factor,
228
+ time_unit=ref_image.pixel_size.time_unit,
229
+ space_unit=ref_image.pixel_size.space_unit,
230
+ axes_names=axes_names_ref,
231
+ chunks=chunks,
232
+ dtype=dtype,
233
+ overwrite=overwrite,
234
+ version=ref_meta.version,
235
+ )
236
+ return None
@@ -175,6 +175,8 @@ class OmeZarrContainer:
175
175
  ref_path: str | None = None,
176
176
  shape: Collection[int] | None = None,
177
177
  chunks: Collection[int] | None = None,
178
+ xy_scaling_factor: float = 2.0,
179
+ z_scaling_factor: float = 1.0,
178
180
  copy_tables: bool = False,
179
181
  copy_labels: bool = False,
180
182
  overwrite: bool = False,
@@ -191,6 +193,8 @@ class OmeZarrContainer:
191
193
  ref_path=ref_path,
192
194
  shape=shape,
193
195
  chunks=chunks,
196
+ xy_scaling_factor=xy_scaling_factor,
197
+ z_scaling_factor=z_scaling_factor,
194
198
  overwrite=overwrite,
195
199
  )
196
200
  return OmeZarrContainer(
@@ -281,15 +285,34 @@ class OmeZarrContainer:
281
285
  raise NgioValidationError("No labels found in the image.")
282
286
  return self._labels_container.get(name=name, path=path)
283
287
 
284
- def derive_label(self, name: str, **kwargs) -> Label:
288
+ def derive_label(
289
+ self,
290
+ name: str,
291
+ ref_image: Image | None = None,
292
+ shape: Collection[int] | None = None,
293
+ chunks: Collection[int] | None = None,
294
+ dtype: str = "uint16",
295
+ xy_scaling_factor=2.0,
296
+ z_scaling_factor=1.0,
297
+ overwrite: bool = False,
298
+ ) -> Label:
285
299
  """Derive a label from an image."""
286
300
  if self._labels_container is None:
287
301
  raise NgioValidationError("No labels found in the image.")
288
302
 
289
- ref_image = self.get_image()
290
- return self._labels_container.derive(
291
- name=name, reference_image=ref_image, **kwargs
303
+ if ref_image is None:
304
+ ref_image = self.get_image()
305
+ self._labels_container.derive(
306
+ name=name,
307
+ ref_image=ref_image,
308
+ shape=shape,
309
+ chunks=chunks,
310
+ dtype=dtype,
311
+ xy_scaling_factor=xy_scaling_factor,
312
+ z_scaling_factor=z_scaling_factor,
313
+ overwrite=overwrite,
292
314
  )
315
+ return self.get_label(name, path="0")
293
316
 
294
317
 
295
318
  def open_omezarr_container(
@@ -28,7 +28,7 @@ def list_ome_zarr_datasets() -> list[str]:
28
28
  def download_ome_zarr_dataset(
29
29
  dataset_name: str,
30
30
  download_dir: str = "data",
31
- ) -> str:
31
+ ) -> Path:
32
32
  """Download an OME-Zarr dataset.
33
33
 
34
34
  To list available datasets, use `list_ome_zarr_datasets`.
@@ -1,96 +0,0 @@
1
- """A module for handling label images in OME-NGFF files."""
2
-
3
- from typing import Literal
4
-
5
- from ngio.images.abstract_image import AbstractImage, consolidate_image
6
- from ngio.ome_zarr_meta import (
7
- ImplementedLabelMetaHandlers,
8
- LabelMetaHandler,
9
- NgioLabelMeta,
10
- )
11
- from ngio.utils import (
12
- NgioValidationError,
13
- ZarrGroupHandler,
14
- )
15
-
16
-
17
- class Label(AbstractImage[LabelMetaHandler]):
18
- """Placeholder class for a label."""
19
-
20
- def __init__(
21
- self,
22
- group_handler: ZarrGroupHandler,
23
- path: str,
24
- meta_handler: LabelMetaHandler | None,
25
- ) -> None:
26
- """Initialize the Image at a single level.
27
-
28
- Args:
29
- group_handler: The Zarr group handler.
30
- path: The path to the image in the omezarr file.
31
- meta_handler: The image metadata handler.
32
-
33
- """
34
- if meta_handler is None:
35
- meta_handler = ImplementedLabelMetaHandlers().find_meta_handler(
36
- group_handler
37
- )
38
- super().__init__(
39
- group_handler=group_handler, path=path, meta_handler=meta_handler
40
- )
41
-
42
- @property
43
- def meta(self) -> NgioLabelMeta:
44
- """Return the metadata."""
45
- return self._meta_handler.meta
46
-
47
- def consolidate(
48
- self,
49
- mode: Literal["dask", "numpy", "coarsen"] = "dask",
50
- ) -> None:
51
- """Consolidate the label on disk."""
52
- consolidate_image(self, mode=mode, order=0)
53
-
54
-
55
- class LabelsContainer:
56
- """A class to handle the /labels group in an OME-NGFF file."""
57
-
58
- def __init__(self, group_handler: ZarrGroupHandler) -> None:
59
- """Initialize the LabelGroupHandler."""
60
- self._group_handler = group_handler
61
-
62
- # Validate the group
63
- # Either contains a labels attribute or is empty
64
- attrs = self._group_handler.load_attrs()
65
- if len(attrs) == 0:
66
- # It's an empty group
67
- pass
68
- elif "labels" in attrs and isinstance(attrs["labels"], list):
69
- # It's a valid group
70
- pass
71
- else:
72
- raise NgioValidationError(
73
- f"Invalid /labels group. "
74
- f"Expected a single labels attribute with a list of label names. "
75
- f"Found: {attrs}"
76
- )
77
-
78
- def list(self) -> list[str]:
79
- """Create the /labels group if it doesn't exist."""
80
- attrs = self._group_handler.load_attrs()
81
- return attrs.get("labels", [])
82
-
83
- def get(self, name: str, path: str) -> Label:
84
- """Get a label from the group."""
85
- group_handler = self._group_handler.derive_handler(name)
86
- return Label(group_handler, path, None)
87
-
88
- def derive(
89
- self,
90
- name: str,
91
- reference_image: AbstractImage,
92
- overwrite: bool = False,
93
- **kwargs,
94
- ) -> Label:
95
- """Derive a label from an image."""
96
- raise NotImplementedError
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes