ngio 0.1.4__tar.gz → 0.1.6__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.
- {ngio-0.1.4 → ngio-0.1.6}/PKG-INFO +2 -1
- {ngio-0.1.4 → ngio-0.1.6}/docs/notebooks/basic_usage.ipynb +6 -4
- {ngio-0.1.4 → ngio-0.1.6}/docs/notebooks/image.ipynb +28 -16
- {ngio-0.1.4 → ngio-0.1.6}/docs/notebooks/processing.ipynb +18 -14
- {ngio-0.1.4 → ngio-0.1.6}/pyproject.toml +1 -2
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/ngff_image.py +2 -2
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/utils.py +3 -3
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/io/_zarr_group_utils.py +1 -2
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/v04/zarr_utils.py +8 -1
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/pipes/_zoom_utils.py +46 -4
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/test_ngff_image.py +28 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/ngff_meta/test_utils.py +1 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/ngff_meta/test_v04.py +30 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/pipes/test_zoom.py +25 -0
- {ngio-0.1.4 → ngio-0.1.6}/.copier-answers.yml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.gitattributes +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.github/ISSUE_TEMPLATE.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.github/TEST_FAIL_TEMPLATE.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.github/dependabot.yml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.github/workflows/build_docs.yml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.github/workflows/ci.yml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.gitignore +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/.pre-commit-config.yaml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/LICENSE +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/README.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/_typos.toml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/docs/api/core.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/docs/getting-started.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/docs/index.md +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/mkdocs.yml +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/setup_data.sh +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/dimensions.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/image_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/image_like_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/label_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/core/roi.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/io/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/io/_zarr.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/io/_zarr_array_utils.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/iterators/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/fractal_image_meta.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/meta_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/utils.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/v04/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/ngff_meta/v04/specs.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/pipes/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/pipes/_slicer_transforms.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/pipes/_transforms.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/pipes/data_pipe.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/_ad_reader.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/_utils.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/tables_group.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/v1/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/v1/_generic_table.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/v1/feature_tables.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/v1/masking_roi_tables.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/tables/v1/roi_tables.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/utils/__init__.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/utils/_common_types.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/utils/_errors.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/utils/_logger.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/src/ngio/utils/_pydantic_utils.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/conftest.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/test_image_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/test_image_like_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/test_label_handler.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/core/test_roi.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/io/conftest.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/io/test_zarr_group_utils.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/ngff_meta/conftest.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/ngff_meta/test_fractal_image_meta.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/ngff_meta/test_pixel_size.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/pipes/conftest.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/tables/conftest.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/tables/test_table_conversion.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/tables/test_table_group.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/tables/test_v1_tables.py +0 -0
- {ngio-0.1.4 → ngio-0.1.6}/tests/tables/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ngio
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
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
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
17
|
Classifier: Typing :: Typed
|
|
17
18
|
Requires-Python: >=3.10
|
|
18
19
|
Requires-Dist: aiohttp
|
|
@@ -194,9 +194,9 @@
|
|
|
194
194
|
"outputs": [],
|
|
195
195
|
"source": [
|
|
196
196
|
"print(\"List of Tables: \", ngff_image.tables.list())\n",
|
|
197
|
-
"print(\" - Feature tables: \", ngff_image.tables.list(table_type
|
|
198
|
-
"print(\" - Roi tables: \", ngff_image.tables.list(table_type
|
|
199
|
-
"print(\" - Masking Roi tables: \", ngff_image.tables.list(table_type
|
|
197
|
+
"print(\" - Feature tables: \", ngff_image.tables.list(table_type=\"feature_table\"))\n",
|
|
198
|
+
"print(\" - Roi tables: \", ngff_image.tables.list(table_type=\"roi_table\"))\n",
|
|
199
|
+
"print(\" - Masking Roi tables: \", ngff_image.tables.list(table_type=\"masking_roi_table\"))"
|
|
200
200
|
]
|
|
201
201
|
},
|
|
202
202
|
{
|
|
@@ -267,7 +267,9 @@
|
|
|
267
267
|
"metadata": {},
|
|
268
268
|
"outputs": [],
|
|
269
269
|
"source": [
|
|
270
|
-
"new_ngff_image = ngff_image.derive_new_image(\
|
|
270
|
+
"new_ngff_image = ngff_image.derive_new_image(\n",
|
|
271
|
+
" \"../../data/new_ome.zarr\", name=\"new_image\"\n",
|
|
272
|
+
")\n",
|
|
271
273
|
"print(new_ngff_image)"
|
|
272
274
|
]
|
|
273
275
|
},
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"\n",
|
|
20
20
|
"from ngio.core.ngff_image import NgffImage\n",
|
|
21
21
|
"\n",
|
|
22
|
-
"ngff_image = NgffImage(\"
|
|
22
|
+
"ngff_image = NgffImage(\n",
|
|
23
|
+
" \"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1_mip.zarr/B/03/0\"\n",
|
|
24
|
+
")"
|
|
23
25
|
]
|
|
24
26
|
},
|
|
25
27
|
{
|
|
@@ -88,7 +90,9 @@
|
|
|
88
90
|
"metadata": {},
|
|
89
91
|
"outputs": [],
|
|
90
92
|
"source": [
|
|
91
|
-
"image_numpy = image.get_array(
|
|
93
|
+
"image_numpy = image.get_array(\n",
|
|
94
|
+
" c=0, x=slice(0, 250), y=slice(0, 250), preserve_dimensions=False, mode=\"numpy\"\n",
|
|
95
|
+
")\n",
|
|
92
96
|
"\n",
|
|
93
97
|
"print(f\"{image_numpy.shape=}\")"
|
|
94
98
|
]
|
|
@@ -112,7 +116,9 @@
|
|
|
112
116
|
"roi = roi_table.get_roi(\"FOV_1\")\n",
|
|
113
117
|
"print(f\"{roi=}\")\n",
|
|
114
118
|
"\n",
|
|
115
|
-
"image_roi_1 = image.get_array_from_roi(
|
|
119
|
+
"image_roi_1 = image.get_array_from_roi(\n",
|
|
120
|
+
" roi=roi, c=0, preserve_dimensions=True, mode=\"dask\"\n",
|
|
121
|
+
")\n",
|
|
116
122
|
"image_roi_1"
|
|
117
123
|
]
|
|
118
124
|
},
|
|
@@ -277,29 +283,35 @@
|
|
|
277
283
|
"print(f\"List of feature table: {ngff_image.tables.list(table_type='feature_table')}\")\n",
|
|
278
284
|
"\n",
|
|
279
285
|
"\n",
|
|
280
|
-
"nuclei = ngff_image.labels.get_label(
|
|
286
|
+
"nuclei = ngff_image.labels.get_label(\"nuclei\")\n",
|
|
281
287
|
"\n",
|
|
282
288
|
"# Create a table with random features for each nuclei in each ROI\n",
|
|
283
289
|
"list_of_records = []\n",
|
|
284
290
|
"for roi in roi_table.rois:\n",
|
|
285
|
-
" nuclei_in_roi = nuclei.get_array_from_roi(roi, mode
|
|
291
|
+
" nuclei_in_roi = nuclei.get_array_from_roi(roi, mode=\"numpy\")\n",
|
|
286
292
|
" for nuclei_id in np.unique(nuclei_in_roi)[1:]:\n",
|
|
287
293
|
" list_of_records.append(\n",
|
|
288
|
-
" {\
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
294
|
+
" {\n",
|
|
295
|
+
" \"label\": nuclei_id,\n",
|
|
296
|
+
" \"feat1\": np.random.rand(),\n",
|
|
297
|
+
" \"feat2\": np.random.rand(),\n",
|
|
298
|
+
" \"ROI\": roi.infos.get(\"FieldIndex\"),\n",
|
|
299
|
+
" }\n",
|
|
300
|
+
" )\n",
|
|
293
301
|
"\n",
|
|
294
302
|
"feat_df = pd.DataFrame.from_records(list_of_records)\n",
|
|
295
303
|
"\n",
|
|
296
304
|
"# Create a new feature table\n",
|
|
297
|
-
"feat_table = ngff_image.tables.new(
|
|
298
|
-
"
|
|
299
|
-
"
|
|
300
|
-
"
|
|
301
|
-
"
|
|
302
|
-
"
|
|
305
|
+
"feat_table = ngff_image.tables.new(\n",
|
|
306
|
+
" name=\"new_feature_table\",\n",
|
|
307
|
+
" label_image=\"../nuclei\",\n",
|
|
308
|
+
" table_type=\"feature_table\",\n",
|
|
309
|
+
" overwrite=True,\n",
|
|
310
|
+
")\n",
|
|
311
|
+
"\n",
|
|
312
|
+
"print(\n",
|
|
313
|
+
" f\"New list of feature table: {ngff_image.tables.list(table_type='feature_table')}\"\n",
|
|
314
|
+
")\n",
|
|
303
315
|
"feat_table.set_table(feat_df)\n",
|
|
304
316
|
"feat_table.consolidate()\n",
|
|
305
317
|
"\n",
|
|
@@ -37,7 +37,9 @@
|
|
|
37
37
|
"\n",
|
|
38
38
|
"from ngio.core import NgffImage\n",
|
|
39
39
|
"\n",
|
|
40
|
-
"ngff_image = NgffImage(\"
|
|
40
|
+
"ngff_image = NgffImage(\n",
|
|
41
|
+
" \"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0\"\n",
|
|
42
|
+
")"
|
|
41
43
|
]
|
|
42
44
|
},
|
|
43
45
|
{
|
|
@@ -53,9 +55,11 @@
|
|
|
53
55
|
"metadata": {},
|
|
54
56
|
"outputs": [],
|
|
55
57
|
"source": [
|
|
56
|
-
"mip_ngff = ngff_image.derive_new_image(\
|
|
57
|
-
"
|
|
58
|
-
"
|
|
58
|
+
"mip_ngff = ngff_image.derive_new_image(\n",
|
|
59
|
+
" \"../../data/20200812-CardiomyocyteDifferentiation14-Cycle1.zarr/B/03/0_mip\",\n",
|
|
60
|
+
" name=\"MIP\",\n",
|
|
61
|
+
" on_disk_shape=(1, 1, 2160, 5120),\n",
|
|
62
|
+
")"
|
|
59
63
|
]
|
|
60
64
|
},
|
|
61
65
|
{
|
|
@@ -93,15 +97,15 @@
|
|
|
93
97
|
" patch = source_image.get_array_from_roi(roi)\n",
|
|
94
98
|
" mip_patch = patch.max(axis=1, keepdims=True)\n",
|
|
95
99
|
" mip_image.set_array_from_roi(patch=mip_patch, roi=roi)\n",
|
|
96
|
-
"
|
|
100
|
+
"\n",
|
|
97
101
|
"print(\"MIP image saved\")\n",
|
|
98
102
|
"\n",
|
|
99
103
|
"plt.figure(figsize=(5, 5))\n",
|
|
100
104
|
"plt.title(\"Mip\")\n",
|
|
101
105
|
"plt.imshow(mip_image.on_disk_array[0, 0, :, :], cmap=\"gray\")\n",
|
|
102
|
-
"plt.axis(
|
|
106
|
+
"plt.axis(\"off\")\n",
|
|
103
107
|
"plt.tight_layout()\n",
|
|
104
|
-
"plt.show()
|
|
108
|
+
"plt.show()"
|
|
105
109
|
]
|
|
106
110
|
},
|
|
107
111
|
{
|
|
@@ -136,9 +140,9 @@
|
|
|
136
140
|
"axs[1].set_title(\"After consolidation\")\n",
|
|
137
141
|
"axs[1].imshow(image_after_consolidation, cmap=\"gray\")\n",
|
|
138
142
|
"for ax in axs:\n",
|
|
139
|
-
" ax.axis(
|
|
143
|
+
" ax.axis(\"off\")\n",
|
|
140
144
|
"plt.tight_layout()\n",
|
|
141
|
-
"plt.show()
|
|
145
|
+
"plt.show()"
|
|
142
146
|
]
|
|
143
147
|
},
|
|
144
148
|
{
|
|
@@ -162,7 +166,7 @@
|
|
|
162
166
|
"roi_list = []\n",
|
|
163
167
|
"for roi in roi_table.rois:\n",
|
|
164
168
|
" print(f\" - Processing ROI {roi.infos.get('field_index')}\")\n",
|
|
165
|
-
" roi.z_length = 1
|
|
169
|
+
" roi.z_length = 1 # In the MIP image, the z dimension is 1\n",
|
|
166
170
|
" roi_list.append(roi)\n",
|
|
167
171
|
"\n",
|
|
168
172
|
"mip_roi_table.set_rois(roi_list, overwrite=True)\n",
|
|
@@ -198,7 +202,7 @@
|
|
|
198
202
|
"rand_cmap = ListedColormap(rand_cmap)\n",
|
|
199
203
|
"\n",
|
|
200
204
|
"\n",
|
|
201
|
-
"def otsu_threshold_segmentation(image: np.ndarray, max_label:int) -> np.ndarray:\n",
|
|
205
|
+
"def otsu_threshold_segmentation(image: np.ndarray, max_label: int) -> np.ndarray:\n",
|
|
202
206
|
" \"\"\"Simple segmentation using Otsu thresholding.\"\"\"\n",
|
|
203
207
|
" threshold = threshold_otsu(image)\n",
|
|
204
208
|
" binary = image > threshold\n",
|
|
@@ -271,11 +275,11 @@
|
|
|
271
275
|
"axs[0].set_title(\"MIP\")\n",
|
|
272
276
|
"axs[0].imshow(source_image.on_disk_array[0, 0], cmap=\"gray\")\n",
|
|
273
277
|
"axs[1].set_title(\"Nuclei segmentation\")\n",
|
|
274
|
-
"axs[1].imshow(nuclei_image.on_disk_array[0], cmap=rand_cmap, interpolation
|
|
278
|
+
"axs[1].imshow(nuclei_image.on_disk_array[0], cmap=rand_cmap, interpolation=\"nearest\")\n",
|
|
275
279
|
"for ax in axs:\n",
|
|
276
|
-
" ax.axis(
|
|
280
|
+
" ax.axis(\"off\")\n",
|
|
277
281
|
"plt.tight_layout()\n",
|
|
278
|
-
"plt.show()
|
|
282
|
+
"plt.show()"
|
|
279
283
|
]
|
|
280
284
|
},
|
|
281
285
|
{
|
|
@@ -29,6 +29,7 @@ classifiers = [
|
|
|
29
29
|
"Programming Language :: Python :: 3.10",
|
|
30
30
|
"Programming Language :: Python :: 3.11",
|
|
31
31
|
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
32
33
|
"Typing :: Typed",
|
|
33
34
|
]
|
|
34
35
|
# add your package dependencies here
|
|
@@ -199,8 +200,6 @@ channels = ["conda-forge"]
|
|
|
199
200
|
platforms = ["osx-arm64", "linux-64", "win-64"]
|
|
200
201
|
|
|
201
202
|
[tool.pixi.pypi-dependencies]
|
|
202
|
-
# zarr = { path = "../zarr-python/", editable = true }
|
|
203
|
-
# anndata = { path = "../anndata/", editable = true }
|
|
204
203
|
ngio = { path = ".", editable = true }
|
|
205
204
|
|
|
206
205
|
[tool.pixi.feature.py310.dependencies]
|
|
@@ -152,7 +152,7 @@ class NgffImage:
|
|
|
152
152
|
|
|
153
153
|
if meta.omero is None:
|
|
154
154
|
raise NotImplementedError(
|
|
155
|
-
"OMERO metadata not found.
|
|
155
|
+
"OMERO metadata not found. Please add OMERO metadata to the image."
|
|
156
156
|
)
|
|
157
157
|
|
|
158
158
|
channel_list = meta.omero.channels
|
|
@@ -269,7 +269,7 @@ class NgffImage:
|
|
|
269
269
|
|
|
270
270
|
if meta.omero is None:
|
|
271
271
|
raise NotImplementedError(
|
|
272
|
-
"OMERO metadata not found.
|
|
272
|
+
"OMERO metadata not found. Please add OMERO metadata to the image."
|
|
273
273
|
)
|
|
274
274
|
|
|
275
275
|
channel_list = meta.omero.channels
|
|
@@ -67,17 +67,17 @@ def _build_empty_pyramid(
|
|
|
67
67
|
|
|
68
68
|
if chunks is not None and len(on_disk_shape) != len(chunks):
|
|
69
69
|
raise ValueError(
|
|
70
|
-
"The shape and chunks must have the same number
|
|
70
|
+
"The shape and chunks must have the same number of dimensions."
|
|
71
71
|
)
|
|
72
72
|
|
|
73
73
|
if len(on_disk_shape) != len(scaling_factor):
|
|
74
74
|
raise ValueError(
|
|
75
|
-
"The shape and scaling factor must have the same number
|
|
75
|
+
"The shape and scaling factor must have the same number of dimensions."
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
if len(on_disk_shape) != len(on_disk_axis):
|
|
79
79
|
raise ValueError(
|
|
80
|
-
"The shape and on-disk axis must have the same number
|
|
80
|
+
"The shape and on-disk axis must have the same number of dimensions."
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
for dataset in image_meta.datasets:
|
|
@@ -50,8 +50,7 @@ def open_group_wrapper(
|
|
|
50
50
|
if isinstance(store, zarr.Group):
|
|
51
51
|
_group = _pass_through_group(store, mode=mode, zarr_format=zarr_format)
|
|
52
52
|
ngio_logger.debug(
|
|
53
|
-
f"Passing through group: {_group}, "
|
|
54
|
-
f"located in store: {_group.store.path}"
|
|
53
|
+
f"Passing through group: {_group}, located in store: {_group.store.path}"
|
|
55
54
|
)
|
|
56
55
|
return _group
|
|
57
56
|
|
|
@@ -110,7 +110,13 @@ def vanilla_omero_v04_to_fractal(omero04: Omero04) -> Omero:
|
|
|
110
110
|
for channel04 in omero04.channels:
|
|
111
111
|
# Convert the window to a dictionary
|
|
112
112
|
label = channel04.label
|
|
113
|
-
|
|
113
|
+
|
|
114
|
+
if "wavelength_id" in channel04.extra_fields:
|
|
115
|
+
# If the wavelength_id is present, pop it from the extra fields
|
|
116
|
+
# so that it is not added to the channel_visualisation
|
|
117
|
+
wavelength_id = channel04.extra_fields.pop("wavelength_id")
|
|
118
|
+
else:
|
|
119
|
+
wavelength_id = label
|
|
114
120
|
|
|
115
121
|
if channel04.window is None:
|
|
116
122
|
window04 = Window04(
|
|
@@ -161,6 +167,7 @@ def fractal_omero_to_vanilla_v04(omero: Omero) -> Omero04:
|
|
|
161
167
|
color=channel.channel_visualisation.color,
|
|
162
168
|
active=channel.channel_visualisation.active,
|
|
163
169
|
window=window04,
|
|
170
|
+
wavelength_id=channel.wavelength_id,
|
|
164
171
|
**channel.channel_visualisation.extra_fields,
|
|
165
172
|
)
|
|
166
173
|
list_channels04.append(channel04)
|
|
@@ -4,7 +4,48 @@ from typing import Literal
|
|
|
4
4
|
import dask.array as da
|
|
5
5
|
import numpy as np
|
|
6
6
|
import zarr
|
|
7
|
-
from scipy.ndimage import zoom
|
|
7
|
+
from scipy.ndimage import zoom as scipy_zoom
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _stacked_zoom(x, zoom_y, zoom_x, order=1, mode="grid-constant", grid_mode=True):
|
|
11
|
+
*rest, yshape, xshape = x.shape
|
|
12
|
+
x = x.reshape(-1, yshape, xshape)
|
|
13
|
+
scale_xy = (zoom_y, zoom_x)
|
|
14
|
+
x_out = np.stack(
|
|
15
|
+
[
|
|
16
|
+
scipy_zoom(x[i], scale_xy, order=order, mode=mode, grid_mode=True)
|
|
17
|
+
for i in range(x.shape[0])
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
return x_out.reshape(*rest, *x_out.shape[1:])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def fast_zoom(x, zoom, order=1, mode="grid-constant", grid_mode=True, auto_stack=True):
|
|
24
|
+
"""Fast zoom function.
|
|
25
|
+
|
|
26
|
+
Scipy zoom function that can handle singleton dimensions
|
|
27
|
+
but the performance degrades with the number of dimensions.
|
|
28
|
+
|
|
29
|
+
This function has two small optimizations:
|
|
30
|
+
- it removes singleton dimensions before calling zoom
|
|
31
|
+
- if it detects that the zoom is only on the last two dimensions
|
|
32
|
+
it stacks the first dimensions to call zoom only on the last two.
|
|
33
|
+
"""
|
|
34
|
+
mask = np.isclose(x.shape, 1)
|
|
35
|
+
zoom = np.array(zoom)
|
|
36
|
+
singletons = tuple(np.where(mask)[0])
|
|
37
|
+
xs = np.squeeze(x, axis=singletons)
|
|
38
|
+
new_zoom = zoom[~mask]
|
|
39
|
+
|
|
40
|
+
*zoom_rest, zoom_y, zoom_x = new_zoom
|
|
41
|
+
if auto_stack and np.allclose(zoom_rest, 1):
|
|
42
|
+
xs = _stacked_zoom(
|
|
43
|
+
xs, zoom_y, zoom_x, order=order, mode=mode, grid_mode=grid_mode
|
|
44
|
+
)
|
|
45
|
+
else:
|
|
46
|
+
xs = scipy_zoom(xs, new_zoom, order=order, mode=mode, grid_mode=grid_mode)
|
|
47
|
+
x = np.expand_dims(xs, axis=singletons)
|
|
48
|
+
return x
|
|
8
49
|
|
|
9
50
|
|
|
10
51
|
def _zoom_inputs_check(
|
|
@@ -73,7 +114,7 @@ def _dask_zoom(
|
|
|
73
114
|
block_output_shape = tuple(np.ceil(better_source_chunks * _scale).astype(int))
|
|
74
115
|
|
|
75
116
|
zoom_wrapper = partial(
|
|
76
|
-
|
|
117
|
+
fast_zoom, zoom=_scale, order=order, mode="grid-constant", grid_mode=True
|
|
77
118
|
)
|
|
78
119
|
|
|
79
120
|
out_array = da.map_blocks(
|
|
@@ -109,7 +150,7 @@ def _numpy_zoom(
|
|
|
109
150
|
source_array=source_array, scale=scale, target_shape=target_shape
|
|
110
151
|
)
|
|
111
152
|
|
|
112
|
-
out_array =
|
|
153
|
+
out_array = fast_zoom(
|
|
113
154
|
source_array, zoom=_scale, order=order, mode="grid-constant", grid_mode=True
|
|
114
155
|
)
|
|
115
156
|
assert isinstance(out_array, np.ndarray)
|
|
@@ -149,6 +190,7 @@ def on_disk_zoom(
|
|
|
149
190
|
target_array = _dask_zoom(source_array, target_shape=target.shape, order=order)
|
|
150
191
|
|
|
151
192
|
target_array = target_array.rechunk(target.chunks)
|
|
193
|
+
target_array.compute_chunk_sizes()
|
|
152
194
|
target_array.to_zarr(target)
|
|
153
195
|
|
|
154
196
|
|
|
@@ -180,7 +222,7 @@ def on_disk_coarsen(
|
|
|
180
222
|
coarsening_setup[i] = int(factor)
|
|
181
223
|
else:
|
|
182
224
|
raise ValueError(
|
|
183
|
-
"Coarsening factor must be an integer, got
|
|
225
|
+
f"Coarsening factor must be an integer, got {factor} on axis {i}"
|
|
184
226
|
)
|
|
185
227
|
|
|
186
228
|
out_target = da.coarsen(
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class TestNgffImage:
|
|
5
7
|
def test_ngff_image(self, ome_zarr_image_v04_path: Path) -> None:
|
|
@@ -57,3 +59,29 @@ class TestNgffImage:
|
|
|
57
59
|
|
|
58
60
|
assert ngff_image.tables.list() == new_ngff_image.tables.list()
|
|
59
61
|
assert ngff_image.labels.list() == new_ngff_image.labels.list()
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize(
|
|
64
|
+
"shape, axis, chunks",
|
|
65
|
+
[
|
|
66
|
+
((1, 4, 1, 1945, 1945), ("t", "c", "z", "y", "x"), (1, 1, 1, 1000, 1000)),
|
|
67
|
+
((1, 4, 1, 1945, 1945), ("t", "c", "z", "y", "x"), None),
|
|
68
|
+
((1, 4, 1, 1945, 2000), ("t", "c", "z", "y", "x"), None),
|
|
69
|
+
((1, 1, 1000, 1000), ("c", "z", "y", "x"), (1, 1, 1000, 1000)),
|
|
70
|
+
((1, 1, 1000, 1000), ("c", "z", "y", "x"), None),
|
|
71
|
+
((739, 1033), ("y", "x"), (53, 173)),
|
|
72
|
+
],
|
|
73
|
+
)
|
|
74
|
+
def test_ngff_image_consolidate(self, tmp_path, shape, axis, chunks) -> None:
|
|
75
|
+
from ngio import NgffImage
|
|
76
|
+
from ngio.core.utils import create_empty_ome_zarr_image
|
|
77
|
+
|
|
78
|
+
ome_zarr = tmp_path / "test_consolidate.zarr"
|
|
79
|
+
create_empty_ome_zarr_image(
|
|
80
|
+
ome_zarr,
|
|
81
|
+
on_disk_shape=shape,
|
|
82
|
+
on_disk_axis=axis,
|
|
83
|
+
chunks=chunks,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
image = NgffImage(ome_zarr).get_image()
|
|
87
|
+
image.consolidate()
|
|
@@ -22,6 +22,7 @@ class TestUtils:
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
assert meta.channel_labels == ["DAPI", "nanog", "Lamin B1"]
|
|
25
|
+
assert meta.channel_wavelength_ids == ["A01_C01", "A02_C02", "A03_C03"]
|
|
25
26
|
np.testing.assert_array_equal(meta.pixel_size(idx=0).zyx, [1.0, 1.0, 1.0])
|
|
26
27
|
np.testing.assert_array_equal(meta.scale(idx=0), [1.0, 1.0, 1.0, 1.0, 1.0])
|
|
27
28
|
np.testing.assert_array_equal(meta.pixel_size(path="2").zyx, [1.0, 4.0, 4.0])
|
|
@@ -4,6 +4,36 @@ import pytest
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class TestOMEZarrHandlerV04:
|
|
7
|
+
def test_create_image_meta(self, tmp_path):
|
|
8
|
+
from ngio.ngff_meta import create_image_metadata
|
|
9
|
+
from ngio.ngff_meta.v04.zarr_utils import (
|
|
10
|
+
fractal_ngff_image_meta_to_vanilla_v04,
|
|
11
|
+
vanilla_ngff_image_meta_v04_to_fractal,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
meta = create_image_metadata(
|
|
15
|
+
on_disk_axis=("t", "c", "z", "y", "x"),
|
|
16
|
+
pixel_sizes=None,
|
|
17
|
+
xy_scaling_factor=2.0,
|
|
18
|
+
z_scaling_factor=1.0,
|
|
19
|
+
time_spacing=1.0,
|
|
20
|
+
time_units="s",
|
|
21
|
+
levels=5,
|
|
22
|
+
name="test",
|
|
23
|
+
channel_labels=["DAPI", "nanog", "Lamin B1"],
|
|
24
|
+
channel_wavelengths=["A01_C01", "A02_C02", "A03_C03"],
|
|
25
|
+
channel_visualization=None,
|
|
26
|
+
omero_kwargs=None,
|
|
27
|
+
version="0.4",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
meta04 = fractal_ngff_image_meta_to_vanilla_v04(meta)
|
|
31
|
+
meta2 = vanilla_ngff_image_meta_v04_to_fractal(meta04)
|
|
32
|
+
assert meta.channel_labels == meta2.channel_labels
|
|
33
|
+
assert meta.channel_wavelength_ids == meta2.channel_wavelength_ids
|
|
34
|
+
assert meta.axes_names == meta2.axes_names
|
|
35
|
+
assert meta.scale(path="2") == meta2.scale(path="2")
|
|
36
|
+
|
|
7
37
|
def test_basic_workflow(self, ome_zarr_image_v04_path):
|
|
8
38
|
from ngio.ngff_meta import get_ngff_image_meta_handler
|
|
9
39
|
from ngio.ngff_meta.v04.zarr_utils import NgffImageMeta04
|
|
@@ -75,3 +75,28 @@ class TestZoom:
|
|
|
75
75
|
source, target = zarr_zoom_2d_array_not_int
|
|
76
76
|
with pytest.raises(ValueError):
|
|
77
77
|
self._test_coarsen(source, target)
|
|
78
|
+
|
|
79
|
+
@pytest.mark.parametrize(
|
|
80
|
+
"shape, zoom_factor",
|
|
81
|
+
[
|
|
82
|
+
((10, 1, 1, 30, 30), (1, 1, 1, 0.5, 0.5)), # with time
|
|
83
|
+
((1, 1, 1, 300, 300), (1, 1, 1, 0.5, 0.5)), # with singletons
|
|
84
|
+
((1, 4, 10, 30, 30), (1, 1, 1, 0.5, 0.5)), # with channels and z
|
|
85
|
+
((1, 4, 10, 30, 30), (1, 1, 0.3234, 0.5, 0.5)), # with channels and z
|
|
86
|
+
((5, 4, 10, 30, 30), (0.5, 1, 0.3234, 0.5, 0.5)), # with channels and z
|
|
87
|
+
((1, 4, 10, 30, 30), (1, 1, 1, 0.5323, 0.5231)), # with channels and z
|
|
88
|
+
((4, 300, 300), (1, 0.5, 0.5)), # without time and channels
|
|
89
|
+
((3000, 3000), (0.5, 0.5)), # without time and channels
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
def test_fast_zoom(self, shape, zoom_factor) -> None:
|
|
93
|
+
from scipy.ndimage import zoom
|
|
94
|
+
|
|
95
|
+
from ngio.pipes._zoom_utils import fast_zoom
|
|
96
|
+
|
|
97
|
+
x = np.random.rand(*shape)
|
|
98
|
+
out1 = fast_zoom(
|
|
99
|
+
x, zoom=zoom_factor, order=1, mode="grid-constant", grid_mode=True
|
|
100
|
+
)
|
|
101
|
+
out2 = zoom(x, zoom=zoom_factor, order=1, mode="grid-constant", grid_mode=True)
|
|
102
|
+
np.testing.assert_allclose(out1, out2)
|
|
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
|
|
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
|
|
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
|