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.
Files changed (98) hide show
  1. {mlarray-0.0.53 → mlarray-1.0.0}/.github/workflows/workflow.yml +9 -5
  2. {mlarray-0.0.53 → mlarray-1.0.0}/PKG-INFO +52 -49
  3. {mlarray-0.0.53 → mlarray-1.0.0}/README.md +45 -46
  4. mlarray-1.0.0/assets/banner.png +0 -0
  5. mlarray-1.0.0/bench/.gitignore +2 -0
  6. mlarray-1.0.0/bench/README.md +8 -0
  7. mlarray-1.0.0/bench/benchmark_formats.py +532 -0
  8. mlarray-1.0.0/bench/helper/print_mla_layouts.py +85 -0
  9. mlarray-1.0.0/docs/api.md +47 -0
  10. mlarray-1.0.0/docs/cli.md +52 -0
  11. mlarray-1.0.0/docs/design/followups.md +40 -0
  12. mlarray-1.0.0/docs/design/rework-plan.md +361 -0
  13. mlarray-1.0.0/docs/design/spec.md +477 -0
  14. {mlarray-0.0.53 → mlarray-1.0.0}/docs/optimization.md +23 -19
  15. mlarray-1.0.0/docs/schema.md +152 -0
  16. mlarray-1.0.0/docs/usage.md +246 -0
  17. mlarray-1.0.0/examples/example_bboxes_only.py +36 -0
  18. mlarray-1.0.0/examples/example_channel.py +32 -0
  19. mlarray-1.0.0/examples/example_compress_decompress.py +50 -0
  20. mlarray-1.0.0/examples/example_compressed_vs_uncompressed.py +44 -0
  21. {mlarray-0.0.53 → mlarray-1.0.0}/examples/example_in_memory_constructors.py +6 -4
  22. mlarray-1.0.0/examples/example_metadata_only.py +21 -0
  23. mlarray-1.0.0/examples/example_non_spatial.py +33 -0
  24. mlarray-1.0.0/examples/example_open.py +28 -0
  25. mlarray-1.0.0/examples/example_save_load.py +41 -0
  26. mlarray-1.0.0/mlarray/__init__.py +77 -0
  27. mlarray-1.0.0/mlarray/blosc2_layout_strategies.py +202 -0
  28. mlarray-1.0.0/mlarray/cli.py +105 -0
  29. mlarray-1.0.0/mlarray/constructors.py +964 -0
  30. mlarray-1.0.0/mlarray/fingerprint.py +81 -0
  31. mlarray-1.0.0/mlarray/metadata.py +1313 -0
  32. mlarray-1.0.0/mlarray/metadata_io.py +125 -0
  33. mlarray-1.0.0/mlarray/mlarray.py +1180 -0
  34. mlarray-1.0.0/mlarray/schemas/mlarray-1.0.json +204 -0
  35. mlarray-1.0.0/mlarray/schemas/profile-medical-1.0.json +14 -0
  36. mlarray-1.0.0/mlarray/schemas/profile-ml-1.0.json +44 -0
  37. mlarray-1.0.0/mlarray/schemas/units-1.0.json +31 -0
  38. mlarray-1.0.0/mlarray/storage.py +332 -0
  39. mlarray-1.0.0/mlarray/units.py +24 -0
  40. {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/PKG-INFO +52 -49
  41. mlarray-1.0.0/mlarray.egg-info/SOURCES.txt +84 -0
  42. {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/requires.txt +4 -1
  43. mlarray-1.0.0/mlarray.egg-info/scm_file_list.json +66 -0
  44. mlarray-1.0.0/mlarray.egg-info/scm_version.json +8 -0
  45. {mlarray-0.0.53 → mlarray-1.0.0}/pyproject.toml +8 -4
  46. mlarray-1.0.0/tests/nnunet_reference.py +197 -0
  47. mlarray-1.0.0/tests/test_asarray.py +145 -0
  48. mlarray-1.0.0/tests/test_cli.py +164 -0
  49. {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_compress_decompress.py +37 -23
  50. {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_constructors.py +26 -2
  51. {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_create.py +22 -2
  52. mlarray-1.0.0/tests/test_layout.py +208 -0
  53. mlarray-1.0.0/tests/test_metadata_model.py +345 -0
  54. mlarray-1.0.0/tests/test_metadata_storage.py +252 -0
  55. {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_open.py +47 -4
  56. mlarray-1.0.0/tests/test_optimization.py +177 -0
  57. mlarray-1.0.0/tests/test_pickle.py +117 -0
  58. mlarray-1.0.0/tests/test_read_ahead.py +64 -0
  59. mlarray-1.0.0/tests/test_save.py +191 -0
  60. mlarray-1.0.0/tests/test_schemas.py +84 -0
  61. mlarray-1.0.0/tests/test_spec_examples.py +119 -0
  62. {mlarray-0.0.53 → mlarray-1.0.0}/tests/test_usage.py +77 -79
  63. mlarray-0.0.53/MANIFEST.in +0 -1
  64. mlarray-0.0.53/assets/banner.png +0 -0
  65. mlarray-0.0.53/assets/banner.png~ +0 -0
  66. mlarray-0.0.53/docs/api.md +0 -35
  67. mlarray-0.0.53/docs/cli.md +0 -29
  68. mlarray-0.0.53/docs/schema.md +0 -189
  69. mlarray-0.0.53/docs/usage.md +0 -240
  70. mlarray-0.0.53/examples/example_bboxes_only.py +0 -36
  71. mlarray-0.0.53/examples/example_channel.py +0 -34
  72. mlarray-0.0.53/examples/example_compress_decompress.py +0 -56
  73. mlarray-0.0.53/examples/example_compressed_vs_uncompressed.py +0 -42
  74. mlarray-0.0.53/examples/example_metadata_only.py +0 -31
  75. mlarray-0.0.53/examples/example_non_spatial.py +0 -42
  76. mlarray-0.0.53/examples/example_open.py +0 -36
  77. mlarray-0.0.53/examples/example_save_load.py +0 -34
  78. mlarray-0.0.53/mlarray/__init__.py +0 -82
  79. mlarray-0.0.53/mlarray/cli.py +0 -60
  80. mlarray-0.0.53/mlarray/meta.py +0 -1354
  81. mlarray-0.0.53/mlarray/mlarray.py +0 -2154
  82. mlarray-0.0.53/mlarray/utils.py +0 -17
  83. mlarray-0.0.53/mlarray.egg-info/SOURCES.txt +0 -52
  84. mlarray-0.0.53/tests/test_asarray.py +0 -85
  85. mlarray-0.0.53/tests/test_bboxes.py +0 -67
  86. mlarray-0.0.53/tests/test_meta_safety.py +0 -141
  87. mlarray-0.0.53/tests/test_metadata.py +0 -114
  88. mlarray-0.0.53/tests/test_optimization.py +0 -116
  89. {mlarray-0.0.53 → mlarray-1.0.0}/.gitignore +0 -0
  90. {mlarray-0.0.53 → mlarray-1.0.0}/LICENSE +0 -0
  91. {mlarray-0.0.53 → mlarray-1.0.0}/docs/index.md +0 -0
  92. {mlarray-0.0.53 → mlarray-1.0.0}/docs/why.md +0 -0
  93. {mlarray-0.0.53 → mlarray-1.0.0}/examples/example_asarray.py +0 -0
  94. {mlarray-0.0.53 → mlarray-1.0.0}/mkdocs.yml +0 -0
  95. {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/dependency_links.txt +0 -0
  96. {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/entry_points.txt +0 -0
  97. {mlarray-0.0.53 → mlarray-1.0.0}/mlarray.egg-info/top_level.txt +0 -0
  98. {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
- pytest || if [ $? -eq 5 ]; then echo "No tests collected; skipping."; exit 0; else exit $?; fi
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.53
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: copier,template,python
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 necessary extra dependencies:
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
- ### Metadata inspection and manipulation
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
- spacing=(1.0, 1.0, 1.5),
114
- origin=(10.0, 10.0, 30.0),
115
- direction=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
116
- meta={"patient_id": "123", "modality": "CT"}, # Any metadata from the original image source (for example raw DICOM metadata)
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) # [1.0, 1.0, 1.5]
120
- print(image.origin) # [10.0, 10.0, 30.0]
121
- print(image.meta.source) # {"patient_id": "123", "modality": "CT"}
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.meta.source["study_id"] = "new-study" # Modify metadata
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
- ### Copy metadata with overrides
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("sample.mla")
140
- array = np.random.random(base.shape)
141
-
142
- image = MLArray(
143
- array,
144
- spacing=(0.8, 0.8, 1.0),
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
- ### Standardized metadata usage
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 header from a `.mla` file.
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 a NIfTI or NRRD file to MLArray and copy metadata.
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 output.mla
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 necessary extra dependencies:
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
- ### Metadata inspection and manipulation
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
- spacing=(1.0, 1.0, 1.5),
80
- origin=(10.0, 10.0, 30.0),
81
- direction=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
82
- meta={"patient_id": "123", "modality": "CT"}, # Any metadata from the original image source (for example raw DICOM metadata)
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) # [1.0, 1.0, 1.5]
86
- print(image.origin) # [10.0, 10.0, 30.0]
87
- print(image.meta.source) # {"patient_id": "123", "modality": "CT"}
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.meta.source["study_id"] = "new-study" # Modify metadata
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
- ### Copy metadata with overrides
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("sample.mla")
106
- array = np.random.random(base.shape)
107
-
108
- image = MLArray(
109
- array,
110
- spacing=(0.8, 0.8, 1.0),
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
- ### Standardized metadata usage
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 header from a `.mla` file.
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 a NIfTI or NRRD file to MLArray and copy metadata.
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 output.mla
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,2 @@
1
+ data/
2
+ results/
@@ -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.