mlarray 0.0.53__tar.gz → 1.0.0__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.
- {mlarray-0.0.53 → mlarray-1.0.0}/.github/workflows/workflow.yml +9 -5
- {mlarray-0.0.53 → mlarray-1.0.0}/PKG-INFO +52 -49
- {mlarray-0.0.53 → mlarray-1.0.0}/README.md +45 -46
- mlarray-1.0.0/assets/banner.png +0 -0
- mlarray-1.0.0/bench/.gitignore +2 -0
- mlarray-1.0.0/bench/README.md +8 -0
- mlarray-1.0.0/bench/benchmark_formats.py +532 -0
- mlarray-1.0.0/bench/helper/print_mla_layouts.py +85 -0
- mlarray-1.0.0/docs/api.md +47 -0
- mlarray-1.0.0/docs/cli.md +52 -0
- mlarray-1.0.0/docs/design/followups.md +40 -0
- mlarray-1.0.0/docs/design/rework-plan.md +361 -0
- mlarray-1.0.0/docs/design/spec.md +477 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/docs/optimization.md +23 -19
- mlarray-1.0.0/docs/schema.md +152 -0
- mlarray-1.0.0/docs/usage.md +246 -0
- mlarray-1.0.0/examples/example_bboxes_only.py +36 -0
- mlarray-1.0.0/examples/example_channel.py +32 -0
- mlarray-1.0.0/examples/example_compress_decompress.py +50 -0
- mlarray-1.0.0/examples/example_compressed_vs_uncompressed.py +44 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/examples/example_in_memory_constructors.py +6 -4
- mlarray-1.0.0/examples/example_metadata_only.py +21 -0
- mlarray-1.0.0/examples/example_non_spatial.py +33 -0
- mlarray-1.0.0/examples/example_open.py +28 -0
- mlarray-1.0.0/examples/example_save_load.py +41 -0
- mlarray-1.0.0/mlarray/__init__.py +77 -0
- mlarray-1.0.0/mlarray/blosc2_layout_strategies.py +202 -0
- mlarray-1.0.0/mlarray/cli.py +105 -0
- mlarray-1.0.0/mlarray/constructors.py +964 -0
- mlarray-1.0.0/mlarray/fingerprint.py +81 -0
- mlarray-1.0.0/mlarray/metadata.py +1313 -0
- mlarray-1.0.0/mlarray/metadata_io.py +125 -0
- mlarray-1.0.0/mlarray/mlarray.py +1180 -0
- mlarray-1.0.0/mlarray/schemas/mlarray-1.0.json +204 -0
- mlarray-1.0.0/mlarray/schemas/profile-medical-1.0.json +14 -0
- mlarray-1.0.0/mlarray/schemas/profile-ml-1.0.json +44 -0
- mlarray-1.0.0/mlarray/schemas/units-1.0.json +31 -0
- mlarray-1.0.0/mlarray/storage.py +332 -0
- mlarray-1.0.0/mlarray/units.py +24 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/PKG-INFO +52 -49
- mlarray-1.0.0/mlarray.egg-info/SOURCES.txt +84 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/requires.txt +4 -1
- mlarray-1.0.0/mlarray.egg-info/scm_file_list.json +66 -0
- mlarray-1.0.0/mlarray.egg-info/scm_version.json +8 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/pyproject.toml +8 -4
- mlarray-1.0.0/tests/nnunet_reference.py +197 -0
- mlarray-1.0.0/tests/test_asarray.py +145 -0
- mlarray-1.0.0/tests/test_cli.py +164 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_compress_decompress.py +37 -23
- {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_constructors.py +26 -2
- {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_create.py +22 -2
- mlarray-1.0.0/tests/test_layout.py +208 -0
- mlarray-1.0.0/tests/test_metadata_model.py +345 -0
- mlarray-1.0.0/tests/test_metadata_storage.py +252 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_open.py +47 -4
- mlarray-1.0.0/tests/test_optimization.py +177 -0
- mlarray-1.0.0/tests/test_pickle.py +117 -0
- mlarray-1.0.0/tests/test_read_ahead.py +64 -0
- mlarray-1.0.0/tests/test_save.py +191 -0
- mlarray-1.0.0/tests/test_schemas.py +84 -0
- mlarray-1.0.0/tests/test_spec_examples.py +119 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_usage.py +77 -79
- mlarray-0.0.53/MANIFEST.in +0 -1
- mlarray-0.0.53/assets/banner.png +0 -0
- mlarray-0.0.53/assets/banner.png~ +0 -0
- mlarray-0.0.53/docs/api.md +0 -35
- mlarray-0.0.53/docs/cli.md +0 -29
- mlarray-0.0.53/docs/schema.md +0 -189
- mlarray-0.0.53/docs/usage.md +0 -240
- mlarray-0.0.53/examples/example_bboxes_only.py +0 -36
- mlarray-0.0.53/examples/example_channel.py +0 -34
- mlarray-0.0.53/examples/example_compress_decompress.py +0 -56
- mlarray-0.0.53/examples/example_compressed_vs_uncompressed.py +0 -42
- mlarray-0.0.53/examples/example_metadata_only.py +0 -31
- mlarray-0.0.53/examples/example_non_spatial.py +0 -42
- mlarray-0.0.53/examples/example_open.py +0 -36
- mlarray-0.0.53/examples/example_save_load.py +0 -34
- mlarray-0.0.53/mlarray/__init__.py +0 -82
- mlarray-0.0.53/mlarray/cli.py +0 -60
- mlarray-0.0.53/mlarray/meta.py +0 -1354
- mlarray-0.0.53/mlarray/mlarray.py +0 -2154
- mlarray-0.0.53/mlarray/utils.py +0 -17
- mlarray-0.0.53/mlarray.egg-info/SOURCES.txt +0 -52
- mlarray-0.0.53/tests/test_asarray.py +0 -85
- mlarray-0.0.53/tests/test_bboxes.py +0 -67
- mlarray-0.0.53/tests/test_meta_safety.py +0 -141
- mlarray-0.0.53/tests/test_metadata.py +0 -114
- mlarray-0.0.53/tests/test_optimization.py +0 -116
- {mlarray-0.0.53 → mlarray-1.0.0}/.gitignore +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/LICENSE +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/docs/index.md +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/docs/why.md +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/examples/example_asarray.py +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mkdocs.yml +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/dependency_links.txt +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/entry_points.txt +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/top_level.txt +0 -0
- {mlarray-0.0.53 → mlarray-1.0.0}/setup.cfg +0 -0
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
strategy:
|
|
19
19
|
fail-fast: false
|
|
20
20
|
matrix:
|
|
21
|
-
python-version: ["3.10"]
|
|
21
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
22
22
|
steps:
|
|
23
23
|
- name: Checkout
|
|
24
24
|
uses: actions/checkout@v4
|
|
@@ -35,7 +35,13 @@ jobs:
|
|
|
35
35
|
|
|
36
36
|
- name: Run tests
|
|
37
37
|
run: |
|
|
38
|
-
|
|
38
|
+
status=0
|
|
39
|
+
pytest || status=$?
|
|
40
|
+
if [ "$status" -eq 5 ]; then
|
|
41
|
+
echo "No tests collected; skipping."
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
exit "$status"
|
|
39
45
|
|
|
40
46
|
publish:
|
|
41
47
|
needs: tests
|
|
@@ -61,10 +67,8 @@ jobs:
|
|
|
61
67
|
- name: Check metadata
|
|
62
68
|
run: python -m twine check dist/*
|
|
63
69
|
|
|
64
|
-
- name: Publish to PyPI
|
|
70
|
+
- name: Publish to PyPI (trusted publishing)
|
|
65
71
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
66
|
-
with:
|
|
67
|
-
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
68
72
|
|
|
69
73
|
docs:
|
|
70
74
|
needs: tests
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mlarray
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Array format specialized for Machine Learning with Blosc2 backend and standardized metadata.
|
|
5
5
|
Author-email: Karol Gotkowski <karol.gotkowski@dkfz.de>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/MIC-DKFZ/mlarray
|
|
8
8
|
Project-URL: Source, https://github.com/MIC-DKFZ/mlarray
|
|
9
9
|
Project-URL: Issues, https://github.com/MIC-DKFZ/mlarray/issues
|
|
10
|
-
Keywords:
|
|
10
|
+
Keywords: machine learning,medical imaging,blosc2,array,file format
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
16
17
|
Requires-Python: >=3.10
|
|
17
18
|
Description-Content-Type: text/markdown
|
|
18
19
|
License-File: LICENSE
|
|
19
20
|
Requires-Dist: blosc2>=3
|
|
20
21
|
Requires-Dist: numpy>=2
|
|
22
|
+
Requires-Dist: msgpack>=1.0
|
|
23
|
+
Requires-Dist: xxhash>=3.0
|
|
21
24
|
Provides-Extra: dev
|
|
22
25
|
Requires-Dist: build>=1.0; extra == "dev"
|
|
26
|
+
Requires-Dist: jsonschema>=4.18; extra == "dev"
|
|
23
27
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
24
28
|
Requires-Dist: twine>=4.0; extra == "dev"
|
|
25
29
|
Requires-Dist: setuptools_scm[toml]>=8.0; extra == "dev"
|
|
@@ -29,7 +33,7 @@ Requires-Dist: mkdocs-include-markdown-plugin>=6.0; extra == "docs"
|
|
|
29
33
|
Requires-Dist: mkdocstrings[python]>=0.25; extra == "docs"
|
|
30
34
|
Requires-Dist: pymdown-extensions>=10.0; extra == "docs"
|
|
31
35
|
Provides-Extra: all
|
|
32
|
-
Requires-Dist: medvol; extra == "all"
|
|
36
|
+
Requires-Dist: medvol>=1.0.0; extra == "all"
|
|
33
37
|
Dynamic: license-file
|
|
34
38
|
|
|
35
39
|
<p align="center">
|
|
@@ -55,7 +59,7 @@ You can install mlarray via [pip](https://pypi.org/project/mlarray/):
|
|
|
55
59
|
pip install mlarray
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
To enable the `mlarray_convert` CLI command, install MLArray with the
|
|
62
|
+
To enable the `mlarray_convert` CLI command (conversion with MedVol 1.0 or newer), install MLArray with the extra dependencies:
|
|
59
63
|
```bash
|
|
60
64
|
pip install mlarray[all]
|
|
61
65
|
```
|
|
@@ -101,7 +105,15 @@ image = MLArray.create("sample.mla", shape=array.shape, dtype=array.dtype, mmap_
|
|
|
101
105
|
image[...] = array # Modify image in memory and disk
|
|
102
106
|
```
|
|
103
107
|
|
|
104
|
-
###
|
|
108
|
+
### Fast reading
|
|
109
|
+
|
|
110
|
+
`MLArray.open` reads lazily: `image[...]` decompresses only the chunks a patch touches. The first open in a process also turns on blosc2's read-ahead (4 threads), which fetches those chunks in parallel and makes reads of data not in the page cache several times faster, especially on network storage. Change it with `mlarray.set_read_ahead(n)` (`0` turns it off) or the `BLOSC_PREFETCH` environment variable. Files are written with nnU-Net's layout and compression settings; see [ML Optimization](docs/optimization.md) for details.
|
|
111
|
+
|
|
112
|
+
### Metadata
|
|
113
|
+
|
|
114
|
+
Metadata is explicit and domain-neutral: what each axis is, where the array sits in physical space, what
|
|
115
|
+
its values mean, optional domain profiles, and your own metadata. Geometry is never implied: spacing,
|
|
116
|
+
origin and direction always refer to a declared space.
|
|
105
117
|
|
|
106
118
|
```python
|
|
107
119
|
import numpy as np
|
|
@@ -110,63 +122,44 @@ from mlarray import MLArray
|
|
|
110
122
|
array = np.random.random((64, 128, 128))
|
|
111
123
|
image = MLArray(
|
|
112
124
|
array,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
axes=["z", "y", "x"],
|
|
126
|
+
spacing=(1.5, 1.0, 1.0), # per array axis, in array order
|
|
127
|
+
origin=(10.0, 10.0, 30.0), # centre of index (0, 0, 0)
|
|
128
|
+
direction=[[0, 0, 1], [0, 1, 0], [1, 0, 0]], # column c: unit vector of array axis c
|
|
129
|
+
space="patient", # DICOM patient frame (LPS)
|
|
130
|
+
values={"kind": "measurement", "unit": "hounsfield_unit"},
|
|
131
|
+
user={"patient_id": "123"}, # any metadata of your own
|
|
117
132
|
)
|
|
118
133
|
|
|
119
|
-
print(image.spacing)
|
|
120
|
-
print(image.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
image.spacing[1] = 5.3
|
|
124
|
-
image.meta.source["study_id"] = "study-001"
|
|
134
|
+
print(image.spacing) # (1.5, 1.0, 1.0)
|
|
135
|
+
print(image.letter_codes()) # ('S', 'P', 'L')
|
|
136
|
+
image.set_geometry(spacing=(1.5, 5.3, 1.0))
|
|
137
|
+
image.user["study_id"] = "study-001"
|
|
125
138
|
image.save("with-metadata.mla")
|
|
126
139
|
|
|
127
140
|
# Open memory-mapped
|
|
128
|
-
image = MLArray.open("with-metadata.mla", mmap_mode='r+')
|
|
129
|
-
image.
|
|
141
|
+
image = MLArray.open("with-metadata.mla", mmap_mode='r+')
|
|
142
|
+
image.user["study_id"] = "new-study" # Modify metadata
|
|
130
143
|
image.close() # Close and save metadata, only necessary to save modified metadata
|
|
131
144
|
```
|
|
132
145
|
|
|
133
|
-
|
|
146
|
+
Segmentations, ML profiles and derived images:
|
|
134
147
|
|
|
135
148
|
```python
|
|
136
149
|
import numpy as np
|
|
137
150
|
from mlarray import MLArray
|
|
138
151
|
|
|
139
|
-
base = MLArray("
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
copy=base, # Copies all non-explicitly set arguments from base
|
|
152
|
+
base = MLArray("with-metadata.mla")
|
|
153
|
+
seg = MLArray(
|
|
154
|
+
np.zeros(base.shape, dtype=np.uint8),
|
|
155
|
+
copy=base, # geometry, values, profiles and user metadata of base (never its source)
|
|
156
|
+
values={"kind": "label", "labels": {"0": {"name": "background"}, "1": {"name": "liver"}}},
|
|
157
|
+
profiles={"ml": {"split": "train"}},
|
|
146
158
|
)
|
|
147
|
-
|
|
148
|
-
image.save("copied-metadata.mla")
|
|
159
|
+
seg.save("seg.mla")
|
|
149
160
|
```
|
|
150
161
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
```python
|
|
154
|
-
import numpy as np
|
|
155
|
-
from mlarray import MLArray, Meta
|
|
156
|
-
|
|
157
|
-
array = np.random.random((64, 128, 128))
|
|
158
|
-
image = MLArray(
|
|
159
|
-
array,
|
|
160
|
-
meta=Meta(source={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
print(image.meta.source) # {"patient_id": "123", "modality": "CT"}
|
|
164
|
-
print(image.meta.is_seg) # True
|
|
165
|
-
|
|
166
|
-
image.meta.source["study_id"] = "study-001"
|
|
167
|
-
image.meta.is_seg = False
|
|
168
|
-
image.save("with-metadata.mla")
|
|
169
|
-
```
|
|
162
|
+
See [Usage](docs/usage.md) and [Schema](docs/schema.md) for the full metadata model.
|
|
170
163
|
|
|
171
164
|
### Patch size variants
|
|
172
165
|
|
|
@@ -228,7 +221,8 @@ image.save("blosc2-auto.mla")
|
|
|
228
221
|
|
|
229
222
|
### mlarray_header
|
|
230
223
|
|
|
231
|
-
Print the metadata
|
|
224
|
+
Print the metadata of a `.mla` file as JSON (`--source` and `--user` add the original metadata of
|
|
225
|
+
converted files and the user metadata).
|
|
232
226
|
|
|
233
227
|
```bash
|
|
234
228
|
mlarray_header sample.mla
|
|
@@ -236,12 +230,21 @@ mlarray_header sample.mla
|
|
|
236
230
|
|
|
237
231
|
### mlarray_convert
|
|
238
232
|
|
|
239
|
-
Convert
|
|
233
|
+
Convert between MLArray and NIfTI, NRRD, MetaImage, MGH/MGZ and DICOM with
|
|
234
|
+
[MedVol](https://github.com/MIC-DKFZ/MedVol) 1.0 or newer (`pip install "mlarray[all]"`). Nothing is reoriented, rescaled or
|
|
235
|
+
dropped silently: the array keeps its stored order and dtype, the geometry is taken from the file's own fields,
|
|
236
|
+
the original header is kept in `source` so the file can be written back faithfully, and every choice is reported.
|
|
240
237
|
|
|
241
238
|
```bash
|
|
242
|
-
mlarray_convert sample.nii.gz
|
|
239
|
+
mlarray_convert sample.nii.gz sample.mla
|
|
240
|
+
mlarray_convert sample.mla roundtrip.nii.gz
|
|
241
|
+
mlarray_convert dicom_dir/ ct.mla --dicom-tilt exact
|
|
242
|
+
mlarray_convert ct.mla out_dir/ --target-format dicom # re-emits the original DICOM series
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
Ambiguities are resolved by explicit MedVol policies, e.g. `--nifti-form sform` when a NIfTI's qform and sform
|
|
246
|
+
disagree, or `--world-frame ras` to store the patient space in RAS instead of DICOM's LPS.
|
|
247
|
+
|
|
245
248
|
## Contributing
|
|
246
249
|
|
|
247
250
|
Contributions are welcome! Please open a pull request with clear changes and add tests when appropriate.
|
|
@@ -21,7 +21,7 @@ You can install mlarray via [pip](https://pypi.org/project/mlarray/):
|
|
|
21
21
|
pip install mlarray
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
To enable the `mlarray_convert` CLI command, install MLArray with the
|
|
24
|
+
To enable the `mlarray_convert` CLI command (conversion with MedVol 1.0 or newer), install MLArray with the extra dependencies:
|
|
25
25
|
```bash
|
|
26
26
|
pip install mlarray[all]
|
|
27
27
|
```
|
|
@@ -67,7 +67,15 @@ image = MLArray.create("sample.mla", shape=array.shape, dtype=array.dtype, mmap_
|
|
|
67
67
|
image[...] = array # Modify image in memory and disk
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
###
|
|
70
|
+
### Fast reading
|
|
71
|
+
|
|
72
|
+
`MLArray.open` reads lazily: `image[...]` decompresses only the chunks a patch touches. The first open in a process also turns on blosc2's read-ahead (4 threads), which fetches those chunks in parallel and makes reads of data not in the page cache several times faster, especially on network storage. Change it with `mlarray.set_read_ahead(n)` (`0` turns it off) or the `BLOSC_PREFETCH` environment variable. Files are written with nnU-Net's layout and compression settings; see [ML Optimization](docs/optimization.md) for details.
|
|
73
|
+
|
|
74
|
+
### Metadata
|
|
75
|
+
|
|
76
|
+
Metadata is explicit and domain-neutral: what each axis is, where the array sits in physical space, what
|
|
77
|
+
its values mean, optional domain profiles, and your own metadata. Geometry is never implied: spacing,
|
|
78
|
+
origin and direction always refer to a declared space.
|
|
71
79
|
|
|
72
80
|
```python
|
|
73
81
|
import numpy as np
|
|
@@ -76,63 +84,44 @@ from mlarray import MLArray
|
|
|
76
84
|
array = np.random.random((64, 128, 128))
|
|
77
85
|
image = MLArray(
|
|
78
86
|
array,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
axes=["z", "y", "x"],
|
|
88
|
+
spacing=(1.5, 1.0, 1.0), # per array axis, in array order
|
|
89
|
+
origin=(10.0, 10.0, 30.0), # centre of index (0, 0, 0)
|
|
90
|
+
direction=[[0, 0, 1], [0, 1, 0], [1, 0, 0]], # column c: unit vector of array axis c
|
|
91
|
+
space="patient", # DICOM patient frame (LPS)
|
|
92
|
+
values={"kind": "measurement", "unit": "hounsfield_unit"},
|
|
93
|
+
user={"patient_id": "123"}, # any metadata of your own
|
|
83
94
|
)
|
|
84
95
|
|
|
85
|
-
print(image.spacing)
|
|
86
|
-
print(image.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
image.spacing[1] = 5.3
|
|
90
|
-
image.meta.source["study_id"] = "study-001"
|
|
96
|
+
print(image.spacing) # (1.5, 1.0, 1.0)
|
|
97
|
+
print(image.letter_codes()) # ('S', 'P', 'L')
|
|
98
|
+
image.set_geometry(spacing=(1.5, 5.3, 1.0))
|
|
99
|
+
image.user["study_id"] = "study-001"
|
|
91
100
|
image.save("with-metadata.mla")
|
|
92
101
|
|
|
93
102
|
# Open memory-mapped
|
|
94
|
-
image = MLArray.open("with-metadata.mla", mmap_mode='r+')
|
|
95
|
-
image.
|
|
103
|
+
image = MLArray.open("with-metadata.mla", mmap_mode='r+')
|
|
104
|
+
image.user["study_id"] = "new-study" # Modify metadata
|
|
96
105
|
image.close() # Close and save metadata, only necessary to save modified metadata
|
|
97
106
|
```
|
|
98
107
|
|
|
99
|
-
|
|
108
|
+
Segmentations, ML profiles and derived images:
|
|
100
109
|
|
|
101
110
|
```python
|
|
102
111
|
import numpy as np
|
|
103
112
|
from mlarray import MLArray
|
|
104
113
|
|
|
105
|
-
base = MLArray("
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
copy=base, # Copies all non-explicitly set arguments from base
|
|
114
|
+
base = MLArray("with-metadata.mla")
|
|
115
|
+
seg = MLArray(
|
|
116
|
+
np.zeros(base.shape, dtype=np.uint8),
|
|
117
|
+
copy=base, # geometry, values, profiles and user metadata of base (never its source)
|
|
118
|
+
values={"kind": "label", "labels": {"0": {"name": "background"}, "1": {"name": "liver"}}},
|
|
119
|
+
profiles={"ml": {"split": "train"}},
|
|
112
120
|
)
|
|
113
|
-
|
|
114
|
-
image.save("copied-metadata.mla")
|
|
121
|
+
seg.save("seg.mla")
|
|
115
122
|
```
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```python
|
|
120
|
-
import numpy as np
|
|
121
|
-
from mlarray import MLArray, Meta
|
|
122
|
-
|
|
123
|
-
array = np.random.random((64, 128, 128))
|
|
124
|
-
image = MLArray(
|
|
125
|
-
array,
|
|
126
|
-
meta=Meta(source={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
print(image.meta.source) # {"patient_id": "123", "modality": "CT"}
|
|
130
|
-
print(image.meta.is_seg) # True
|
|
131
|
-
|
|
132
|
-
image.meta.source["study_id"] = "study-001"
|
|
133
|
-
image.meta.is_seg = False
|
|
134
|
-
image.save("with-metadata.mla")
|
|
135
|
-
```
|
|
124
|
+
See [Usage](docs/usage.md) and [Schema](docs/schema.md) for the full metadata model.
|
|
136
125
|
|
|
137
126
|
### Patch size variants
|
|
138
127
|
|
|
@@ -194,7 +183,8 @@ image.save("blosc2-auto.mla")
|
|
|
194
183
|
|
|
195
184
|
### mlarray_header
|
|
196
185
|
|
|
197
|
-
Print the metadata
|
|
186
|
+
Print the metadata of a `.mla` file as JSON (`--source` and `--user` add the original metadata of
|
|
187
|
+
converted files and the user metadata).
|
|
198
188
|
|
|
199
189
|
```bash
|
|
200
190
|
mlarray_header sample.mla
|
|
@@ -202,12 +192,21 @@ mlarray_header sample.mla
|
|
|
202
192
|
|
|
203
193
|
### mlarray_convert
|
|
204
194
|
|
|
205
|
-
Convert
|
|
195
|
+
Convert between MLArray and NIfTI, NRRD, MetaImage, MGH/MGZ and DICOM with
|
|
196
|
+
[MedVol](https://github.com/MIC-DKFZ/MedVol) 1.0 or newer (`pip install "mlarray[all]"`). Nothing is reoriented, rescaled or
|
|
197
|
+
dropped silently: the array keeps its stored order and dtype, the geometry is taken from the file's own fields,
|
|
198
|
+
the original header is kept in `source` so the file can be written back faithfully, and every choice is reported.
|
|
206
199
|
|
|
207
200
|
```bash
|
|
208
|
-
mlarray_convert sample.nii.gz
|
|
201
|
+
mlarray_convert sample.nii.gz sample.mla
|
|
202
|
+
mlarray_convert sample.mla roundtrip.nii.gz
|
|
203
|
+
mlarray_convert dicom_dir/ ct.mla --dicom-tilt exact
|
|
204
|
+
mlarray_convert ct.mla out_dir/ --target-format dicom # re-emits the original DICOM series
|
|
209
205
|
```
|
|
210
206
|
|
|
207
|
+
Ambiguities are resolved by explicit MedVol policies, e.g. `--nifti-form sform` when a NIfTI's qform and sform
|
|
208
|
+
disagree, or `--world-frame ras` to store the patient space in RAS instead of DICOM's LPS.
|
|
209
|
+
|
|
211
210
|
## Contributing
|
|
212
211
|
|
|
213
212
|
Contributions are welcome! Please open a pull request with clear changes and add tests when appropriate.
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Benchmark Scripts
|
|
2
|
+
|
|
3
|
+
- `benchmark_formats.py`: compares MLArray with other image file formats.
|
|
4
|
+
|
|
5
|
+
The benchmark behind MLArray's storage defaults (layout, codec, read mode and
|
|
6
|
+
read-ahead, measured on a compute cluster with nnU-Net-style loading) lives on
|
|
7
|
+
the `feat/bench-suite` branch under `bench/`; `docs/optimization.md` summarizes
|
|
8
|
+
its results.
|