monai-weekly 1.5.dev2451__py3-none-any.whl → 1.5.dev2501__py3-none-any.whl
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.
- monai/__init__.py +1 -1
- monai/_version.py +3 -3
- monai/data/image_reader.py +77 -9
- monai/data/meta_tensor.py +0 -1
- monai/transforms/io/array.py +0 -1
- {monai_weekly-1.5.dev2451.dist-info → monai_weekly-1.5.dev2501.dist-info}/METADATA +1 -1
- {monai_weekly-1.5.dev2451.dist-info → monai_weekly-1.5.dev2501.dist-info}/RECORD +10 -10
- {monai_weekly-1.5.dev2451.dist-info → monai_weekly-1.5.dev2501.dist-info}/LICENSE +0 -0
- {monai_weekly-1.5.dev2451.dist-info → monai_weekly-1.5.dev2501.dist-info}/WHEEL +0 -0
- {monai_weekly-1.5.dev2451.dist-info → monai_weekly-1.5.dev2501.dist-info}/top_level.txt +0 -0
monai/__init__.py
CHANGED
monai/_version.py
CHANGED
@@ -8,11 +8,11 @@ import json
|
|
8
8
|
|
9
9
|
version_json = '''
|
10
10
|
{
|
11
|
-
"date": "
|
11
|
+
"date": "2025-01-05T02:28:29+0000",
|
12
12
|
"dirty": false,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "1.5.
|
14
|
+
"full-revisionid": "5a5531d74a1a2cb3f0f3bbe16f5d445ca4639456",
|
15
|
+
"version": "1.5.dev2501"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
monai/data/image_reader.py
CHANGED
@@ -12,8 +12,11 @@
|
|
12
12
|
from __future__ import annotations
|
13
13
|
|
14
14
|
import glob
|
15
|
+
import gzip
|
16
|
+
import io
|
15
17
|
import os
|
16
18
|
import re
|
19
|
+
import tempfile
|
17
20
|
import warnings
|
18
21
|
from abc import ABC, abstractmethod
|
19
22
|
from collections.abc import Callable, Iterable, Iterator, Sequence
|
@@ -51,6 +54,9 @@ else:
|
|
51
54
|
pydicom, has_pydicom = optional_import("pydicom")
|
52
55
|
nrrd, has_nrrd = optional_import("nrrd", allow_namespace_pkg=True)
|
53
56
|
|
57
|
+
cp, has_cp = optional_import("cupy")
|
58
|
+
kvikio, has_kvikio = optional_import("kvikio")
|
59
|
+
|
54
60
|
__all__ = ["ImageReader", "ITKReader", "NibabelReader", "NumpyReader", "PILReader", "PydicomReader", "NrrdReader"]
|
55
61
|
|
56
62
|
|
@@ -137,14 +143,18 @@ def _copy_compatible_dict(from_dict: dict, to_dict: dict):
|
|
137
143
|
)
|
138
144
|
|
139
145
|
|
140
|
-
def _stack_images(image_list: list, meta_dict: dict):
|
146
|
+
def _stack_images(image_list: list, meta_dict: dict, to_cupy: bool = False):
|
141
147
|
if len(image_list) <= 1:
|
142
148
|
return image_list[0]
|
143
149
|
if not is_no_channel(meta_dict.get(MetaKeys.ORIGINAL_CHANNEL_DIM, None)):
|
144
150
|
channel_dim = int(meta_dict[MetaKeys.ORIGINAL_CHANNEL_DIM])
|
151
|
+
if to_cupy and has_cp:
|
152
|
+
return cp.concatenate(image_list, axis=channel_dim)
|
145
153
|
return np.concatenate(image_list, axis=channel_dim)
|
146
154
|
# stack at a new first dim as the channel dim, if `'original_channel_dim'` is unspecified
|
147
155
|
meta_dict[MetaKeys.ORIGINAL_CHANNEL_DIM] = 0
|
156
|
+
if to_cupy and has_cp:
|
157
|
+
return cp.stack(image_list, axis=0)
|
148
158
|
return np.stack(image_list, axis=0)
|
149
159
|
|
150
160
|
|
@@ -864,12 +874,18 @@ class NibabelReader(ImageReader):
|
|
864
874
|
Load NIfTI format images based on Nibabel library.
|
865
875
|
|
866
876
|
Args:
|
867
|
-
as_closest_canonical: if True, load the image as closest to canonical axis format.
|
868
|
-
squeeze_non_spatial_dims: if True, non-spatial singletons will be squeezed, e.g. (256,256,1,3) -> (256,256,3)
|
869
877
|
channel_dim: the channel dimension of the input image, default is None.
|
870
878
|
this is used to set original_channel_dim in the metadata, EnsureChannelFirstD reads this field.
|
871
879
|
if None, `original_channel_dim` will be either `no_channel` or `-1`.
|
872
880
|
most Nifti files are usually "channel last", no need to specify this argument for them.
|
881
|
+
as_closest_canonical: if True, load the image as closest to canonical axis format.
|
882
|
+
squeeze_non_spatial_dims: if True, non-spatial singletons will be squeezed, e.g. (256,256,1,3) -> (256,256,3)
|
883
|
+
to_gpu: If True, load the image into GPU memory using CuPy and Kvikio. This can accelerate data loading.
|
884
|
+
Default is False. CuPy and Kvikio are required for this option.
|
885
|
+
Note: For compressed NIfTI files, some operations may still be performed on CPU memory,
|
886
|
+
and the acceleration may not be significant. In some cases, it may be slower than loading on CPU.
|
887
|
+
In practical use, it's recommended to add a warm up call before the actual loading.
|
888
|
+
A related tutorial will be prepared in the future, and the document will be updated accordingly.
|
873
889
|
kwargs: additional args for `nibabel.load` API. more details about available args:
|
874
890
|
https://github.com/nipy/nibabel/blob/master/nibabel/loadsave.py
|
875
891
|
|
@@ -880,14 +896,42 @@ class NibabelReader(ImageReader):
|
|
880
896
|
channel_dim: str | int | None = None,
|
881
897
|
as_closest_canonical: bool = False,
|
882
898
|
squeeze_non_spatial_dims: bool = False,
|
899
|
+
to_gpu: bool = False,
|
883
900
|
**kwargs,
|
884
901
|
):
|
885
902
|
super().__init__()
|
886
903
|
self.channel_dim = float("nan") if channel_dim == "no_channel" else channel_dim
|
887
904
|
self.as_closest_canonical = as_closest_canonical
|
888
905
|
self.squeeze_non_spatial_dims = squeeze_non_spatial_dims
|
906
|
+
if to_gpu and (not has_cp or not has_kvikio):
|
907
|
+
warnings.warn(
|
908
|
+
"NibabelReader: CuPy and/or Kvikio not installed for GPU loading, falling back to CPU loading."
|
909
|
+
)
|
910
|
+
to_gpu = False
|
911
|
+
|
912
|
+
if to_gpu:
|
913
|
+
self.warmup_kvikio()
|
914
|
+
|
915
|
+
self.to_gpu = to_gpu
|
889
916
|
self.kwargs = kwargs
|
890
917
|
|
918
|
+
def warmup_kvikio(self):
|
919
|
+
"""
|
920
|
+
Warm up the Kvikio library to initialize the internal buffers, cuFile, GDS, etc.
|
921
|
+
This can accelerate the data loading process when `to_gpu` is set to True.
|
922
|
+
"""
|
923
|
+
if has_cp and has_kvikio:
|
924
|
+
a = cp.arange(100)
|
925
|
+
with tempfile.NamedTemporaryFile() as tmp_file:
|
926
|
+
tmp_file_name = tmp_file.name
|
927
|
+
f = kvikio.CuFile(tmp_file_name, "w")
|
928
|
+
f.write(a)
|
929
|
+
f.close()
|
930
|
+
|
931
|
+
b = cp.empty_like(a)
|
932
|
+
f = kvikio.CuFile(tmp_file_name, "r")
|
933
|
+
f.read(b)
|
934
|
+
|
891
935
|
def verify_suffix(self, filename: Sequence[PathLike] | PathLike) -> bool:
|
892
936
|
"""
|
893
937
|
Verify whether the specified file or files format is supported by Nibabel reader.
|
@@ -916,6 +960,7 @@ class NibabelReader(ImageReader):
|
|
916
960
|
img_: list[Nifti1Image] = []
|
917
961
|
|
918
962
|
filenames: Sequence[PathLike] = ensure_tuple(data)
|
963
|
+
self.filenames = filenames
|
919
964
|
kwargs_ = self.kwargs.copy()
|
920
965
|
kwargs_.update(kwargs)
|
921
966
|
for name in filenames:
|
@@ -936,10 +981,13 @@ class NibabelReader(ImageReader):
|
|
936
981
|
img: a Nibabel image object loaded from an image file or a list of Nibabel image objects.
|
937
982
|
|
938
983
|
"""
|
984
|
+
# TODO: the actual type is list[np.ndarray | cp.ndarray]
|
985
|
+
# should figure out how to define correct types without having cupy not found error
|
986
|
+
# https://github.com/Project-MONAI/MONAI/pull/8188#discussion_r1886645918
|
939
987
|
img_array: list[np.ndarray] = []
|
940
988
|
compatible_meta: dict = {}
|
941
989
|
|
942
|
-
for i in ensure_tuple(img):
|
990
|
+
for i, filename in zip(ensure_tuple(img), self.filenames):
|
943
991
|
header = self._get_meta_dict(i)
|
944
992
|
header[MetaKeys.AFFINE] = self._get_affine(i)
|
945
993
|
header[MetaKeys.ORIGINAL_AFFINE] = self._get_affine(i)
|
@@ -949,7 +997,7 @@ class NibabelReader(ImageReader):
|
|
949
997
|
header[MetaKeys.AFFINE] = self._get_affine(i)
|
950
998
|
header[MetaKeys.SPATIAL_SHAPE] = self._get_spatial_shape(i)
|
951
999
|
header[MetaKeys.SPACE] = SpaceKeys.RAS
|
952
|
-
data = self._get_array_data(i)
|
1000
|
+
data = self._get_array_data(i, filename)
|
953
1001
|
if self.squeeze_non_spatial_dims:
|
954
1002
|
for d in range(len(data.shape), len(header[MetaKeys.SPATIAL_SHAPE]), -1):
|
955
1003
|
if data.shape[d - 1] == 1:
|
@@ -963,7 +1011,7 @@ class NibabelReader(ImageReader):
|
|
963
1011
|
header[MetaKeys.ORIGINAL_CHANNEL_DIM] = self.channel_dim
|
964
1012
|
_copy_compatible_dict(header, compatible_meta)
|
965
1013
|
|
966
|
-
return _stack_images(img_array, compatible_meta), compatible_meta
|
1014
|
+
return _stack_images(img_array, compatible_meta, to_cupy=self.to_gpu), compatible_meta
|
967
1015
|
|
968
1016
|
def _get_meta_dict(self, img) -> dict:
|
969
1017
|
"""
|
@@ -1015,14 +1063,34 @@ class NibabelReader(ImageReader):
|
|
1015
1063
|
spatial_rank = max(min(ndim, 3), 1)
|
1016
1064
|
return np.asarray(size[:spatial_rank])
|
1017
1065
|
|
1018
|
-
def _get_array_data(self, img):
|
1066
|
+
def _get_array_data(self, img, filename):
|
1019
1067
|
"""
|
1020
1068
|
Get the raw array data of the image, converted to Numpy array.
|
1021
1069
|
|
1022
1070
|
Args:
|
1023
1071
|
img: a Nibabel image object loaded from an image file.
|
1024
|
-
|
1025
|
-
|
1072
|
+
filename: file name of the image.
|
1073
|
+
|
1074
|
+
"""
|
1075
|
+
if self.to_gpu:
|
1076
|
+
file_size = os.path.getsize(filename)
|
1077
|
+
image = cp.empty(file_size, dtype=cp.uint8)
|
1078
|
+
with kvikio.CuFile(filename, "r") as f:
|
1079
|
+
f.read(image)
|
1080
|
+
if filename.endswith(".nii.gz"):
|
1081
|
+
# for compressed data, have to tansfer to CPU to decompress
|
1082
|
+
# and then transfer back to GPU. It is not efficient compared to .nii file
|
1083
|
+
# and may be slower than CPU loading in some cases.
|
1084
|
+
warnings.warn("Loading compressed NIfTI file into GPU may not be efficient.")
|
1085
|
+
compressed_data = cp.asnumpy(image)
|
1086
|
+
with gzip.GzipFile(fileobj=io.BytesIO(compressed_data)) as gz_file:
|
1087
|
+
decompressed_data = gz_file.read()
|
1088
|
+
|
1089
|
+
image = cp.frombuffer(decompressed_data, dtype=cp.uint8)
|
1090
|
+
data_shape = img.shape
|
1091
|
+
data_offset = img.dataobj.offset
|
1092
|
+
data_dtype = img.dataobj.dtype
|
1093
|
+
return image[data_offset:].view(data_dtype).reshape(data_shape, order="F")
|
1026
1094
|
return np.asanyarray(img.dataobj, order="C")
|
1027
1095
|
|
1028
1096
|
|
monai/data/meta_tensor.py
CHANGED
@@ -553,7 +553,6 @@ class MetaTensor(MetaObj, torch.Tensor):
|
|
553
553
|
However, if `get_track_meta()` is `False` or meta=None, a `torch.Tensor` is returned.
|
554
554
|
"""
|
555
555
|
img = convert_to_tensor(im, track_meta=get_track_meta() and meta is not None) # potentially ascontiguousarray
|
556
|
-
|
557
556
|
# if not tracking metadata, return `torch.Tensor`
|
558
557
|
if not isinstance(img, MetaTensor):
|
559
558
|
return img
|
monai/transforms/io/array.py
CHANGED
@@ -286,7 +286,6 @@ class LoadImage(Transform):
|
|
286
286
|
" https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies.\n"
|
287
287
|
f" The current registered: {self.readers}.\n{msg}"
|
288
288
|
)
|
289
|
-
|
290
289
|
img_array: NdarrayOrTensor
|
291
290
|
img_array, meta_data = reader.get_data(img)
|
292
291
|
img_array = convert_to_dst_type(img_array, dst=img_array, dtype=self.dtype)[0]
|
@@ -1,5 +1,5 @@
|
|
1
|
-
monai/__init__.py,sha256=
|
2
|
-
monai/_version.py,sha256=
|
1
|
+
monai/__init__.py,sha256=EDmpj3WGm6pqQLjdXAYmCYvFgRTzMlBZ9vsUlJF2-H8,4095
|
2
|
+
monai/_version.py,sha256=hq5lqkY7TJcQbePDU-i2WF2tRziUTMy7_5pXWu8u1IA,503
|
3
3
|
monai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
monai/_extensions/__init__.py,sha256=NEBPreRhQ8H9gVvgrLr_y52_TmqB96u_u4VQmeNT93I,642
|
5
5
|
monai/_extensions/loader.py,sha256=7SiKw36q-nOzH8CRbBurFrz7GM40GCu7rc93Tm8XpnI,3643
|
@@ -131,12 +131,12 @@ monai/data/fft_utils.py,sha256=in9Zu8hC4oSVzuA-Zl236X6EkvgFka0RXdOxgvdGkv0,4448
|
|
131
131
|
monai/data/folder_layout.py,sha256=IsHW1-Bkupn_T8r6MgFTIJQh5HwCg0xQwOKmgBtl0gE,6344
|
132
132
|
monai/data/grid_dataset.py,sha256=O0gHf3BgrisH1erOMZNSpoIut92mydnNpzGYWnBKg4U,19483
|
133
133
|
monai/data/image_dataset.py,sha256=OhNJ3awauWtqsDhefIGDw3UYGF3RoleeNUPdJOKg3kI,7008
|
134
|
-
monai/data/image_reader.py,sha256=
|
134
|
+
monai/data/image_reader.py,sha256=5Hd0rSkK7D-dWt6LENS2AJ8BZqkPLrlc0jdxRMOWGUM,65114
|
135
135
|
monai/data/image_writer.py,sha256=rH6vboPFkX4ziN3lnrmK6AzAOQYI9tEiOJb7Al2tj-8,39856
|
136
136
|
monai/data/iterable_dataset.py,sha256=A0L5jaxwnfgProBj96tlT160esI21yutnTf3a4c29Ms,13100
|
137
137
|
monai/data/itk_torch_bridge.py,sha256=3th-B3tJuJE22JFfOUgGeTMOPh1czJEiSccFyn_Ob0w,14461
|
138
138
|
monai/data/meta_obj.py,sha256=EDQdYbc4HkHcuDYbokvuIbDW-peqvnPW2JPWT8zgaNU,8827
|
139
|
-
monai/data/meta_tensor.py,sha256=
|
139
|
+
monai/data/meta_tensor.py,sha256=iK3er8zkj88O4XxTOBO2slhuwxBKuM-3Xh_HUejxo2I,27556
|
140
140
|
monai/data/samplers.py,sha256=LUCAHy38ddGm67oJJp3W6ITBsDRqyGCrKtYn-pjrWc4,5102
|
141
141
|
monai/data/synthetic.py,sha256=H0MaQq2nnYxXEMlvOW1-XoWJWY_VKsgZ75tWLO1aCXg,7375
|
142
142
|
monai/data/test_time_augmentation.py,sha256=KgIcPDwF_KelBCX118J5gx13sefGaDgQFUDgGWCZujA,9871
|
@@ -370,7 +370,7 @@ monai/transforms/intensity/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJ
|
|
370
370
|
monai/transforms/intensity/array.py,sha256=SpG3u9LPuQxDk77lEvPC4-tH1tiOtacDDfcyQydIhkI,121592
|
371
371
|
monai/transforms/intensity/dictionary.py,sha256=MEeMKQckn6X-cEk51Z2YTGjt89RohBzFfO_jU3D06wk,85086
|
372
372
|
monai/transforms/io/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJr4ZZJZLq0,573
|
373
|
-
monai/transforms/io/array.py,sha256=
|
373
|
+
monai/transforms/io/array.py,sha256=QxB19z8aBhaSNlSkrubyoM0swHqTcDatWT0GrzmOOwM,27481
|
374
374
|
monai/transforms/io/dictionary.py,sha256=64M9KUsKyzwXopDcarXT7JKIv9rHP8Ae-fYRvI0yBuM,18716
|
375
375
|
monai/transforms/lazy/__init__.py,sha256=s9djSd6kvViPnFvMR11Dgd30Lv4oY6FaPJr4ZZJZLq0,573
|
376
376
|
monai/transforms/lazy/array.py,sha256=2jNLmQ3_sMX7DdbfcT3Extpwe5FgOBbbz2RqlDlyNcw,1211
|
@@ -420,8 +420,8 @@ monai/visualize/img2tensorboard.py,sha256=NnMcyfIFqX-jD7TBO3Rn02zt5uug79d_7pIIaV
|
|
420
420
|
monai/visualize/occlusion_sensitivity.py,sha256=OQHEJLyIhB8zWqQsfKaX-1kvCjWFVYtLfS4dFC0nKFI,18160
|
421
421
|
monai/visualize/utils.py,sha256=B-MhTVs7sQbIqYS3yPnpBwPw2K82rE2PBtGIfpwZtWM,9894
|
422
422
|
monai/visualize/visualizer.py,sha256=qckyaMZCbezYUwE20k5yc-Pb7UozVavMDbrmyQwfYHY,1377
|
423
|
-
monai_weekly-1.5.
|
424
|
-
monai_weekly-1.5.
|
425
|
-
monai_weekly-1.5.
|
426
|
-
monai_weekly-1.5.
|
427
|
-
monai_weekly-1.5.
|
423
|
+
monai_weekly-1.5.dev2501.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
424
|
+
monai_weekly-1.5.dev2501.dist-info/METADATA,sha256=lPpVp6DVewBS_b3g83G0xSKcSOpVE74pHs5ZwjlCmzg,11876
|
425
|
+
monai_weekly-1.5.dev2501.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
426
|
+
monai_weekly-1.5.dev2501.dist-info/top_level.txt,sha256=UaNwRzLGORdus41Ip446s3bBfViLkdkDsXDo34J2P44,6
|
427
|
+
monai_weekly-1.5.dev2501.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|