mlarray 0.0.47__tar.gz → 0.0.48__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.47 → mlarray-0.0.48}/PKG-INFO +1 -1
- mlarray-0.0.48/examples/example_compressed_vs_uncompressed.py +42 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/meta.py +48 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/mlarray.py +254 -67
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/PKG-INFO +1 -1
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/SOURCES.txt +1 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_asarray.py +16 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_usage.py +22 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/.github/workflows/workflow.yml +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/.gitignore +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/LICENSE +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/MANIFEST.in +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/README.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/assets/banner.png +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/assets/banner.png~ +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/api.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/cli.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/index.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/optimization.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/schema.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/usage.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/docs/why.md +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_asarray.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_bboxes_only.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_channel.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_in_memory_constructors.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_metadata_only.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_non_spatial.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_open.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/examples/example_save_load.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mkdocs.yml +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/__init__.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/cli.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray/utils.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/dependency_links.txt +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/entry_points.txt +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/requires.txt +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/mlarray.egg-info/top_level.txt +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/pyproject.toml +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/setup.cfg +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_bboxes.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_constructors.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_create.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_metadata.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_open.py +0 -0
- {mlarray-0.0.47 → mlarray-0.0.48}/tests/test_optimization.py +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from mlarray import MLArray
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
array = np.arange(2 * 4 * 4, dtype=np.float32).reshape(2, 4, 4)
|
|
10
|
+
|
|
11
|
+
compressed_img = MLArray.asarray(
|
|
12
|
+
array,
|
|
13
|
+
compressed=True,
|
|
14
|
+
patch_size=None,
|
|
15
|
+
chunk_size=(1, 4, 4),
|
|
16
|
+
block_size=(1, 2, 2),
|
|
17
|
+
)
|
|
18
|
+
uncompressed_img = MLArray.asarray(array, compressed=False)
|
|
19
|
+
|
|
20
|
+
compressed_path = Path("example_compressed_output.mla")
|
|
21
|
+
uncompressed_path = Path("example_uncompressed_output.mla")
|
|
22
|
+
|
|
23
|
+
print("compressed in-memory backend (before save):", type(compressed_img._store))
|
|
24
|
+
print(
|
|
25
|
+
"uncompressed in-memory backend (before save):",
|
|
26
|
+
type(uncompressed_img._store),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
compressed_img.save(compressed_path)
|
|
30
|
+
uncompressed_img.save(uncompressed_path)
|
|
31
|
+
|
|
32
|
+
print("compressed in-memory backend (after save):", type(compressed_img._store))
|
|
33
|
+
print(
|
|
34
|
+
"uncompressed in-memory backend (after save):",
|
|
35
|
+
type(uncompressed_img._store),
|
|
36
|
+
)
|
|
37
|
+
print("saved compressed file:", compressed_path)
|
|
38
|
+
print("saved uncompressed->compressed file:", uncompressed_path)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
main()
|
|
@@ -393,6 +393,44 @@ def _cast_to_list(value: Any, label: str):
|
|
|
393
393
|
return out
|
|
394
394
|
|
|
395
395
|
|
|
396
|
+
def _to_jsonable(value: Any) -> Any:
|
|
397
|
+
"""Recursively convert values to JSON-serializable plain Python objects."""
|
|
398
|
+
if isinstance(value, Enum):
|
|
399
|
+
return value.value
|
|
400
|
+
|
|
401
|
+
if isinstance(value, Mapping):
|
|
402
|
+
return {str(k): _to_jsonable(v) for k, v in value.items()}
|
|
403
|
+
|
|
404
|
+
if isinstance(value, (list, tuple)):
|
|
405
|
+
return [_to_jsonable(v) for v in value]
|
|
406
|
+
|
|
407
|
+
if isinstance(value, np.generic):
|
|
408
|
+
return value.item()
|
|
409
|
+
|
|
410
|
+
return value
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _cast_to_jsonable_mapping(value: Any, label: str) -> dict[str, Any]:
|
|
414
|
+
"""Cast a value to a JSON-serializable mapping.
|
|
415
|
+
|
|
416
|
+
Accepts mappings directly or objects exposing ``__dict__`` (for example
|
|
417
|
+
Blosc2 ``CParams`` / ``DParams`` objects).
|
|
418
|
+
"""
|
|
419
|
+
if isinstance(value, Mapping):
|
|
420
|
+
out = dict(value)
|
|
421
|
+
elif hasattr(value, "__dict__"):
|
|
422
|
+
out = dict(vars(value))
|
|
423
|
+
else:
|
|
424
|
+
raise TypeError(f"{label} must be a mapping or object with __dict__")
|
|
425
|
+
|
|
426
|
+
out = _to_jsonable(out)
|
|
427
|
+
if not isinstance(out, dict):
|
|
428
|
+
raise TypeError(f"{label} could not be converted to a mapping")
|
|
429
|
+
if not is_serializable(out):
|
|
430
|
+
raise TypeError(f"{label} is not JSON-serializable")
|
|
431
|
+
return out
|
|
432
|
+
|
|
433
|
+
|
|
396
434
|
def _validate_int(value: Any, label: str) -> None:
|
|
397
435
|
"""Validate that value is an int.
|
|
398
436
|
|
|
@@ -565,10 +603,14 @@ class MetaBlosc2(BaseMeta):
|
|
|
565
603
|
chunk_size: List of per-dimension chunk sizes. Length must match ndims.
|
|
566
604
|
block_size: List of per-dimension block sizes. Length must match ndims.
|
|
567
605
|
patch_size: List of per-dimension patch sizes. Length must match spatial ndims.
|
|
606
|
+
cparams: Blosc2 compression parameters as a JSON-serializable dict.
|
|
607
|
+
dparams: Blosc2 decompression parameters as a JSON-serializable dict.
|
|
568
608
|
"""
|
|
569
609
|
chunk_size: Optional[list] = None
|
|
570
610
|
block_size: Optional[list] = None
|
|
571
611
|
patch_size: Optional[list] = None
|
|
612
|
+
cparams: Optional[dict[str, Any]] = None
|
|
613
|
+
dparams: Optional[dict[str, Any]] = None
|
|
572
614
|
|
|
573
615
|
def _validate_and_cast(self, *, ndims: Optional[int] = None, spatial_ndims: Optional[int] = None, **_: Any) -> None:
|
|
574
616
|
"""Validate and normalize tiling sizes.
|
|
@@ -591,6 +633,12 @@ class MetaBlosc2(BaseMeta):
|
|
|
591
633
|
self.patch_size = _cast_to_list(self.patch_size, "meta.blosc2.patch_size")
|
|
592
634
|
_validate_float_int_list(self.patch_size, "meta.blosc2.patch_size", spatial_ndims)
|
|
593
635
|
|
|
636
|
+
if self.cparams is not None:
|
|
637
|
+
self.cparams = _cast_to_jsonable_mapping(self.cparams, "meta.blosc2.cparams")
|
|
638
|
+
|
|
639
|
+
if self.dparams is not None:
|
|
640
|
+
self.dparams = _cast_to_jsonable_mapping(self.dparams, "meta.blosc2.dparams")
|
|
641
|
+
|
|
594
642
|
|
|
595
643
|
class AxisLabelEnum(str, Enum):
|
|
596
644
|
"""Axis label/role identifiers used for spatial metadata.
|
|
@@ -2,7 +2,7 @@ from copy import deepcopy
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import blosc2
|
|
4
4
|
import math
|
|
5
|
-
from typing import Dict, Optional, Union, List, Tuple
|
|
5
|
+
from typing import Any, Dict, Optional, Union, List, Tuple
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
import os
|
|
8
8
|
from mlarray.meta import Meta, MetaBlosc2, AxisLabel, _spatial_axis_mask
|
|
@@ -28,6 +28,7 @@ class MLArray:
|
|
|
28
28
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
29
29
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
30
30
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
31
|
+
compressed: bool = True,
|
|
31
32
|
) -> None:
|
|
32
33
|
"""Initializes a MLArray instance.
|
|
33
34
|
|
|
@@ -75,6 +76,7 @@ class MLArray:
|
|
|
75
76
|
self.mmap_mode = None
|
|
76
77
|
self.meta = None
|
|
77
78
|
self._store = None
|
|
79
|
+
self._backend = None
|
|
78
80
|
if isinstance(array, (str, Path)) and (
|
|
79
81
|
spacing is not None
|
|
80
82
|
or origin is not None
|
|
@@ -94,7 +96,7 @@ class MLArray:
|
|
|
94
96
|
"array is a filepath."
|
|
95
97
|
)
|
|
96
98
|
if isinstance(array, (str, Path)):
|
|
97
|
-
self._load(array)
|
|
99
|
+
self._load(array, compressed=compressed)
|
|
98
100
|
else:
|
|
99
101
|
self._validate_and_add_meta(meta, spacing, origin, direction, axis_labels, False, validate=False)
|
|
100
102
|
if array is not None:
|
|
@@ -106,10 +108,16 @@ class MLArray:
|
|
|
106
108
|
block_size=block_size,
|
|
107
109
|
cparams=cparams,
|
|
108
110
|
dparams=dparams,
|
|
111
|
+
compressed=compressed,
|
|
109
112
|
)
|
|
110
113
|
has_array = True
|
|
111
114
|
else:
|
|
112
|
-
|
|
115
|
+
if compressed:
|
|
116
|
+
self._store = blosc2.empty((0,))
|
|
117
|
+
self._backend = "blosc2"
|
|
118
|
+
else:
|
|
119
|
+
self._store = np.empty((0,))
|
|
120
|
+
self._backend = "numpy"
|
|
113
121
|
has_array = False
|
|
114
122
|
if copy is not None:
|
|
115
123
|
self.meta.copy_from(copy.meta)
|
|
@@ -224,6 +232,7 @@ class MLArray:
|
|
|
224
232
|
cls,
|
|
225
233
|
filepath: Union[str, Path],
|
|
226
234
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
235
|
+
compressed: bool = True,
|
|
227
236
|
):
|
|
228
237
|
"""Loads a MLArray file as a whole. Does not use memory-mapping. Both MLArray ('.mla') and Blosc2 ('.b2nd') files are supported.
|
|
229
238
|
|
|
@@ -243,7 +252,7 @@ class MLArray:
|
|
|
243
252
|
RuntimeError: If the file extension is not ".b2nd" or ".mla".
|
|
244
253
|
"""
|
|
245
254
|
class_instance = cls()
|
|
246
|
-
class_instance._load(filepath, dparams)
|
|
255
|
+
class_instance._load(filepath, dparams, compressed=compressed)
|
|
247
256
|
return class_instance
|
|
248
257
|
|
|
249
258
|
@classmethod
|
|
@@ -257,6 +266,7 @@ class MLArray:
|
|
|
257
266
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
258
267
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
259
268
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
269
|
+
compressed: bool = True,
|
|
260
270
|
):
|
|
261
271
|
"""Create an in-memory MLArray with uninitialized values.
|
|
262
272
|
|
|
@@ -284,6 +294,7 @@ class MLArray:
|
|
|
284
294
|
"""
|
|
285
295
|
class_instance = cls()
|
|
286
296
|
class_instance._construct_in_memory(
|
|
297
|
+
constructor="empty",
|
|
287
298
|
shape=shape,
|
|
288
299
|
dtype=dtype,
|
|
289
300
|
meta=meta,
|
|
@@ -292,7 +303,7 @@ class MLArray:
|
|
|
292
303
|
block_size=block_size,
|
|
293
304
|
cparams=cparams,
|
|
294
305
|
dparams=dparams,
|
|
295
|
-
|
|
306
|
+
compressed=compressed,
|
|
296
307
|
)
|
|
297
308
|
return class_instance
|
|
298
309
|
|
|
@@ -307,6 +318,7 @@ class MLArray:
|
|
|
307
318
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
308
319
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
309
320
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
321
|
+
compressed: bool = True,
|
|
310
322
|
):
|
|
311
323
|
"""Create an in-memory MLArray filled with zeros.
|
|
312
324
|
|
|
@@ -332,6 +344,7 @@ class MLArray:
|
|
|
332
344
|
"""
|
|
333
345
|
class_instance = cls()
|
|
334
346
|
class_instance._construct_in_memory(
|
|
347
|
+
constructor="zeros",
|
|
335
348
|
shape=shape,
|
|
336
349
|
dtype=dtype,
|
|
337
350
|
meta=meta,
|
|
@@ -340,7 +353,7 @@ class MLArray:
|
|
|
340
353
|
block_size=block_size,
|
|
341
354
|
cparams=cparams,
|
|
342
355
|
dparams=dparams,
|
|
343
|
-
|
|
356
|
+
compressed=compressed,
|
|
344
357
|
)
|
|
345
358
|
return class_instance
|
|
346
359
|
|
|
@@ -355,6 +368,7 @@ class MLArray:
|
|
|
355
368
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
356
369
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
357
370
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
371
|
+
compressed: bool = True,
|
|
358
372
|
):
|
|
359
373
|
"""Create an in-memory MLArray filled with ones.
|
|
360
374
|
|
|
@@ -382,6 +396,7 @@ class MLArray:
|
|
|
382
396
|
dtype = blosc2.DEFAULT_FLOAT if dtype is None else dtype
|
|
383
397
|
class_instance = cls()
|
|
384
398
|
class_instance._construct_in_memory(
|
|
399
|
+
constructor="ones",
|
|
385
400
|
shape=shape,
|
|
386
401
|
dtype=dtype,
|
|
387
402
|
meta=meta,
|
|
@@ -390,7 +405,7 @@ class MLArray:
|
|
|
390
405
|
block_size=block_size,
|
|
391
406
|
cparams=cparams,
|
|
392
407
|
dparams=dparams,
|
|
393
|
-
|
|
408
|
+
compressed=compressed,
|
|
394
409
|
)
|
|
395
410
|
return class_instance
|
|
396
411
|
|
|
@@ -406,6 +421,7 @@ class MLArray:
|
|
|
406
421
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
407
422
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
408
423
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
424
|
+
compressed: bool = True,
|
|
409
425
|
):
|
|
410
426
|
"""Create an in-memory MLArray filled with ``fill_value``.
|
|
411
427
|
|
|
@@ -439,6 +455,7 @@ class MLArray:
|
|
|
439
455
|
dtype = np.dtype(type(fill_value))
|
|
440
456
|
class_instance = cls()
|
|
441
457
|
class_instance._construct_in_memory(
|
|
458
|
+
constructor="full",
|
|
442
459
|
shape=shape,
|
|
443
460
|
dtype=dtype,
|
|
444
461
|
meta=meta,
|
|
@@ -447,7 +464,8 @@ class MLArray:
|
|
|
447
464
|
block_size=block_size,
|
|
448
465
|
cparams=cparams,
|
|
449
466
|
dparams=dparams,
|
|
450
|
-
|
|
467
|
+
compressed=compressed,
|
|
468
|
+
constructor_kwargs={"fill_value": fill_value},
|
|
451
469
|
)
|
|
452
470
|
return class_instance
|
|
453
471
|
|
|
@@ -466,6 +484,7 @@ class MLArray:
|
|
|
466
484
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
467
485
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
468
486
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
487
|
+
compressed: bool = True,
|
|
469
488
|
):
|
|
470
489
|
"""Create an in-memory MLArray with evenly spaced values.
|
|
471
490
|
|
|
@@ -525,6 +544,7 @@ class MLArray:
|
|
|
525
544
|
|
|
526
545
|
class_instance = cls()
|
|
527
546
|
class_instance._construct_in_memory(
|
|
547
|
+
constructor="arange",
|
|
528
548
|
shape=shape,
|
|
529
549
|
dtype=dtype,
|
|
530
550
|
meta=meta,
|
|
@@ -533,13 +553,13 @@ class MLArray:
|
|
|
533
553
|
block_size=block_size,
|
|
534
554
|
cparams=cparams,
|
|
535
555
|
dparams=dparams,
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
556
|
+
compressed=compressed,
|
|
557
|
+
constructor_kwargs={
|
|
558
|
+
"start": start,
|
|
559
|
+
"stop": stop,
|
|
560
|
+
"step": step,
|
|
561
|
+
"c_order": c_order,
|
|
562
|
+
},
|
|
543
563
|
)
|
|
544
564
|
return class_instance
|
|
545
565
|
|
|
@@ -559,6 +579,7 @@ class MLArray:
|
|
|
559
579
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
560
580
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
561
581
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
582
|
+
compressed: bool = True,
|
|
562
583
|
):
|
|
563
584
|
"""Create an in-memory MLArray with evenly spaced samples.
|
|
564
585
|
|
|
@@ -617,6 +638,7 @@ class MLArray:
|
|
|
617
638
|
|
|
618
639
|
class_instance = cls()
|
|
619
640
|
class_instance._construct_in_memory(
|
|
641
|
+
constructor="linspace",
|
|
620
642
|
shape=shape,
|
|
621
643
|
dtype=dtype,
|
|
622
644
|
meta=meta,
|
|
@@ -625,14 +647,14 @@ class MLArray:
|
|
|
625
647
|
block_size=block_size,
|
|
626
648
|
cparams=cparams,
|
|
627
649
|
dparams=dparams,
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
650
|
+
compressed=compressed,
|
|
651
|
+
constructor_kwargs={
|
|
652
|
+
"start": start,
|
|
653
|
+
"stop": stop,
|
|
654
|
+
"num": num,
|
|
655
|
+
"endpoint": endpoint,
|
|
656
|
+
"c_order": c_order,
|
|
657
|
+
},
|
|
636
658
|
)
|
|
637
659
|
return class_instance
|
|
638
660
|
|
|
@@ -645,7 +667,8 @@ class MLArray:
|
|
|
645
667
|
chunk_size: Optional[Union[int, List, Tuple]]= None,
|
|
646
668
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
647
669
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
648
|
-
dparams: Optional[Union[Dict, blosc2.DParams]] = None
|
|
670
|
+
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
671
|
+
compressed: bool = True,
|
|
649
672
|
):
|
|
650
673
|
"""Convert a NumPy array into an in-memory Blosc2-backed MLArray.
|
|
651
674
|
|
|
@@ -688,7 +711,16 @@ class MLArray:
|
|
|
688
711
|
implemented for the provided dimensionality.
|
|
689
712
|
"""
|
|
690
713
|
class_instance = cls()
|
|
691
|
-
class_instance._asarray(
|
|
714
|
+
class_instance._asarray(
|
|
715
|
+
array,
|
|
716
|
+
meta,
|
|
717
|
+
patch_size,
|
|
718
|
+
chunk_size,
|
|
719
|
+
block_size,
|
|
720
|
+
cparams,
|
|
721
|
+
dparams,
|
|
722
|
+
compressed=compressed,
|
|
723
|
+
)
|
|
692
724
|
return class_instance
|
|
693
725
|
|
|
694
726
|
@classmethod
|
|
@@ -702,6 +734,7 @@ class MLArray:
|
|
|
702
734
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
703
735
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
704
736
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
737
|
+
compressed: bool = True,
|
|
705
738
|
):
|
|
706
739
|
"""Create an in-memory MLArray with the same shape as ``x``.
|
|
707
740
|
|
|
@@ -730,6 +763,7 @@ class MLArray:
|
|
|
730
763
|
class_instance = cls()
|
|
731
764
|
shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
|
|
732
765
|
class_instance._construct_in_memory(
|
|
766
|
+
constructor="empty",
|
|
733
767
|
shape=shape,
|
|
734
768
|
dtype=dtype,
|
|
735
769
|
meta=meta,
|
|
@@ -738,7 +772,7 @@ class MLArray:
|
|
|
738
772
|
block_size=block_size,
|
|
739
773
|
cparams=cparams,
|
|
740
774
|
dparams=dparams,
|
|
741
|
-
|
|
775
|
+
compressed=compressed,
|
|
742
776
|
)
|
|
743
777
|
return class_instance
|
|
744
778
|
|
|
@@ -753,6 +787,7 @@ class MLArray:
|
|
|
753
787
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
754
788
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
755
789
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
790
|
+
compressed: bool = True,
|
|
756
791
|
):
|
|
757
792
|
"""Create an in-memory MLArray of zeros with the same shape as ``x``.
|
|
758
793
|
|
|
@@ -781,6 +816,7 @@ class MLArray:
|
|
|
781
816
|
class_instance = cls()
|
|
782
817
|
shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
|
|
783
818
|
class_instance._construct_in_memory(
|
|
819
|
+
constructor="zeros",
|
|
784
820
|
shape=shape,
|
|
785
821
|
dtype=dtype,
|
|
786
822
|
meta=meta,
|
|
@@ -789,7 +825,7 @@ class MLArray:
|
|
|
789
825
|
block_size=block_size,
|
|
790
826
|
cparams=cparams,
|
|
791
827
|
dparams=dparams,
|
|
792
|
-
|
|
828
|
+
compressed=compressed,
|
|
793
829
|
)
|
|
794
830
|
return class_instance
|
|
795
831
|
|
|
@@ -804,6 +840,7 @@ class MLArray:
|
|
|
804
840
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
805
841
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
806
842
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
843
|
+
compressed: bool = True,
|
|
807
844
|
):
|
|
808
845
|
"""Create an in-memory MLArray of ones with the same shape as ``x``.
|
|
809
846
|
|
|
@@ -832,6 +869,7 @@ class MLArray:
|
|
|
832
869
|
class_instance = cls()
|
|
833
870
|
shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
|
|
834
871
|
class_instance._construct_in_memory(
|
|
872
|
+
constructor="ones",
|
|
835
873
|
shape=shape,
|
|
836
874
|
dtype=dtype,
|
|
837
875
|
meta=meta,
|
|
@@ -840,7 +878,7 @@ class MLArray:
|
|
|
840
878
|
block_size=block_size,
|
|
841
879
|
cparams=cparams,
|
|
842
880
|
dparams=dparams,
|
|
843
|
-
|
|
881
|
+
compressed=compressed,
|
|
844
882
|
)
|
|
845
883
|
return class_instance
|
|
846
884
|
|
|
@@ -856,6 +894,7 @@ class MLArray:
|
|
|
856
894
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
857
895
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
858
896
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
897
|
+
compressed: bool = True,
|
|
859
898
|
):
|
|
860
899
|
"""Create an in-memory MLArray filled with ``fill_value`` and shape of ``x``.
|
|
861
900
|
|
|
@@ -886,6 +925,7 @@ class MLArray:
|
|
|
886
925
|
class_instance = cls()
|
|
887
926
|
shape, dtype, meta = class_instance._resolve_like_input(x, dtype, meta)
|
|
888
927
|
class_instance._construct_in_memory(
|
|
928
|
+
constructor="full",
|
|
889
929
|
shape=shape,
|
|
890
930
|
dtype=dtype,
|
|
891
931
|
meta=meta,
|
|
@@ -894,7 +934,8 @@ class MLArray:
|
|
|
894
934
|
block_size=block_size,
|
|
895
935
|
cparams=cparams,
|
|
896
936
|
dparams=dparams,
|
|
897
|
-
|
|
937
|
+
compressed=compressed,
|
|
938
|
+
constructor_kwargs={"fill_value": fill_value},
|
|
898
939
|
)
|
|
899
940
|
return class_instance
|
|
900
941
|
|
|
@@ -923,7 +964,8 @@ class MLArray:
|
|
|
923
964
|
|
|
924
965
|
if Path(filepath).is_file():
|
|
925
966
|
os.remove(str(filepath))
|
|
926
|
-
|
|
967
|
+
|
|
968
|
+
self._ensure_blosc2_store()
|
|
927
969
|
self._write_metadata(force=True)
|
|
928
970
|
self._store.save(str(filepath))
|
|
929
971
|
self._update_blosc2_meta()
|
|
@@ -937,6 +979,7 @@ class MLArray:
|
|
|
937
979
|
"""
|
|
938
980
|
self._write_metadata()
|
|
939
981
|
self._store = None
|
|
982
|
+
self._backend = None
|
|
940
983
|
self.filepath = None
|
|
941
984
|
self.support_metadata = None
|
|
942
985
|
self.mode = None
|
|
@@ -1360,6 +1403,7 @@ class MLArray:
|
|
|
1360
1403
|
dparams = MLArray._resolve_dparams(dparams)
|
|
1361
1404
|
|
|
1362
1405
|
self._store = blosc2.open(urlpath=str(filepath), dparams=dparams, mode=mode, mmap_mode=mmap_mode)
|
|
1406
|
+
self._backend = "blosc2"
|
|
1363
1407
|
self._read_meta()
|
|
1364
1408
|
self._update_blosc2_meta()
|
|
1365
1409
|
self.mode = mode
|
|
@@ -1439,15 +1483,13 @@ class MLArray:
|
|
|
1439
1483
|
|
|
1440
1484
|
self._validate_and_add_meta(meta, has_array=True)
|
|
1441
1485
|
spatial_axis_mask = [True] * len(shape) if self.meta.spatial.axis_labels is None else _spatial_axis_mask(self.meta.spatial.axis_labels)
|
|
1442
|
-
self.meta.blosc2 = self._comp_and_validate_blosc2_meta(self.meta.blosc2, patch_size, chunk_size, block_size, shape, np.dtype(dtype).itemsize, spatial_axis_mask)
|
|
1486
|
+
self.meta.blosc2 = self._comp_and_validate_blosc2_meta(self.meta.blosc2, patch_size, chunk_size, block_size, shape, np.dtype(dtype).itemsize, spatial_axis_mask, cparams, dparams)
|
|
1443
1487
|
self.meta._has_array.has_array = True
|
|
1444
1488
|
|
|
1445
1489
|
self.support_metadata = str(filepath).endswith(f".{MLARRAY_SUFFIX}")
|
|
1446
|
-
|
|
1447
|
-
cparams = MLArray._resolve_cparams(cparams)
|
|
1448
|
-
dparams = MLArray._resolve_dparams(dparams)
|
|
1449
1490
|
|
|
1450
|
-
self._store = blosc2.empty(shape=shape, dtype=np.dtype(dtype), urlpath=str(filepath), chunks=self.meta.blosc2.chunk_size, blocks=self.meta.blosc2.block_size, cparams=cparams, dparams=dparams, mmap_mode=mmap_mode)
|
|
1491
|
+
self._store = blosc2.empty(shape=shape, dtype=np.dtype(dtype), urlpath=str(filepath), chunks=self.meta.blosc2.chunk_size, blocks=self.meta.blosc2.block_size, cparams=MLArray._resolve_cparams(self.meta.blosc2.cparams), dparams=MLArray._resolve_dparams(self.meta.blosc2.dparams), mmap_mode=mmap_mode)
|
|
1492
|
+
self._backend = "blosc2"
|
|
1451
1493
|
self._update_blosc2_meta()
|
|
1452
1494
|
self.mode = mode
|
|
1453
1495
|
self.mmap_mode = mmap_mode
|
|
@@ -1458,6 +1500,7 @@ class MLArray:
|
|
|
1458
1500
|
self,
|
|
1459
1501
|
filepath: Union[str, Path],
|
|
1460
1502
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
1503
|
+
compressed: bool = True,
|
|
1461
1504
|
):
|
|
1462
1505
|
"""Internal MLArray load method. Loads a MLArray file. Both MLArray ('.mla') and Blosc2 ('.b2nd') files are supported.
|
|
1463
1506
|
|
|
@@ -1484,10 +1527,14 @@ class MLArray:
|
|
|
1484
1527
|
ondisk = blosc2.open(str(filepath), dparams=dparams, mode="r")
|
|
1485
1528
|
cframe = ondisk.to_cframe()
|
|
1486
1529
|
self._store = blosc2.ndarray_from_cframe(cframe, copy=True)
|
|
1530
|
+
self._backend = "blosc2"
|
|
1487
1531
|
self.mode = None
|
|
1488
1532
|
self.mmap_mode = None
|
|
1489
1533
|
self._read_meta()
|
|
1490
1534
|
self._update_blosc2_meta()
|
|
1535
|
+
if not compressed:
|
|
1536
|
+
self._store = np.asarray(self._store[...])
|
|
1537
|
+
self._backend = "numpy"
|
|
1491
1538
|
|
|
1492
1539
|
def _asarray(
|
|
1493
1540
|
self,
|
|
@@ -1497,7 +1544,8 @@ class MLArray:
|
|
|
1497
1544
|
chunk_size: Optional[Union[int, List, Tuple]]= None,
|
|
1498
1545
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
1499
1546
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
1500
|
-
dparams: Optional[Union[Dict, blosc2.DParams]] = None
|
|
1547
|
+
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
1548
|
+
compressed: bool = True,
|
|
1501
1549
|
):
|
|
1502
1550
|
"""Internal MLArray asarray method.
|
|
1503
1551
|
|
|
@@ -1539,25 +1587,20 @@ class MLArray:
|
|
|
1539
1587
|
if not isinstance(array, np.ndarray):
|
|
1540
1588
|
raise TypeError("array must be a numpy.ndarray")
|
|
1541
1589
|
self._construct_in_memory(
|
|
1542
|
-
|
|
1590
|
+
constructor="asarray",
|
|
1591
|
+
source_array=np.ascontiguousarray(array[...]),
|
|
1543
1592
|
meta=meta,
|
|
1544
1593
|
patch_size=patch_size,
|
|
1545
1594
|
chunk_size=chunk_size,
|
|
1546
1595
|
block_size=block_size,
|
|
1547
1596
|
cparams=cparams,
|
|
1548
1597
|
dparams=dparams,
|
|
1549
|
-
|
|
1550
|
-
kwargs["source_array"],
|
|
1551
|
-
chunks=kwargs["chunks"],
|
|
1552
|
-
blocks=kwargs["blocks"],
|
|
1553
|
-
cparams=kwargs["cparams"],
|
|
1554
|
-
dparams=kwargs["dparams"],
|
|
1555
|
-
),
|
|
1598
|
+
compressed=compressed,
|
|
1556
1599
|
)
|
|
1557
1600
|
|
|
1558
1601
|
def _construct_in_memory(
|
|
1559
1602
|
self,
|
|
1560
|
-
|
|
1603
|
+
constructor: str,
|
|
1561
1604
|
shape: Optional[Union[int, List, Tuple, np.ndarray]] = None,
|
|
1562
1605
|
dtype: Optional[np.dtype] = None,
|
|
1563
1606
|
source_array: Optional[np.ndarray] = None,
|
|
@@ -1567,6 +1610,8 @@ class MLArray:
|
|
|
1567
1610
|
block_size: Optional[Union[int, List, Tuple]] = None,
|
|
1568
1611
|
cparams: Optional[Union[Dict, blosc2.CParams]] = None,
|
|
1569
1612
|
dparams: Optional[Union[Dict, blosc2.DParams]] = None,
|
|
1613
|
+
compressed: bool = True,
|
|
1614
|
+
constructor_kwargs: Optional[dict[str, Any]] = None,
|
|
1570
1615
|
):
|
|
1571
1616
|
"""Internal generic constructor for in-memory Blosc2-backed MLArrays.
|
|
1572
1617
|
|
|
@@ -1575,8 +1620,7 @@ class MLArray:
|
|
|
1575
1620
|
array shape, required when ``source_array`` is None.
|
|
1576
1621
|
dtype (Optional[np.dtype]): Target dtype, required when
|
|
1577
1622
|
``source_array`` is None.
|
|
1578
|
-
|
|
1579
|
-
returning a Blosc2 NDArray.
|
|
1623
|
+
constructor (str): Constructor operation name.
|
|
1580
1624
|
source_array (Optional[np.ndarray]): Source array that should be
|
|
1581
1625
|
converted into an in-memory Blosc2-backed store.
|
|
1582
1626
|
meta (Optional[Union[Dict, Meta]]): Optional metadata attached to
|
|
@@ -1599,12 +1643,13 @@ class MLArray:
|
|
|
1599
1643
|
Raises:
|
|
1600
1644
|
ValueError: If constructor inputs are inconsistent.
|
|
1601
1645
|
"""
|
|
1646
|
+
constructor_kwargs = {} if constructor_kwargs is None else dict(constructor_kwargs)
|
|
1647
|
+
|
|
1602
1648
|
if source_array is not None:
|
|
1603
1649
|
if shape is not None or dtype is not None:
|
|
1604
1650
|
raise ValueError(
|
|
1605
1651
|
"shape/dtype must not be set when source_array is provided."
|
|
1606
1652
|
)
|
|
1607
|
-
source_array = np.ascontiguousarray(source_array[...])
|
|
1608
1653
|
shape = self._normalize_shape(source_array.shape)
|
|
1609
1654
|
dtype = np.dtype(source_array.dtype)
|
|
1610
1655
|
else:
|
|
@@ -1621,6 +1666,7 @@ class MLArray:
|
|
|
1621
1666
|
if self.meta.spatial.axis_labels is None
|
|
1622
1667
|
else _spatial_axis_mask(self.meta.spatial.axis_labels)
|
|
1623
1668
|
)
|
|
1669
|
+
|
|
1624
1670
|
self.meta.blosc2 = self._comp_and_validate_blosc2_meta(
|
|
1625
1671
|
self.meta.blosc2,
|
|
1626
1672
|
patch_size,
|
|
@@ -1629,29 +1675,101 @@ class MLArray:
|
|
|
1629
1675
|
shape,
|
|
1630
1676
|
dtype.itemsize,
|
|
1631
1677
|
spatial_axis_mask,
|
|
1678
|
+
cparams,
|
|
1679
|
+
dparams,
|
|
1632
1680
|
)
|
|
1633
1681
|
self.meta._has_array.has_array = True
|
|
1634
1682
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1683
|
+
backend_lib = blosc2 if compressed else np
|
|
1684
|
+
blosc2_storage_kwargs = {}
|
|
1685
|
+
if compressed:
|
|
1686
|
+
blosc2_storage_kwargs = {
|
|
1687
|
+
"chunks": self.meta.blosc2.chunk_size,
|
|
1688
|
+
"blocks": self.meta.blosc2.block_size,
|
|
1689
|
+
"cparams": MLArray._resolve_cparams(self.meta.blosc2.cparams),
|
|
1690
|
+
"dparams": MLArray._resolve_dparams(self.meta.blosc2.dparams),
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
if constructor == "asarray":
|
|
1694
|
+
if compressed:
|
|
1695
|
+
self._store = blosc2.asarray(source_array, **blosc2_storage_kwargs)
|
|
1696
|
+
else:
|
|
1697
|
+
self._store = np.asarray(source_array, dtype=dtype)
|
|
1698
|
+
else:
|
|
1699
|
+
call_kwargs = self._build_constructor_call_kwargs(
|
|
1700
|
+
constructor=constructor,
|
|
1701
|
+
shape=shape,
|
|
1702
|
+
dtype=dtype,
|
|
1703
|
+
constructor_kwargs=constructor_kwargs,
|
|
1704
|
+
)
|
|
1637
1705
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1706
|
+
if not compressed and constructor in ("arange", "linspace"):
|
|
1707
|
+
self._store = self._construct_numpy_range(constructor, call_kwargs)
|
|
1708
|
+
else:
|
|
1709
|
+
func = getattr(backend_lib, constructor, None)
|
|
1710
|
+
if func is None:
|
|
1711
|
+
raise ValueError(f"Unknown constructor '{constructor}'.")
|
|
1712
|
+
if compressed:
|
|
1713
|
+
call_kwargs.update(blosc2_storage_kwargs)
|
|
1714
|
+
self._store = func(**call_kwargs)
|
|
1715
|
+
|
|
1716
|
+
self._backend = "blosc2" if compressed else "numpy"
|
|
1648
1717
|
|
|
1649
|
-
self.
|
|
1718
|
+
self.support_metadata = True
|
|
1650
1719
|
|
|
1651
1720
|
self._update_blosc2_meta()
|
|
1652
1721
|
self._validate_and_add_meta(self.meta)
|
|
1653
1722
|
|
|
1654
|
-
|
|
1723
|
+
@staticmethod
|
|
1724
|
+
def _build_constructor_call_kwargs(
|
|
1725
|
+
constructor: str,
|
|
1726
|
+
shape: Tuple[int, ...],
|
|
1727
|
+
dtype: np.dtype,
|
|
1728
|
+
constructor_kwargs: dict[str, Any],
|
|
1729
|
+
) -> dict[str, Any]:
|
|
1730
|
+
"""Build constructor kwargs shared by NumPy and Blosc2 backends."""
|
|
1731
|
+
if constructor in ("empty", "zeros", "ones"):
|
|
1732
|
+
return {"shape": shape, "dtype": dtype}
|
|
1733
|
+
if constructor == "full":
|
|
1734
|
+
return {
|
|
1735
|
+
"shape": shape,
|
|
1736
|
+
"fill_value": constructor_kwargs["fill_value"],
|
|
1737
|
+
"dtype": dtype,
|
|
1738
|
+
}
|
|
1739
|
+
if constructor == "arange":
|
|
1740
|
+
return {
|
|
1741
|
+
"start": constructor_kwargs["start"],
|
|
1742
|
+
"stop": constructor_kwargs["stop"],
|
|
1743
|
+
"step": constructor_kwargs["step"],
|
|
1744
|
+
"dtype": dtype,
|
|
1745
|
+
"shape": shape,
|
|
1746
|
+
"c_order": constructor_kwargs.get("c_order", True),
|
|
1747
|
+
}
|
|
1748
|
+
if constructor == "linspace":
|
|
1749
|
+
return {
|
|
1750
|
+
"start": constructor_kwargs["start"],
|
|
1751
|
+
"stop": constructor_kwargs["stop"],
|
|
1752
|
+
"num": constructor_kwargs["num"],
|
|
1753
|
+
"dtype": dtype,
|
|
1754
|
+
"shape": shape,
|
|
1755
|
+
"endpoint": constructor_kwargs.get("endpoint", True),
|
|
1756
|
+
"c_order": constructor_kwargs.get("c_order", True),
|
|
1757
|
+
}
|
|
1758
|
+
raise ValueError(f"Unknown constructor '{constructor}'.")
|
|
1759
|
+
|
|
1760
|
+
@staticmethod
|
|
1761
|
+
def _construct_numpy_range(
|
|
1762
|
+
constructor: str,
|
|
1763
|
+
call_kwargs: dict[str, Any],
|
|
1764
|
+
) -> np.ndarray:
|
|
1765
|
+
"""Construct NumPy arange/linspace arrays and apply shape/order reshaping."""
|
|
1766
|
+
shape = call_kwargs.pop("shape")
|
|
1767
|
+
c_order = call_kwargs.pop("c_order", True)
|
|
1768
|
+
func = getattr(np, constructor)
|
|
1769
|
+
arr = func(**call_kwargs)
|
|
1770
|
+
return np.reshape(arr, shape, order="C" if c_order else "F")
|
|
1771
|
+
|
|
1772
|
+
def _comp_and_validate_blosc2_meta(self, meta_blosc2, patch_size, chunk_size, block_size, shape, dtype_itemsize, spatial_axis_mask, cparams, dparams):
|
|
1655
1773
|
"""Compute and validate Blosc2 chunk/block metadata.
|
|
1656
1774
|
|
|
1657
1775
|
Args:
|
|
@@ -1693,7 +1811,16 @@ class MLArray:
|
|
|
1693
1811
|
if patch_size is not None:
|
|
1694
1812
|
chunk_size, block_size = MLArray.comp_blosc2_params(shape, patch_size, spatial_axis_mask, bytes_per_pixel=dtype_itemsize)
|
|
1695
1813
|
|
|
1696
|
-
|
|
1814
|
+
cparams = MLArray._resolve_cparams(cparams)
|
|
1815
|
+
dparams = MLArray._resolve_dparams(dparams)
|
|
1816
|
+
|
|
1817
|
+
meta_blosc2 = MetaBlosc2(
|
|
1818
|
+
chunk_size=chunk_size,
|
|
1819
|
+
block_size=block_size,
|
|
1820
|
+
patch_size=patch_size,
|
|
1821
|
+
cparams=cparams,
|
|
1822
|
+
dparams=dparams,
|
|
1823
|
+
)
|
|
1697
1824
|
meta_blosc2._validate_and_cast(ndims=len(shape), spatial_ndims=num_spatial_axes)
|
|
1698
1825
|
return meta_blosc2
|
|
1699
1826
|
|
|
@@ -1747,14 +1874,16 @@ class MLArray:
|
|
|
1747
1874
|
Updates ``self.meta.blosc2`` from the underlying store when the array
|
|
1748
1875
|
is present.
|
|
1749
1876
|
"""
|
|
1750
|
-
if self.
|
|
1877
|
+
if self._backend != "blosc2":
|
|
1878
|
+
return
|
|
1879
|
+
if self.support_metadata and self.meta._has_array.has_array == True:
|
|
1751
1880
|
self.meta.blosc2.chunk_size = list(self._store.chunks)
|
|
1752
1881
|
self.meta.blosc2.block_size = list(self._store.blocks)
|
|
1753
1882
|
|
|
1754
1883
|
def _read_meta(self):
|
|
1755
1884
|
"""Read MLArray metadata from the underlying store, if available."""
|
|
1756
1885
|
meta = Meta()
|
|
1757
|
-
if self.support_metadata and
|
|
1886
|
+
if self.support_metadata and self._backend == "blosc2":
|
|
1758
1887
|
meta = self._store.vlmeta["mlarray"]
|
|
1759
1888
|
meta = Meta.from_mapping(meta)
|
|
1760
1889
|
self._validate_and_add_meta(meta)
|
|
@@ -1766,7 +1895,7 @@ class MLArray:
|
|
|
1766
1895
|
force (bool): If True, write even when mmap_mode is read-only.
|
|
1767
1896
|
"""
|
|
1768
1897
|
is_writable = False
|
|
1769
|
-
if self.support_metadata
|
|
1898
|
+
if self.support_metadata:
|
|
1770
1899
|
if self.mode in ('a', 'w') and self.mmap_mode is None:
|
|
1771
1900
|
is_writable = True
|
|
1772
1901
|
elif self.mmap_mode in ('r+', 'w+'):
|
|
@@ -1776,12 +1905,57 @@ class MLArray:
|
|
|
1776
1905
|
|
|
1777
1906
|
if not is_writable:
|
|
1778
1907
|
return
|
|
1779
|
-
|
|
1908
|
+
|
|
1909
|
+
if self._backend != "blosc2":
|
|
1910
|
+
return
|
|
1911
|
+
|
|
1780
1912
|
metadata = self.meta.to_mapping()
|
|
1781
1913
|
if not is_serializable(metadata):
|
|
1782
1914
|
raise RuntimeError("Metadata is not serializable.")
|
|
1783
1915
|
self._store.vlmeta["mlarray"] = metadata
|
|
1784
1916
|
|
|
1917
|
+
def _ensure_blosc2_store(self):
|
|
1918
|
+
"""Ensure underlying store is Blosc2, converting from NumPy when needed."""
|
|
1919
|
+
if self._store is None:
|
|
1920
|
+
self._store = blosc2.empty((0,))
|
|
1921
|
+
self._backend = "blosc2"
|
|
1922
|
+
return
|
|
1923
|
+
|
|
1924
|
+
if self._backend == "blosc2":
|
|
1925
|
+
return
|
|
1926
|
+
|
|
1927
|
+
array = np.asarray(self._store)
|
|
1928
|
+
if not self.meta._has_array.has_array:
|
|
1929
|
+
self._store = blosc2.empty((0,))
|
|
1930
|
+
self._backend = "blosc2"
|
|
1931
|
+
return
|
|
1932
|
+
|
|
1933
|
+
shape = self._normalize_shape(array.shape)
|
|
1934
|
+
spatial_axis_mask = (
|
|
1935
|
+
[True] * len(shape)
|
|
1936
|
+
if self.meta.spatial.axis_labels is None
|
|
1937
|
+
else _spatial_axis_mask(self.meta.spatial.axis_labels)
|
|
1938
|
+
)
|
|
1939
|
+
self.meta.blosc2 = self._comp_and_validate_blosc2_meta(
|
|
1940
|
+
self.meta.blosc2,
|
|
1941
|
+
patch_size="default",
|
|
1942
|
+
chunk_size=self.meta.blosc2.chunk_size,
|
|
1943
|
+
block_size=self.meta.blosc2.block_size,
|
|
1944
|
+
shape=shape,
|
|
1945
|
+
dtype_itemsize=np.dtype(array.dtype).itemsize,
|
|
1946
|
+
spatial_axis_mask=spatial_axis_mask,
|
|
1947
|
+
cparams=self.meta.blosc2.cparams,
|
|
1948
|
+
dparams=self.meta.blosc2.dparams,
|
|
1949
|
+
)
|
|
1950
|
+
self._store = blosc2.asarray(
|
|
1951
|
+
np.ascontiguousarray(array),
|
|
1952
|
+
chunks=self.meta.blosc2.chunk_size,
|
|
1953
|
+
blocks=self.meta.blosc2.block_size,
|
|
1954
|
+
cparams=MLArray._resolve_cparams(self.meta.blosc2.cparams),
|
|
1955
|
+
dparams=MLArray._resolve_dparams(self.meta.blosc2.dparams),
|
|
1956
|
+
)
|
|
1957
|
+
self._backend = "blosc2"
|
|
1958
|
+
|
|
1785
1959
|
@staticmethod
|
|
1786
1960
|
def _normalize_shape(shape: Union[int, List, Tuple, np.ndarray]) -> Tuple[int, ...]:
|
|
1787
1961
|
if isinstance(shape, (int, np.integer)):
|
|
@@ -1812,6 +1986,19 @@ class MLArray:
|
|
|
1812
1986
|
"""Resolve compression params with MLArray defaults."""
|
|
1813
1987
|
if cparams is None:
|
|
1814
1988
|
return {"codec": blosc2.Codec.LZ4HC, "clevel": 8}
|
|
1989
|
+
if isinstance(cparams, dict):
|
|
1990
|
+
cparams = dict(cparams)
|
|
1991
|
+
if "codec" in cparams and not isinstance(cparams["codec"], blosc2.Codec):
|
|
1992
|
+
cparams["codec"] = blosc2.Codec(cparams["codec"])
|
|
1993
|
+
if "splitmode" in cparams and not isinstance(cparams["splitmode"], blosc2.SplitMode):
|
|
1994
|
+
cparams["splitmode"] = blosc2.SplitMode(cparams["splitmode"])
|
|
1995
|
+
if "tuner" in cparams and not isinstance(cparams["tuner"], blosc2.Tuner):
|
|
1996
|
+
cparams["tuner"] = blosc2.Tuner(cparams["tuner"])
|
|
1997
|
+
if "filters" in cparams and isinstance(cparams["filters"], (list, tuple)):
|
|
1998
|
+
cparams["filters"] = [
|
|
1999
|
+
f if isinstance(f, blosc2.Filter) else blosc2.Filter(f)
|
|
2000
|
+
for f in cparams["filters"]
|
|
2001
|
+
]
|
|
1815
2002
|
return cparams
|
|
1816
2003
|
|
|
1817
2004
|
@staticmethod
|
|
@@ -22,6 +22,7 @@ docs/why.md
|
|
|
22
22
|
examples/example_asarray.py
|
|
23
23
|
examples/example_bboxes_only.py
|
|
24
24
|
examples/example_channel.py
|
|
25
|
+
examples/example_compressed_vs_uncompressed.py
|
|
25
26
|
examples/example_in_memory_constructors.py
|
|
26
27
|
examples/example_metadata_only.py
|
|
27
28
|
examples/example_non_spatial.py
|
|
@@ -64,6 +64,22 @@ class TestAsArray(unittest.TestCase):
|
|
|
64
64
|
self.assertEqual(image.meta.source.to_plain(), {"patient_id": "p-001"})
|
|
65
65
|
self.assertTrue(image.meta.is_seg)
|
|
66
66
|
|
|
67
|
+
def test_asarray_uncompressed_numpy_store_and_save(self):
|
|
68
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
69
|
+
array = _make_array(seed=3)
|
|
70
|
+
image = MLArray.asarray(array, meta={"case_id": "np"}, compressed=False)
|
|
71
|
+
|
|
72
|
+
self.assertTrue(isinstance(image._store, np.ndarray))
|
|
73
|
+
self.assertTrue(np.allclose(image.to_numpy(), array))
|
|
74
|
+
self.assertEqual(image.meta.source.to_plain(), {"case_id": "np"})
|
|
75
|
+
|
|
76
|
+
path = Path(tmpdir) / "asarray-numpy-save.mla"
|
|
77
|
+
image.save(path)
|
|
78
|
+
loaded = MLArray(path)
|
|
79
|
+
self.assertFalse(isinstance(loaded._store, np.ndarray))
|
|
80
|
+
self.assertTrue(np.allclose(loaded.to_numpy(), array))
|
|
81
|
+
self.assertEqual(loaded.meta.source.to_plain(), {"case_id": "np"})
|
|
82
|
+
|
|
67
83
|
|
|
68
84
|
if __name__ == "__main__":
|
|
69
85
|
unittest.main()
|
|
@@ -25,6 +25,18 @@ class TestUsage(unittest.TestCase):
|
|
|
25
25
|
loaded = MLArray(path)
|
|
26
26
|
self.assertEqual(loaded.shape, array.shape)
|
|
27
27
|
|
|
28
|
+
def test_default_usage_uncompressed_backend(self):
|
|
29
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
30
|
+
array = _make_array(seed=11)
|
|
31
|
+
image = MLArray(array, compressed=False)
|
|
32
|
+
self.assertTrue(isinstance(image._store, np.ndarray))
|
|
33
|
+
|
|
34
|
+
path = Path(tmpdir) / "sample-uncompressed.mla"
|
|
35
|
+
image.save(path)
|
|
36
|
+
loaded = MLArray(path)
|
|
37
|
+
self.assertEqual(loaded.shape, array.shape)
|
|
38
|
+
self.assertTrue(np.allclose(loaded.to_numpy(), array))
|
|
39
|
+
|
|
28
40
|
def test_mmap_loading(self):
|
|
29
41
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
30
42
|
array = _make_array()
|
|
@@ -34,6 +46,16 @@ class TestUsage(unittest.TestCase):
|
|
|
34
46
|
loaded = MLArray.open(path, mmap_mode="r")
|
|
35
47
|
self.assertFalse(isinstance(loaded._store, np.ndarray))
|
|
36
48
|
|
|
49
|
+
def test_load_uncompressed_store(self):
|
|
50
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
51
|
+
array = _make_array()
|
|
52
|
+
path = Path(tmpdir) / "sample-load-uncompressed.mla"
|
|
53
|
+
MLArray(array).save(path)
|
|
54
|
+
|
|
55
|
+
loaded = MLArray.load(path, compressed=False)
|
|
56
|
+
self.assertTrue(isinstance(loaded._store, np.ndarray))
|
|
57
|
+
self.assertTrue(np.allclose(loaded.to_numpy(), array))
|
|
58
|
+
|
|
37
59
|
def test_loading_and_saving(self):
|
|
38
60
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
39
61
|
array = _make_array()
|
|
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
|