mlarray 0.0.24__tar.gz → 0.0.32__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 (39) hide show
  1. {mlarray-0.0.24 → mlarray-0.0.32}/PKG-INFO +14 -14
  2. {mlarray-0.0.24 → mlarray-0.0.32}/README.md +12 -12
  3. mlarray-0.0.32/docs/api.md +33 -0
  4. {mlarray-0.0.24 → mlarray-0.0.32}/docs/optimization.md +3 -3
  5. {mlarray-0.0.24 → mlarray-0.0.32}/docs/schema.md +57 -27
  6. {mlarray-0.0.24 → mlarray-0.0.32}/docs/usage.md +12 -12
  7. {mlarray-0.0.24 → mlarray-0.0.32}/examples/example_channel.py +2 -2
  8. {mlarray-0.0.24 → mlarray-0.0.32}/examples/example_metadata_only.py +2 -2
  9. {mlarray-0.0.24 → mlarray-0.0.32}/examples/example_open.py +5 -6
  10. {mlarray-0.0.24 → mlarray-0.0.32}/examples/example_save_load.py +4 -2
  11. mlarray-0.0.32/mlarray/__init__.py +79 -0
  12. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray/cli.py +1 -1
  13. mlarray-0.0.32/mlarray/meta.py +774 -0
  14. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray/mlarray.py +135 -45
  15. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/PKG-INFO +14 -14
  16. {mlarray-0.0.24 → mlarray-0.0.32}/pyproject.toml +1 -1
  17. {mlarray-0.0.24 → mlarray-0.0.32}/tests/test_metadata.py +16 -16
  18. {mlarray-0.0.24 → mlarray-0.0.32}/tests/test_optimization.py +3 -3
  19. {mlarray-0.0.24 → mlarray-0.0.32}/tests/test_usage.py +7 -8
  20. mlarray-0.0.24/docs/api.md +0 -17
  21. mlarray-0.0.24/mlarray/__init__.py +0 -54
  22. mlarray-0.0.24/mlarray/meta.py +0 -726
  23. {mlarray-0.0.24 → mlarray-0.0.32}/.github/workflows/workflow.yml +0 -0
  24. {mlarray-0.0.24 → mlarray-0.0.32}/.gitignore +0 -0
  25. {mlarray-0.0.24 → mlarray-0.0.32}/LICENSE +0 -0
  26. {mlarray-0.0.24 → mlarray-0.0.32}/MANIFEST.in +0 -0
  27. {mlarray-0.0.24 → mlarray-0.0.32}/assets/banner.png +0 -0
  28. {mlarray-0.0.24 → mlarray-0.0.32}/assets/banner.png~ +0 -0
  29. {mlarray-0.0.24 → mlarray-0.0.32}/docs/cli.md +0 -0
  30. {mlarray-0.0.24 → mlarray-0.0.32}/docs/index.md +0 -0
  31. {mlarray-0.0.24 → mlarray-0.0.32}/docs/why.md +0 -0
  32. {mlarray-0.0.24 → mlarray-0.0.32}/mkdocs.yml +0 -0
  33. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray/utils.py +0 -0
  34. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/SOURCES.txt +0 -0
  35. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/dependency_links.txt +0 -0
  36. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/entry_points.txt +0 -0
  37. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/requires.txt +0 -0
  38. {mlarray-0.0.24 → mlarray-0.0.32}/mlarray.egg-info/top_level.txt +0 -0
  39. {mlarray-0.0.24 → mlarray-0.0.32}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mlarray
3
- Version: 0.0.24
4
- Summary: A standardized blosc2 image reader and writer for medical images.
3
+ Version: 0.0.32
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
@@ -41,7 +41,7 @@ Dynamic: license-file
41
41
  <a href="https://pypi.org/project/mlarray/"><img src="https://img.shields.io/pypi/pyversions/mlarray?logo=python&cacheSeconds=300&v" alt="Python Version" align="middle" /></a>
42
42
  <a href="https://github.com/MIC-DKFZ/mlarray/actions"><img src="https://img.shields.io/github/actions/workflow/status/MIC-DKFZ/mlarray/workflow.yml?branch=main&logo=github" alt="Tests" align="middle" /></a>
43
43
  <a href="https://MIC-DKFZ.github.io/mlarray/"><img src="https://img.shields.io/badge/docs-mlarray-blue?logo=readthedocs&logoColor=white" alt="Docs" align="middle" /></a>
44
- <a href="https://github.com/MIC-DKFZ/mlarray/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MIC-DKFZmlarray" alt="License" align="middle" /></a>
44
+ <a href="https://github.com/MIC-DKFZ/mlarray/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MIC-DKFZ/mlarray" alt="License" align="middle" /></a>
45
45
  </p>
46
46
 
47
47
  **tl;dr:** Working with large medical or scientific images for machine learning? -> Use MLArray.
@@ -88,16 +88,16 @@ from mlarray import MLArray
88
88
  import numpy as np
89
89
 
90
90
  # read-only, partial access (default)
91
- image = MLArray().open("sample.mla", mmap='r')
91
+ image = MLArray.open("sample.mla", mmap='r')
92
92
  crop = image[10:20, 50:60] # Read crop
93
93
 
94
94
  # read/write, partial access
95
- image = MLArray().open("sample.mla", mmap='r+')
95
+ image = MLArray.open("sample.mla", mmap='r+')
96
96
  image[10:20, 50:60] *= 5 # Modify crop in memory and disk
97
97
 
98
98
  # read/write, partial access, create/overwrite
99
99
  array = np.random.random((128, 256, 256))
100
- image = MLArray().open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
100
+ image = MLArray.open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
101
101
  image[...] = array # Modify image in memory and disk
102
102
  ```
103
103
 
@@ -113,20 +113,20 @@ image = MLArray(
113
113
  spacing=(1.0, 1.0, 1.5),
114
114
  origin=(10.0, 10.0, 30.0),
115
115
  direction=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
116
- meta={"patient_id": "123", "modality": "CT"}, # Any image metadata (for example raw DICOM metadata)
116
+ meta={"patient_id": "123", "modality": "CT"}, # Any metadata from the original image source (for example raw DICOM metadata)
117
117
  )
118
118
 
119
119
  print(image.spacing) # [1.0, 1.0, 1.5]
120
120
  print(image.origin) # [10.0, 10.0, 30.0]
121
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
121
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
122
122
 
123
123
  image.spacing[1] = 5.3
124
- image.meta.image["study_id"] = "study-001"
124
+ image.meta.original["study_id"] = "study-001"
125
125
  image.save("with-metadata.mla")
126
126
 
127
127
  # Open memory-mapped
128
- image = MLArray().open("with-metadata.mla", mmap='r+')
129
- image.meta.image["study_id"] = "new-study" # Modify metadata
128
+ image = MLArray.open("with-metadata.mla", mmap='r+')
129
+ image.meta.original["study_id"] = "new-study" # Modify metadata
130
130
  image.close() # Close and save metadata, only necessary to save modified metadata
131
131
  ```
132
132
 
@@ -157,13 +157,13 @@ from mlarray import MLArray, Meta
157
157
  array = np.random.random((64, 128, 128))
158
158
  image = MLArray(
159
159
  array,
160
- meta=Meta(image={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
160
+ meta=Meta(original={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
161
161
  )
162
162
 
163
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
163
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
164
164
  print(image.meta.is_seg) # True
165
165
 
166
- image.meta.image["study_id"] = "study-001"
166
+ image.meta.original["study_id"] = "study-001"
167
167
  image.meta.is_seg = False
168
168
  image.save("with-metadata.mla")
169
169
  ```
@@ -7,7 +7,7 @@
7
7
  <a href="https://pypi.org/project/mlarray/"><img src="https://img.shields.io/pypi/pyversions/mlarray?logo=python&cacheSeconds=300&v" alt="Python Version" align="middle" /></a>
8
8
  <a href="https://github.com/MIC-DKFZ/mlarray/actions"><img src="https://img.shields.io/github/actions/workflow/status/MIC-DKFZ/mlarray/workflow.yml?branch=main&logo=github" alt="Tests" align="middle" /></a>
9
9
  <a href="https://MIC-DKFZ.github.io/mlarray/"><img src="https://img.shields.io/badge/docs-mlarray-blue?logo=readthedocs&logoColor=white" alt="Docs" align="middle" /></a>
10
- <a href="https://github.com/MIC-DKFZ/mlarray/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MIC-DKFZmlarray" alt="License" align="middle" /></a>
10
+ <a href="https://github.com/MIC-DKFZ/mlarray/blob/main/LICENSE"><img src="https://img.shields.io/github/license/MIC-DKFZ/mlarray" alt="License" align="middle" /></a>
11
11
  </p>
12
12
 
13
13
  **tl;dr:** Working with large medical or scientific images for machine learning? -> Use MLArray.
@@ -54,16 +54,16 @@ from mlarray import MLArray
54
54
  import numpy as np
55
55
 
56
56
  # read-only, partial access (default)
57
- image = MLArray().open("sample.mla", mmap='r')
57
+ image = MLArray.open("sample.mla", mmap='r')
58
58
  crop = image[10:20, 50:60] # Read crop
59
59
 
60
60
  # read/write, partial access
61
- image = MLArray().open("sample.mla", mmap='r+')
61
+ image = MLArray.open("sample.mla", mmap='r+')
62
62
  image[10:20, 50:60] *= 5 # Modify crop in memory and disk
63
63
 
64
64
  # read/write, partial access, create/overwrite
65
65
  array = np.random.random((128, 256, 256))
66
- image = MLArray().open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
66
+ image = MLArray.open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
67
67
  image[...] = array # Modify image in memory and disk
68
68
  ```
69
69
 
@@ -79,20 +79,20 @@ image = MLArray(
79
79
  spacing=(1.0, 1.0, 1.5),
80
80
  origin=(10.0, 10.0, 30.0),
81
81
  direction=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
82
- meta={"patient_id": "123", "modality": "CT"}, # Any image metadata (for example raw DICOM metadata)
82
+ meta={"patient_id": "123", "modality": "CT"}, # Any metadata from the original image source (for example raw DICOM metadata)
83
83
  )
84
84
 
85
85
  print(image.spacing) # [1.0, 1.0, 1.5]
86
86
  print(image.origin) # [10.0, 10.0, 30.0]
87
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
87
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
88
88
 
89
89
  image.spacing[1] = 5.3
90
- image.meta.image["study_id"] = "study-001"
90
+ image.meta.original["study_id"] = "study-001"
91
91
  image.save("with-metadata.mla")
92
92
 
93
93
  # Open memory-mapped
94
- image = MLArray().open("with-metadata.mla", mmap='r+')
95
- image.meta.image["study_id"] = "new-study" # Modify metadata
94
+ image = MLArray.open("with-metadata.mla", mmap='r+')
95
+ image.meta.original["study_id"] = "new-study" # Modify metadata
96
96
  image.close() # Close and save metadata, only necessary to save modified metadata
97
97
  ```
98
98
 
@@ -123,13 +123,13 @@ from mlarray import MLArray, Meta
123
123
  array = np.random.random((64, 128, 128))
124
124
  image = MLArray(
125
125
  array,
126
- meta=Meta(image={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
126
+ meta=Meta(original={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
127
127
  )
128
128
 
129
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
129
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
130
130
  print(image.meta.is_seg) # True
131
131
 
132
- image.meta.image["study_id"] = "study-001"
132
+ image.meta.original["study_id"] = "study-001"
133
133
  image.meta.is_seg = False
134
134
  image.save("with-metadata.mla")
135
135
  ```
@@ -0,0 +1,33 @@
1
+ # API Reference
2
+
3
+ ## MLArray Module
4
+
5
+ ::: mlarray.mlarray.MLArray
6
+
7
+ ## Metadata Module
8
+
9
+ ::: mlarray.meta.BaseMeta
10
+
11
+ ::: mlarray.meta.SingleKeyBaseMeta
12
+
13
+ ::: mlarray.meta.Meta
14
+
15
+ ::: mlarray.meta.MetaOriginal
16
+
17
+ ::: mlarray.meta.MetaExtra
18
+
19
+ ::: mlarray.meta.MetaSpatial
20
+
21
+ ::: mlarray.meta.MetaStatistics
22
+
23
+ ::: mlarray.meta.MetaBbox
24
+
25
+ ::: mlarray.meta.MetaIsSeg
26
+
27
+ ::: mlarray.meta.MetaBlosc2
28
+
29
+ ::: mlarray.meta.MetaHasArray
30
+
31
+ ::: mlarray.meta.MetaImageFormat
32
+
33
+ ::: mlarray.meta.MetaVersion
@@ -96,7 +96,7 @@ For large files, you typically want **mmap reads** so random patches don’t req
96
96
  from mlarray import MLArray
97
97
 
98
98
  # read-only mmap: fast random access without loading the full volume
99
- image = MLArray().open("patch-non-iso.mla", mmap='r')
99
+ image = MLArray.open("patch-non-iso.mla", mmap='r')
100
100
 
101
101
  patch = image[10:20, 50:60] # Read a crop/patch (partial read)
102
102
  ```
@@ -119,7 +119,7 @@ You can modify regions in-place with `mmap='r+'`. This is useful for workflows l
119
119
  ```python
120
120
  from mlarray import MLArray
121
121
 
122
- image = MLArray().open("patch-non-iso.mla", mmap='r+')
122
+ image = MLArray.open("patch-non-iso.mla", mmap='r+')
123
123
  image[10:20, 50:60] *= 5 # Modify crop in memory and on disk
124
124
  image.close()
125
125
  ```
@@ -137,7 +137,7 @@ from mlarray import MLArray
137
137
  shape = (128, 256, 256)
138
138
  dtype = np.float32
139
139
 
140
- image = MLArray().open(
140
+ image = MLArray.open(
141
141
  "streamed-write.mla",
142
142
  shape=shape,
143
143
  dtype=dtype,
@@ -11,6 +11,10 @@ The schema is designed around a few core goals:
11
11
 
12
12
  All fields in the schema are **JSON-serializable** unless otherwise noted. Fields marked as `Optional[...]` may be omitted if unknown or not applicable.
13
13
 
14
+ ### Representation notes
15
+
16
+ Many namespaces are implemented as **single-key dataclasses** (subclasses of `SingleKeyBaseMeta`). In Python, these behave like their wrapped value (e.g., `meta.is_seg` is a `bool`, `meta.original` is a `dict`). When serialized via `Meta.to_mapping()`, they appear as a one-field object keyed by their internal field name (e.g., `is_seg: {"is_seg": true}` or `original: {"data": {...}}`). `Meta.from_mapping()` accepts either the one-field object or the raw value and will coerce it to the correct class.
17
+
14
18
  ---
15
19
 
16
20
  ## Meta
@@ -19,16 +23,20 @@ Top-level metadata container.
19
23
 
20
24
  ### Overview
21
25
 
22
- `Meta` is the root object that groups all metadata into well-defined namespaces. Some namespaces are **standardized** (e.g., `spatial`, `stats`), while others are intentionally **free-form** (`image`, `extra`) to support arbitrary metadata and long-term extensibility.
26
+ `Meta` is the root object that groups all metadata into well-defined namespaces. Some namespaces are **standardized** (e.g., `spatial`, `stats`), while others are intentionally **free-form** (`original`, `extra`) to support arbitrary metadata and long-term extensibility. Several entries are single-key dataclasses that wrap a primitive value while still allowing schema-aware validation.
23
27
 
24
28
  ---
25
29
 
26
- ### image
30
+ ### original
31
+
32
+ * **Description:** Arbitrary JSON-serializable dictionary for metadata from the original image source.
33
+ Stores information from image sources such as DICOM, NIfTI, NRRD,
34
+ or other imaging formats.
35
+ * **Dataclass:** `MetaOriginal` (single-key wrapper).
27
36
 
28
- * **Description:** Arbitrary JSON-serializable dictionary for image-level metadata.
29
- Stores information from medical image sources such as DICOM, NIfTI, NRRD,
30
- or other medical imaging formats.
31
- * **Dataclass:** None (plain dict).
37
+ | field | type | description |
38
+ | ----- | --------------------- | -------------------------------------------- |
39
+ | data | Dict[str, Any] | JSON-serializable metadata from the source. |
32
40
 
33
41
  ---
34
42
 
@@ -37,7 +45,11 @@ Top-level metadata container.
37
45
  * **Description:** Flexible container for arbitrary, JSON-serializable metadata
38
46
  when no schema exists. Intended for experimental or application-specific
39
47
  fields that are not part of the standard.
40
- * **Dataclass:** None (plain dict).
48
+ * **Dataclass:** `MetaExtra` (single-key wrapper).
49
+
50
+ | field | type | description |
51
+ | ----- | -------------- | -------------------------------------------- |
52
+ | data | Dict[str, Any] | JSON-serializable metadata for extra fields. |
41
53
 
42
54
  ---
43
55
 
@@ -53,18 +65,11 @@ This section stores the information needed to interpret the array in physical sp
53
65
  | spacing | Optional[List[float]] | Voxel spacing per spatial axis, length = `ndims`. |
54
66
  | origin | Optional[List[float]] | Origin per spatial axis, length = `ndims`. |
55
67
  | direction | Optional[List[List[float]]] | Direction matrix, shape `[ndims][ndims]`. |
56
- | shape | Optional[List[int]] | Array shape. If `channel_axis` is set, length = `ndims + 1`, otherwise length = `ndims`. |
68
+ | shape | Optional[List[int]] | Array shape. If `channel_axis` is set, length = `ndims + 1`, otherwise length = `ndims`. |
57
69
  | channel_axis | Optional[int] | Index of channel dimension in the full array, if present. |
58
70
 
59
71
  ---
60
72
 
61
- ### is_seg
62
-
63
- * **Description:** Whether the image is a segmentation mask.
64
- * **Dataclass:** None (boolean).
65
-
66
- ---
67
-
68
73
  ### stats
69
74
 
70
75
  * **Description:** Summary statistics for the image.
@@ -84,13 +89,15 @@ This section stores precomputed global statistics for the array, which can be us
84
89
  | percentile_mean | Optional[float] | Mean within a selected percentile range. |
85
90
  | percentile_median | Optional[float] | Median within a selected percentile range. |
86
91
  | percentile_std | Optional[float] | Standard deviation within a selected percentile range. |
92
+ | percentile_min_key | Optional[float] | Minimum percentile key used to determine percentile_min (for example 0.05). |
93
+ | percentile_max_key | Optional[float] | Maximum percentile key used to determine percentile_max (for example 0.95). |
87
94
 
88
95
  ---
89
96
 
90
97
  ### bbox
91
98
 
92
99
  * **Description:** Bounding boxes for objects/regions in the image.
93
- * **Dataclass:** `MetaBbox`.
100
+ * **Dataclass:** `MetaBbox` (single-key wrapper).
94
101
  * **Structure:** List of bboxes, each bbox is a list with length equal to image `ndims`,
95
102
  and each entry is `[min, max]`.
96
103
 
@@ -102,18 +109,14 @@ Bounding boxes are stored in a normalized, axis-aligned representation that work
102
109
 
103
110
  ---
104
111
 
105
- ### _has_array
106
-
107
- * **Description:** Whether this metadata instance represents an on-disk array.
108
- * **Dataclass:** None (boolean).
109
-
110
- ---
112
+ ### is_seg
111
113
 
112
- ### _image_meta_format
114
+ * **Description:** Whether the image is a segmentation mask.
115
+ * **Dataclass:** `MetaIsSeg` (single-key wrapper).
113
116
 
114
- * **Description:** Source format identifier for the `image` metadata (e.g., "dicom",
115
- "nifti", "nrrd"). This is advisory and application-defined.
116
- * **Dataclass:** None (string).
117
+ | field | type | description |
118
+ | ------ | -------------- | ----------------------------------------------- |
119
+ | is_seg | Optional[bool] | True/False when known, None when unknown. |
117
120
 
118
121
  ---
119
122
 
@@ -132,7 +135,34 @@ This section records how the array was laid out on disk (chunking, blocking, pat
132
135
 
133
136
  ---
134
137
 
138
+ ### _has_array
139
+
140
+ * **Description:** Whether this metadata instance represents an on-disk array.
141
+ * **Dataclass:** `MetaHasArray` (single-key wrapper).
142
+
143
+ | field | type | description |
144
+ | --------- | ---- | ------------------------------------- |
145
+ | has_array | bool | True when an array payload is stored. |
146
+
147
+ ---
148
+
149
+ ### _image_meta_format
150
+
151
+ * **Description:** Source format identifier for the `image` metadata (e.g., "dicom",
152
+ "nifti", "nrrd"). This is advisory and application-defined.
153
+ * **Dataclass:** `MetaImageFormat` (single-key wrapper).
154
+
155
+ | field | type | description |
156
+ | ----------------- | -------------- | --------------------------------------------- |
157
+ | image_meta_format | Optional[str] | Identifier for the original metadata format. |
158
+
159
+ ---
160
+
135
161
  ### _mlarray_version
136
162
 
137
163
  * **Description:** MLArray version string used to write the file.
138
- * **Dataclass:** None (string).
164
+ * **Dataclass:** `MetaVersion` (single-key wrapper).
165
+
166
+ | field | type | description |
167
+ | --------------- | -------------- | ------------------------------------ |
168
+ | mlarray_version | Optional[str] | Version string for the writer. |
@@ -34,16 +34,16 @@ from mlarray import MLArray
34
34
  import numpy as np
35
35
 
36
36
  # read-only, partial access (default)
37
- image = MLArray().open("sample.mla", mmap='r')
37
+ image = MLArray.open("sample.mla", mmap='r')
38
38
  crop = image[10:20, 50:60] # Read crop
39
39
 
40
40
  # read/write, partial access
41
- image = MLArray().open("sample.mla", mmap='r+')
41
+ image = MLArray.open("sample.mla", mmap='r+')
42
42
  image[10:20, 50:60] *= 5 # Modify crop in memory and disk
43
43
 
44
44
  # read/write, partial access, create/overwrite
45
45
  array = np.random.random((128, 256, 256))
46
- image = MLArray().open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
46
+ image = MLArray.open("sample.mla", shape=array.shape, dtype=array.dtype, mmap='w+')
47
47
  image[...] = array # Modify image in memory and disk
48
48
  ```
49
49
 
@@ -51,7 +51,7 @@ image[...] = array # Modify image in memory and disk
51
51
 
52
52
  ## Metadata inspection and manipulation
53
53
 
54
- MLArray provides first-class support for common image metadata (spacing, origin, direction), and also lets you attach arbitrary metadata via `meta=...` (e.g., raw DICOM fields, acquisition parameters, dataset identifiers).
54
+ MLArray provides first-class support for common image metadata (spacing, origin, direction), and also lets you attach arbitrary metadata from the original image source via `meta=...` (e.g., raw DICOM fields, acquisition parameters, dataset identifiers).
55
55
 
56
56
  ```python
57
57
  import numpy as np
@@ -63,20 +63,20 @@ image = MLArray(
63
63
  spacing=(1.0, 1.0, 1.5),
64
64
  origin=(10.0, 10.0, 30.0),
65
65
  direction=[[1, 0, 0], [0, 1, 0], [0, 0, 1]],
66
- meta={"patient_id": "123", "modality": "CT"}, # Any image metadata (for example raw DICOM metadata)
66
+ meta={"patient_id": "123", "modality": "CT"}, # Any metadata from the original image source (for example raw DICOM metadata)
67
67
  )
68
68
 
69
69
  print(image.spacing) # [1.0, 1.0, 1.5]
70
70
  print(image.origin) # [10.0, 10.0, 30.0]
71
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
71
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
72
72
 
73
73
  image.spacing[1] = 5.3
74
- image.meta.image["study_id"] = "study-001"
74
+ image.meta.original["study_id"] = "study-001"
75
75
  image.save("with-metadata.mla")
76
76
 
77
77
  # Open memory-mapped
78
- image = MLArray().open("with-metadata.mla", mmap='r+')
79
- image.meta.image["study_id"] = "new-study" # Modify metadata
78
+ image = MLArray.open("with-metadata.mla", mmap='r+')
79
+ image.meta.original["study_id"] = "new-study" # Modify metadata
80
80
  image.close() # Close and save metadata, only necessary to save modified metadata
81
81
  ```
82
82
 
@@ -115,13 +115,13 @@ from mlarray import MLArray, Meta
115
115
  array = np.random.random((64, 128, 128))
116
116
  image = MLArray(
117
117
  array,
118
- meta=Meta(image={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
118
+ meta=Meta(original={"patient_id": "123", "modality": "CT"}, is_seg=True), # Add metadata in a pre-defined format
119
119
  )
120
120
 
121
- print(image.meta.image) # {"patient_id": "123", "modality": "CT"}
121
+ print(image.meta.original) # {"patient_id": "123", "modality": "CT"}
122
122
  print(image.meta.is_seg) # True
123
123
 
124
- image.meta.image["study_id"] = "study-001"
124
+ image.meta.original["study_id"] = "study-001"
125
125
  image.meta.is_seg = False
126
126
  image.save("with-metadata.mla")
127
127
  ```
@@ -20,13 +20,13 @@ if __name__ == '__main__':
20
20
  os.remove(filepath)
21
21
 
22
22
  print("Initializing image...")
23
- image = MLArray(array, spacing=spacing, origin=origin, direction=direction, channel_axis=channel_axis, meta=Meta(image=image_meta, bbox=bboxes))
23
+ image = MLArray(array, spacing=spacing, origin=origin, direction=direction, channel_axis=channel_axis, meta=Meta(original=image_meta, bbox=bboxes))
24
24
  print("Saving image...")
25
25
  image.save(filepath)
26
26
 
27
27
  print("Loading image...")
28
28
  image = MLArray(filepath)
29
- print(json.dumps(image.meta.to_dict(), indent=2, sort_keys=True))
29
+ print(json.dumps(image.meta.to_mapping(), indent=2, sort_keys=True))
30
30
  print("Image mean value: ", np.mean(image.to_numpy()))
31
31
  print("Some array data: \n", image[:2, :2, 0])
32
32
 
@@ -19,13 +19,13 @@ if __name__ == '__main__':
19
19
  os.remove(filepath)
20
20
 
21
21
  print("Initializing image...")
22
- image = MLArray(spacing=spacing, origin=origin, direction=direction, meta=Meta(image=image_meta, bbox=bboxes))
22
+ image = MLArray(spacing=spacing, origin=origin, direction=direction, meta=Meta(original=image_meta, bbox=bboxes))
23
23
  print("Saving image...")
24
24
  image.save(filepath)
25
25
 
26
26
  print("Loading image...")
27
27
  image = MLArray(filepath)
28
- print(json.dumps(image.meta.to_dict(), indent=2, sort_keys=True))
28
+ print(json.dumps(image.meta.to_mapping(), indent=2, sort_keys=True))
29
29
 
30
30
  if Path(filepath).is_file():
31
31
  os.remove(filepath)
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import os
3
3
  from pathlib import Path
4
- from mlarray import MLArray, Meta
4
+ from mlarray import MLArray, Meta, MetaSpatial
5
5
  import json
6
6
 
7
7
 
@@ -19,17 +19,16 @@ if __name__ == '__main__':
19
19
  os.remove(filepath)
20
20
 
21
21
  print("Initializing image...")
22
- image = MLArray(spacing=spacing, origin=origin, direction=direction, meta=Meta(image=image_meta, bbox=bboxes))
23
- image.open(filepath, shape=array.shape, dtype=array.dtype, mmap='w+')
22
+ image = MLArray.open(filepath, shape=array.shape, dtype=array.dtype, mmap='w+')
24
23
  print("Saving image...")
25
24
  image[...] = array
25
+ image.meta.copy_from(Meta(original=image_meta, spatial=MetaSpatial(spacing=spacing, origin=origin, direction=direction), bbox=bboxes))
26
26
  image.meta.is_seg = True
27
27
  image.close()
28
28
 
29
29
  print("Loading image...")
30
- image = MLArray()
31
- image.open(filepath)
32
- print(json.dumps(image.meta.to_dict(), indent=2, sort_keys=True))
30
+ image = MLArray.open(filepath)
31
+ print(json.dumps(image.meta.to_mapping(), indent=2, sort_keys=True))
33
32
  print("Image mean value: ", np.mean(image.to_numpy()))
34
33
  print("Some array data: \n", image[:2, :2, 0])
35
34
 
@@ -2,6 +2,7 @@ import numpy as np
2
2
  import os
3
3
  from pathlib import Path
4
4
  from mlarray import MLArray, Meta
5
+ from mlarray.meta import MetaOriginal, MetaIsSeg
5
6
  import json
6
7
 
7
8
 
@@ -19,13 +20,14 @@ if __name__ == '__main__':
19
20
  os.remove(filepath)
20
21
 
21
22
  print("Initializing image...")
22
- image = MLArray(array, spacing=spacing, origin=origin, direction=direction, meta=Meta(image=image_meta, bbox=bboxes))
23
+ image = MLArray(array, spacing=spacing, origin=origin, direction=direction, meta=Meta(original=image_meta, bbox=bboxes, is_seg=True))
23
24
  print("Saving image...")
24
25
  image.save(filepath)
25
26
 
26
27
  print("Loading image...")
27
28
  image = MLArray(filepath)
28
- print(json.dumps(image.meta.to_dict(), indent=2, sort_keys=True))
29
+ image = MLArray.load(filepath) # Just testing both load methods
30
+ print(json.dumps(image.meta.to_mapping(), indent=2, sort_keys=True))
29
31
  print("Image mean value: ", np.mean(image.to_numpy()))
30
32
  print("Some array data: \n", image[:2, :2, 0])
31
33
 
@@ -0,0 +1,79 @@
1
+ """A standardized blosc2 image reader and writer for medical images."""
2
+
3
+ from importlib import metadata as _metadata
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from mlarray.mlarray import MLArray, MLARRAY_DEFAULT_PATCH_SIZE
8
+ from mlarray.meta import (
9
+ Meta,
10
+ MetaBbox,
11
+ MetaBlosc2,
12
+ MetaExtra,
13
+ MetaHasArray,
14
+ MetaOriginal,
15
+ MetaImageFormat,
16
+ MetaIsSeg,
17
+ MetaSpatial,
18
+ MetaStatistics,
19
+ MetaVersion,
20
+ )
21
+ from mlarray.utils import is_serializable
22
+ from mlarray.cli import cli_print_header, cli_convert_to_mlarray
23
+
24
+ __all__ = [
25
+ "__version__",
26
+ "MLArray",
27
+ "MLARRAY_DEFAULT_PATCH_SIZE",
28
+ "Meta",
29
+ "MetaBbox",
30
+ "MetaBlosc2",
31
+ "MetaExtra",
32
+ "MetaHasArray",
33
+ "MetaOriginal",
34
+ "MetaImageFormat",
35
+ "MetaIsSeg",
36
+ "MetaSpatial",
37
+ "MetaStatistics",
38
+ "MetaVersion",
39
+ "is_serializable",
40
+ "cli_print_header",
41
+ "cli_convert_to_mlarray",
42
+ ]
43
+
44
+ try:
45
+ __version__ = _metadata.version(__name__)
46
+ except _metadata.PackageNotFoundError: # pragma: no cover - during editable installs pre-build
47
+ __version__ = "0.0.0"
48
+
49
+
50
+ _LAZY_ATTRS = {
51
+ "MLArray": ("mlarray.mlarray", "MLArray"),
52
+ "MLARRAY_DEFAULT_PATCH_SIZE": ("mlarray.mlarray", "MLARRAY_DEFAULT_PATCH_SIZE"),
53
+ "Meta": ("mlarray.meta", "Meta"),
54
+ "MetaBbox": ("mlarray.meta", "MetaBbox"),
55
+ "MetaBlosc2": ("mlarray.meta", "MetaBlosc2"),
56
+ "MetaExtra": ("mlarray.meta", "MetaExtra"),
57
+ "MetaHasArray": ("mlarray.meta", "MetaHasArray"),
58
+ "MetaOriginal": ("mlarray.meta", "MetaOriginal"),
59
+ "MetaImageFormat": ("mlarray.meta", "MetaImageFormat"),
60
+ "MetaIsSeg": ("mlarray.meta", "MetaIsSeg"),
61
+ "MetaSpatial": ("mlarray.meta", "MetaSpatial"),
62
+ "MetaStatistics": ("mlarray.meta", "MetaStatistics"),
63
+ "MetaVersion": ("mlarray.meta", "MetaVersion"),
64
+ "is_serializable": ("mlarray.utils", "is_serializable"),
65
+ "cli_print_header": ("mlarray.cli", "cli_print_header"),
66
+ "cli_convert_to_mlarray": ("mlarray.cli", "cli_convert_to_mlarray"),
67
+ }
68
+
69
+
70
+ def __getattr__(name: str):
71
+ if name in _LAZY_ATTRS:
72
+ module_name, attr_name = _LAZY_ATTRS[name]
73
+ module = __import__(module_name, fromlist=[attr_name])
74
+ return getattr(module, attr_name)
75
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
76
+
77
+
78
+ def __dir__():
79
+ return sorted(__all__)
@@ -20,7 +20,7 @@ def print_header(filepath: Union[str, Path]) -> None:
20
20
  if meta is None:
21
21
  print("null")
22
22
  return
23
- print(json.dumps(meta.to_dict(include_none=True), indent=2, sort_keys=True))
23
+ print(json.dumps(meta.to_plain(include_none=True), indent=2, sort_keys=True))
24
24
 
25
25
 
26
26
  def convert_to_mlarray(load_filepath: Union[str, Path], save_filepath: Union[str, Path]):